aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java462
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java14
2 files changed, 242 insertions, 234 deletions
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 03488f6d5..eccb5c007 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -15,15 +15,15 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
-import org.apache.commons.lang3.StringUtils;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.helper.DataConverter;
-import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -31,15 +31,14 @@ import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.Channel;
+import org.traccar.session.DeviceSession;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
@@ -50,8 +49,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private boolean ignoreFixTime;
+ private final DateFormat dateFormat;
+
public Gl200TextProtocolDecoder(Protocol protocol) {
super(protocol);
+ dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@Override
@@ -59,6 +62,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName()));
}
+ private String getDeviceModel(DeviceSession deviceSession, String value) {
+ String model = value.isEmpty() ? getDeviceModel(deviceSession) : value;
+ return model != null ? model.toUpperCase() : "";
+ }
+
private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
if (parser.matches()) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
@@ -82,7 +90,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
private Long parseHours(String hoursString) {
- if (hoursString != null) {
+ if (hoursString != null && !hoursString.isEmpty()) {
String[] hours = hoursString.split(":");
return (Integer.parseInt(hours[0]) * 3600L
+ (hours.length > 1 ? Integer.parseInt(hours[1]) * 60L : 0)
@@ -91,13 +99,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Position decodeAck(
- Channel channel, SocketAddress remoteAddress, String[] values, String type) throws Exception {
+ private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[2]);
if (deviceSession == null) {
return null;
}
- if (type.equals("HBD")) {
+ if (values[0].equals("+ACK:GTHBD")) {
if (channel != null) {
channel.writeAndFlush(new NetworkMessage(
"+SACK:GTHBD," + values[1] + "," + values[values.length - 1] + "$", remoteAddress));
@@ -105,7 +112,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
} else {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, new SimpleDateFormat("yyyyMMddHHmmss").parse(values[values.length - 2]));
+ getLastLocation(position, dateFormat.parse(values[values.length - 2]));
position.setValid(false);
position.set(Position.KEY_RESULT, values[0]);
return position;
@@ -323,6 +330,59 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private int decodeLocation(Position position, String model, String[] values, int index) throws ParseException {
+ double hdop = values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(
+ values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1])));
+ position.setCourse(values[index++].isEmpty() ? 0 : Integer.parseInt(values[index - 1]));
+ position.setAltitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+
+ if (!values[index].isEmpty()) {
+ position.setValid(true);
+ position.setLongitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+ position.setLatitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+ position.setTime(dateFormat.parse(values[index++]));
+ } else {
+ index += 3;
+ getLastLocation(position, null);
+ }
+
+ Network network = new Network();
+
+ if (!values[index].isEmpty()) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++], 16),
+ Long.parseLong(values[index++], 16)));
+ } else {
+ index += 4;
+ }
+
+ if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+
+ if (model.startsWith("GL5")) {
+ index += 1; // csq rssi
+ index += 1; // csq ber
+ }
+
+ if (!values[index++].isEmpty()) {
+ int appendMask = Integer.parseInt(values[index - 1]);
+ if (BitUtil.check(appendMask, 0)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(appendMask, 1)) {
+ index += 1; // trigger type
+ }
+ }
+
+ return index;
+ }
+
private static final Pattern PATTERN_OBD = new PatternBuilder()
.text("+RESP:GTOBD,")
.expression("(?:.{6}|.{10})?,") // protocol version
@@ -392,92 +452,92 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
- Position position = new Position(getProtocolName());
-
+ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
int index = 0;
- String[] values = sentence.split(",");
-
index += 1; // header
index += 1; // protocol version
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- String model = StringUtils.firstNonEmpty(values[index++], getDeviceModel(deviceSession));
+ String model = getDeviceModel(deviceSession, v[index++]);
index += 1; // report type
index += 1; // can bus state
- long reportMask = Long.parseLong(values[index++], 16);
+ long reportMask = Long.parseLong(v[index++], 16);
long reportMaskExt = 0;
if (BitUtil.check(reportMask, 0)) {
- position.set(Position.KEY_VIN, values[index++]);
+ position.set(Position.KEY_VIN, v[index++]);
}
- if (BitUtil.check(reportMask, 1) && !values[index++].isEmpty()) {
- position.set(Position.KEY_IGNITION, Integer.parseInt(values[index - 1]) > 0);
+ if (BitUtil.check(reportMask, 1) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_IGNITION, Integer.parseInt(v[index - 1]) > 0);
}
- if (BitUtil.check(reportMask, 2) && !values[index++].isEmpty()) {
- position.set(Position.KEY_OBD_ODOMETER, Integer.parseInt(values[index - 1].substring(1)));
+ if (BitUtil.check(reportMask, 2) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_ODOMETER, Integer.parseInt(v[index - 1].substring(1)));
}
- if (BitUtil.check(reportMask, 3) && !values[index++].isEmpty()) {
- position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 3) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_USED, Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 5) && !values[index++].isEmpty()) {
- position.set(Position.KEY_RPM, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 5) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_RPM, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 4) && !values[index++].isEmpty()) {
- position.set(Position.KEY_OBD_SPEED, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 4) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_SPEED, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 6) && !values[index++].isEmpty()) {
- position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 6) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 7) && !values[index++].isEmpty()) {
- String value = values[index - 1];
+ if (BitUtil.check(reportMask, 7) && !v[index++].isEmpty()) {
+ String value = v[index - 1];
if (value.startsWith("L/H")) {
position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(value.substring(3)));
}
}
- if (BitUtil.check(reportMask, 8) && !values[index++].isEmpty()) {
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index - 1].substring(1)));
+ if (BitUtil.check(reportMask, 8) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index - 1].substring(1)));
}
- if (BitUtil.check(reportMask, 9) && !values[index++].isEmpty()) {
- position.set("range", Long.parseLong(values[index - 1]) * 100);
+ if (BitUtil.check(reportMask, 9) && !v[index++].isEmpty()) {
+ position.set("range", Long.parseLong(v[index - 1]) * 100);
}
- if (BitUtil.check(reportMask, 10) && !values[index++].isEmpty()) {
- position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 10) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) {
- position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1])));
+ if (BitUtil.check(reportMask, 11) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(v[index - 1])));
}
- if (BitUtil.check(reportMask, 12) && !values[index++].isEmpty()) {
- position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 12) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 13) && !values[index++].isEmpty()) {
- position.set("idleHours", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 13) && !v[index++].isEmpty()) {
+ position.set("idleHours", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) {
- position.set("idleFuelConsumption", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 14) && !v[index++].isEmpty()) {
+ position.set("idleFuelConsumption", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 15) && !values[index++].isEmpty()) {
- position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 15) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) {
- position.set("tachographInfo", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 16) && !v[index++].isEmpty()) {
+ position.set("tachographInfo", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) {
- position.set("indicators", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 17) && !v[index++].isEmpty()) {
+ position.set("indicators", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) {
- position.set("lights", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 18) && !v[index++].isEmpty()) {
+ position.set("lights", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) {
- position.set("doors", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 19) && !v[index++].isEmpty()) {
+ position.set("doors", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) {
- position.set("vehicleOverspeed", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 20) && !v[index++].isEmpty()) {
+ position.set("vehicleOverspeed", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) {
- position.set("engineOverspeed", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 21) && !v[index++].isEmpty()) {
+ position.set("engineOverspeed", Double.parseDouble(v[index - 1]));
}
if ("GV350M".equals(model)) {
if (BitUtil.check(reportMask, 22)) {
@@ -512,20 +572,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
index += 1; // electric report mask
}
}
- if (BitUtil.check(reportMask, 29) && !values[index++].isEmpty()) {
- reportMaskExt = Long.parseLong(values[index - 1], 16);
+ if (BitUtil.check(reportMask, 29) && !v[index++].isEmpty()) {
+ reportMaskExt = Long.parseLong(v[index - 1], 16);
}
- if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) {
- position.set("adBlueLevel", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 0) && !v[index++].isEmpty()) {
+ position.set("adBlueLevel", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 1) && !values[index++].isEmpty()) {
- position.set("axleWeight1", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 1) && !v[index++].isEmpty()) {
+ position.set("axleWeight1", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 2) && !values[index++].isEmpty()) {
- position.set("axleWeight3", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 2) && !v[index++].isEmpty()) {
+ position.set("axleWeight3", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 3) && !values[index++].isEmpty()) {
- position.set("axleWeight4", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 3) && !v[index++].isEmpty()) {
+ position.set("axleWeight4", Integer.parseInt(v[index - 1]));
}
if (BitUtil.check(reportMaskExt, 4)) {
index += 1; // tachograph overspeed
@@ -536,8 +596,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(reportMaskExt, 6)) {
index += 1; // tachograph direction
}
- if (BitUtil.check(reportMaskExt, 7) && !values[index++].isEmpty()) {
- position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[index - 1]) * 0.001);
+ if (BitUtil.check(reportMaskExt, 7) && !v[index++].isEmpty()) {
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(v[index - 1]) * 0.001);
}
if (BitUtil.check(reportMaskExt, 8)) {
index += 1; // pedal breaking factor
@@ -560,20 +620,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(reportMaskExt, 14)) {
index += 1; // total brake application
}
- if (BitUtil.check(reportMaskExt, 15) && !values[index++].isEmpty()) {
- position.set("driver1Card", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 15) && !v[index++].isEmpty()) {
+ position.set("driver1Card", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 16) && !values[index++].isEmpty()) {
- position.set("driver2Card", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 16) && !v[index++].isEmpty()) {
+ position.set("driver2Card", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 17) && !values[index++].isEmpty()) {
- position.set("driver1Name", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 17) && !v[index++].isEmpty()) {
+ position.set("driver1Name", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 18) && !values[index++].isEmpty()) {
- position.set("driver2Name", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 18) && !v[index++].isEmpty()) {
+ position.set("driver2Name", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 19) && !values[index++].isEmpty()) {
- position.set("registration", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 19) && !v[index++].isEmpty()) {
+ position.set("registration", v[index - 1]);
}
if (BitUtil.check(reportMaskExt, 20)) {
index += 1; // expansion information
@@ -592,17 +652,17 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
if (!"GV355CEU".equals(model) && BitUtil.check(reportMask, 30)) {
- while (values[index].isEmpty()) {
+ while (v[index].isEmpty()) {
index += 1;
}
- position.setValid(Integer.parseInt(values[index++]) > 0);
- if (!values[index].isEmpty()) {
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- position.setCourse(Integer.parseInt(values[index++]));
- position.setAltitude(Double.parseDouble(values[index++]));
- position.setLongitude(Double.parseDouble(values[index++]));
- position.setLatitude(Double.parseDouble(values[index++]));
- position.setTime(dateFormat.parse(values[index++]));
+ position.setValid(Integer.parseInt(v[index++]) > 0);
+ if (!v[index].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(v[index++])));
+ position.setCourse(Integer.parseInt(v[index++]));
+ position.setAltitude(Double.parseDouble(v[index++]));
+ position.setLongitude(Double.parseDouble(v[index++]));
+ position.setLatitude(Double.parseDouble(v[index++]));
+ position.setTime(dateFormat.parse(v[index++]));
} else {
index += 6; // no location
getLastLocation(position, null);
@@ -616,33 +676,31 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
index += 1; // reserved
}
- index = values.length - 2;
+ index = v.length - 2;
if (ignoreFixTime) {
- position.setTime(dateFormat.parse(values[index]));
+ position.setTime(dateFormat.parse(v[index]));
} else {
- position.setDeviceTime(dateFormat.parse(values[index]));
+ position.setDeviceTime(dateFormat.parse(v[index]));
}
return position;
}
- private void decodeStatus(Position position, Parser parser) {
- if (parser.hasNext(3)) {
- int ignition = parser.nextHexInt();
- if (BitUtil.check(ignition, 4)) {
- position.set(Position.KEY_IGNITION, false);
- } else if (BitUtil.check(ignition, 5)) {
- position.set(Position.KEY_IGNITION, true);
- }
- int input = parser.nextHexInt();
- int output = parser.nextHexInt();
- position.set(Position.KEY_INPUT, input);
- position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1));
- position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2));
- position.set(Position.KEY_OUTPUT, output);
- position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0));
- position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1));
- }
+ private void decodeStatus(Position position, long value) {
+ long ignition = BitUtil.between(value, 2 * 8, 3 * 8);
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+ long input = BitUtil.between(value, 8, 2 * 8);
+ long output = BitUtil.to(value, 8);
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2));
+ position.set(Position.KEY_OUTPUT, output);
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1));
}
private static final Pattern PATTERN_FRI = new PatternBuilder()
@@ -672,7 +730,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)?,") // adc 1
.number("(x+)?,") // adc 2
.number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
+ .number("(x{6})?,") // device status
.number("(d+)?,") // rpm
.number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
.number("(d+)?,") // fuel level
@@ -746,7 +804,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 2, parser.next());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- decodeStatus(position, parser);
+ if (parser.hasNext()) {
+ decodeStatus(position, parser.nextHexLong());
+ }
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
@@ -773,142 +833,91 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return positions;
}
- private static final Pattern PATTERN_ERI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTERI,")
- .expression("(?:.{6}|.{10})?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("(x{8}),") // mask
- .number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
- .expression("((?:")
- .expression(PATTERN_LOCATION.pattern())
- .expression(")+)")
- .groupBegin()
- .number("(d{1,7}.d)?,") // odometer
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(x+)?,") // adc 1
- .number("(x+)?,").optional() // adc 2
- .groupBegin()
- .number("(x+)?,") // adc 3
- .number("(xx),") // inputs
- .number("(xx),") // outputs
- .or()
- .number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
- .groupEnd()
- .expression("(.*)") // additional data
- .or()
- .number("d*,,")
- .number("(d+),") // battery
- .any()
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_ERI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
+ int index = 0;
+ index += 1; // header
+ index += 1; // protocol version
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
if (deviceSession == null) {
return null;
}
- long mask = parser.nextHexLong();
+ String model = getDeviceModel(deviceSession, v[index++]);
+ long mask = Long.parseLong(v[index++], 16);
+ Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001;
+ index += 1; // report type
+ int count = Integer.parseInt(v[index++]);
LinkedList<Position> positions = new LinkedList<>();
-
- Integer power = parser.nextInt();
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
+ for (int i = 0; i < count; i++) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
-
- decodeLocation(position, itemParser);
-
+ index = decodeLocation(position, model, v, index);
positions.add(position);
}
Position position = positions.getLast();
+ position.set(Position.KEY_POWER, power);
- skipLocation(parser);
-
- if (power != null) {
- position.set(Position.KEY_POWER, power * 0.001);
+ if (!model.startsWith("GL5")) {
+ position.set(Position.KEY_ODOMETER, v[index++].isEmpty() ? null : Double.parseDouble(v[index - 1]) * 1000);
+ position.set(Position.KEY_HOURS, parseHours(v[index++]));
+ position.set(Position.PREFIX_ADC + 1, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001);
+ }
+ if (model.startsWith("GV") && !model.startsWith("GV6")) {
+ position.set(Position.PREFIX_ADC + 2, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001);
}
- if (parser.hasNextAny(12)) {
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
- position.set(Position.KEY_HOURS, parseHours(parser.next()));
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
- position.set(Position.PREFIX_ADC + 3, parser.next());
- if (parser.hasNext(2)) {
- position.set(Position.KEY_INPUT, parser.nextHexInt());
- position.set(Position.KEY_OUTPUT, parser.nextHexInt());
- }
- if (parser.hasNext(4)) {
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- decodeStatus(position, parser);
+ position.set(Position.KEY_BATTERY_LEVEL, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]));
+ if (model.startsWith("GL5")) {
+ index += 1; // mode selection
+ position.set(Position.KEY_MOTION, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) > 0);
+ } else {
+ if (!v[index++].isEmpty()) {
+ decodeStatus(position, Long.parseLong(v[index - 1]));
}
+ index += 1; // reserved
+ }
- int index = 0;
- String[] data = parser.next().split(",");
-
- index += 1; // device type
-
- if (BitUtil.check(mask, 0)) {
- position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(data[index++], 16));
- }
+ if (BitUtil.check(mask, 0)) {
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(v[index++], 16));
+ }
- if (BitUtil.check(mask, 1)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // id
- index += 1; // type
- if (!data[index++].isEmpty()) {
- position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
- }
+ if (BitUtil.check(mask, 1)) {
+ int deviceCount = Integer.parseInt(v[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // id
+ index += 1; // type
+ if (!v[index++].isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(v[index - 1], 16) * 0.0625);
}
}
+ }
- if (BitUtil.check(mask, 2)) {
- index += 1; // can data
- }
+ if (BitUtil.check(mask, 2)) {
+ index += 1; // can data
+ }
- if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // type
- if (BitUtil.check(mask, 3)) {
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
- }
- if (BitUtil.check(mask, 4)) {
- index += 1; // volume
- }
+ if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
+ int deviceCount = Integer.parseInt(v[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // type
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index++]));
+ }
+ if (BitUtil.check(mask, 4)) {
+ index += 1; // volume
}
}
-
}
- if (parser.hasNext()) {
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- }
-
- decodeDeviceTime(position, parser);
+ Date time = dateFormat.parse(v[v.length - 2]);
if (ignoreFixTime) {
+ position.setTime(time);
positions.clear();
positions.add(position);
+ } else {
+ position.setDeviceTime(time);
}
return positions;
@@ -1187,8 +1196,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
String data = Unpooled.wrappedBuffer(DataConverter.parseHex(parser.next()))
.toString(StandardCharsets.US_ASCII);
if (data.contains("COMB")) {
- String[] values = data.split(",");
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[2]));
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data.split(",")[2]));
} else {
position.set(Position.KEY_RESULT, data);
}
@@ -1537,7 +1545,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
Object result;
String type = sentence.substring(typeIndex + 3, typeIndex + 6);
if (sentence.startsWith("+ACK")) {
- result = decodeAck(channel, remoteAddress, values, type);
+ result = decodeAck(channel, remoteAddress, values);
} else {
switch (type) {
case "INF":
@@ -1547,7 +1555,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
result = decodeObd(channel, remoteAddress, sentence);
break;
case "CAN":
- result = decodeCan(channel, remoteAddress, sentence);
+ result = decodeCan(channel, remoteAddress, values);
break;
case "CTN":
case "FRI":
@@ -1558,7 +1566,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
result = decodeFri(channel, remoteAddress, sentence);
break;
case "ERI":
- result = decodeEri(channel, remoteAddress, sentence);
+ result = decodeEri(channel, remoteAddress, values);
break;
case "IGN":
case "IGF":
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index a6e1c212f..2968161c9 100644
--- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -46,14 +46,14 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTFRI,5E0100,862061048023666,,,12940,10,1,1,0.0,97,179.8,-90.366478,38.735379,20230616183231,0310,0410,6709,03ADF710,00,6223.7,,,,,110000,,,,202306161834$"));
verifyAttribute(decoder, buffer(
- "+BUFF:GTERI,410502,864802030794634,,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"),
+ "+BUFF:GTERI,410502,864802030794634,GV75W,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"),
Position.KEY_FUEL_LEVEL, 153);
verifyPositions(decoder, false, buffer(
"+BUFF:GTFRI,2E0503,861106050005423,,,0,1,,,,,,,,,,,,0,0,,98,1,0,,,20200101000001,0083$"));
verifyAttribute(decoder, buffer(
- "+RESP:GTERI,271002,863457051562823,,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"),
+ "+RESP:GTERI,271002,863457051562823,GV300,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"),
Position.PREFIX_TEMP + 1, 25.0);
verifyAttribute(decoder, buffer(
@@ -170,7 +170,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
+ "+RESP:GTERI,250C02,868789023691057,GV300,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
verifyAttributes(decoder, buffer(
"+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$"));
@@ -197,7 +197,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
+ "+RESP:GTERI,060502,861074023620928,GV300,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
verifyAttributes(decoder, buffer(
"+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$"));
@@ -212,7 +212,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
+ "+RESP:GTERI,060800,861074023677175,GV300,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
verifyPosition(decoder, buffer(
"+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$"));
@@ -233,7 +233,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
+ "+RESP:GTERI,060502,861074023376992,GV300,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
verifyPositions(decoder, buffer(
"+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$"));
@@ -245,7 +245,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
+ "+RESP:GTERI,060502,861074023692562,GV300,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
verifyPositions(decoder, buffer(
"+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$"));