aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2024-06-14 21:08:21 -0600
committerIván Ávalos <avalos@disroot.org>2024-06-14 21:08:21 -0600
commit471dc4ca7b6cfd656cc2c04c526fe56ee538991c (patch)
tree4766fa7209e2eaab65269db456cf0436e6a64a49 /src/main/java/org/traccar/protocol
parent447c7e15fcec8fc72d0457bb7dbf166cbea84acd (diff)
parent64528b96da4a742070d5845a876b07ca66ad0be3 (diff)
downloadtrackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.tar.gz
trackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.tar.bz2
trackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.zip
Merge tag 'v6.2'
Diffstat (limited to 'src/main/java/org/traccar/protocol')
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SnapperFrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/SnapperProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java229
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java158
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocolDecoder.java93
12 files changed, 524 insertions, 109 deletions
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
index b10ff4c64..ff192807b 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -92,7 +92,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
.number("C(d+);") // coolant temperature
.number("L(d+.d);") // fuel level
.number("[XY][MH]d+.d+;")
- .number("M(d+);") // mileage
+ .number("Md+.?d*;") // mileage
.number("F(d+.d+);") // fuel consumption
.number("T(d+);") // engine time
.any()
@@ -264,7 +264,6 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ENGINE_LOAD, parser.nextDouble());
position.set(Position.KEY_COOLANT_TEMP, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble());
position.set(Position.KEY_HOURS, parser.nextInt());
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 775e98401..8edce9346 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -974,6 +974,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
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 (model.startsWith("GV355CEU")) {
+ index += 1; // reserved
+ }
position.set(Position.KEY_BATTERY_LEVEL, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]));
if (model.startsWith("GL5")) {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index b75e612b8..654071d22 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.config.Keys;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -65,12 +64,6 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
private final XPath xPath;
private final XPathExpression messageExpression;
- private boolean alternative;
-
- public void setAlternative(boolean alternative) {
- this.alternative = alternative;
- }
-
public GlobalstarProtocolDecoder(Protocol protocol) {
super(protocol);
try {
@@ -89,11 +82,6 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
- @Override
- protected void init() {
- this.alternative = getConfig().getBoolean(Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()));
- }
-
private void sendResponse(Channel channel, String messageId) throws TransformerException {
Document document = documentBuilder.newDocument();
@@ -144,6 +132,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, xPath.evaluate("esn", node));
if (deviceSession != null) {
+ boolean atlas = "AtlasTrax".equalsIgnoreCase(getDeviceModel(deviceSession));
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -154,7 +143,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
int flags = buf.readUnsignedByte();
int type;
- if (alternative) {
+ if (atlas) {
type = BitUtil.to(flags, 1);
position.setValid(true);
position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1));
@@ -179,7 +168,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
position.setLongitude(longitude > 180 ? longitude - 360 : longitude);
int speed = 0;
- if (alternative) {
+ if (atlas) {
speed = buf.readUnsignedByte();
position.setSpeed(UnitsConverter.knotsFromKph(speed));
position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7));
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 6c0380278..a1a9c3773 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -410,7 +410,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
}
- private String decodeAlarm(short value) {
+ private String decodeAlarm(short value, String model) {
+ boolean modelLW = model != null && model.toUpperCase().startsWith("LW");
switch (value) {
case 0x01:
return Position.ALARM_SOS;
@@ -427,7 +428,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_OVERSPEED;
case 0x0E:
case 0x0F:
- case 0x19:
return Position.ALARM_LOW_BATTERY;
case 0x11:
return Position.ALARM_POWER_OFF;
@@ -438,17 +438,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case 0x14:
return Position.ALARM_DOOR;
case 0x18:
- return Position.ALARM_REMOVING;
- case 0x23:
- return Position.ALARM_FALL_DOWN;
+ return modelLW ? Position.ALARM_ACCIDENT : Position.ALARM_REMOVING;
+ case 0x19:
+ return modelLW ? Position.ALARM_ACCELERATION : Position.ALARM_LOW_BATTERY;
+ case 0x1A:
case 0x28:
return Position.ALARM_BRAKING;
- case 0x29:
- return Position.ALARM_ACCELERATION;
+ case 0x1B:
case 0x2A:
case 0x2B:
case 0x2E:
return Position.ALARM_CORNERING;
+ case 0x23:
+ return Position.ALARM_FALL_DOWN;
+ case 0x29:
+ return Position.ALARM_ACCELERATION;
case 0x2C:
return Position.ALARM_ACCIDENT;
case 0x30:
@@ -831,7 +835,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
int satellites = BitUtil.between(signal, 10, 15) + BitUtil.between(signal, 5, 10);
position.set(Position.KEY_SATELLITES, satellites);
position.set(Position.KEY_RSSI, BitUtil.to(signal, 5));
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ position.set(Position.KEY_ALARM, decodeAlarm(
+ buf.readUnsignedByte(), getDeviceModel(deviceSession)));
buf.readUnsignedByte(); // language
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
buf.readUnsignedByte(); // working mode
@@ -842,7 +847,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
short alarmExtension = buf.readUnsignedByte();
if (variant != Variant.VXT01) {
- position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension));
+ position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension, getDeviceModel(deviceSession)));
}
}
}
@@ -881,7 +886,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
decodeStatus(position, buf);
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ position.set(Position.KEY_ALARM, decodeAlarm(
+ buf.readUnsignedByte(), getDeviceModel(deviceSession)));
position.set("oil", buf.readUnsignedShort());
int temperature = buf.readUnsignedByte();
if (BitUtil.check(temperature, 7)) {
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 648c5fb42..d010a8fe0 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -492,6 +492,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
}
break;
+ case 0x51:
+ if (length == 16) {
+ for (int i = 1; i <= 8; i++) {
+ position.set(Position.PREFIX_TEMP + i, buf.readShort());
+ }
+ }
+ break;
case 0x56:
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 10);
buf.readUnsignedByte(); // reserved
@@ -668,6 +675,10 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set("fuel2", Double.parseDouble(
buf.readCharSequence(6, StandardCharsets.US_ASCII).toString()));
break;
+ case 0x00B2:
+ position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(
+ buf.readSlice(10)).replaceAll("f", ""));
+ break;
case 0x00CE:
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
break;
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index e88b34478..2b234ab21 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -171,7 +171,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
}
long status = buf.readUnsignedInt();
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 7 + 3 * 8));
+ position.set(Position.KEY_IGNITION, !BitUtil.check(status, 7 + 3 * 8));
position.set(Position.KEY_STATUS, status);
buf.readUnsignedShort();
diff --git a/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java b/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java
new file mode 100644
index 000000000..be45346a6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class SnapperFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ byte header = buf.getByte(buf.readerIndex());
+ if (header == 'P') {
+ if (buf.readableBytes() >= 2) {
+ return buf.readRetainedSlice(2);
+ }
+ } else if (buf.readableBytes() >= 16) {
+ int length = buf.getIntLE(buf.readerIndex() + 12) + 12 + 4 + 9;
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SnapperProtocol.java b/src/main/java/org/traccar/protocol/SnapperProtocol.java
new file mode 100644
index 000000000..25a11ed3a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SnapperProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class SnapperProtocol extends BaseProtocol {
+
+ @Inject
+ public SnapperProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new SnapperFrameDecoder());
+ pipeline.addLast(new SnapperProtocolDecoder(SnapperProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java b/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java
new file mode 100644
index 000000000..ef1a4426a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class SnapperProtocolDecoder extends BaseProtocolDecoder {
+
+ public SnapperProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HELLO = 0x01;
+ public static final int MSG_SENDING_START = 0x02;
+ public static final int MSG_SENDING_FINISH = 0x03;
+ public static final int MSG_SEND_EVENTS = 0x21;
+ public static final int MSG_SEND_TECH_INFO = 0x23;
+ public static final int MSG_UPDATE_CMS_NUM = 0x24;
+ public static final int MSG_SEND_SYSTEM_INFO = 0x26;
+ public static final int MSG_SEND_USER_PHONE_NUMBERS = 0x31;
+ public static final int MSG_SEND_GPS_DATA = 0x32;
+ public static final int MSG_SEND_LBS_DATA = 0x33;
+ public static final int MSG_SEND_SYSTEM_STATUS = 0x34;
+ public static final int MSG_SEND_TRANSIT_SETTINGS = 0x35;
+ public static final int MSG_GET_SETTINGS = 0x36;
+ public static final int MSG_SEND_CONCATENATED_PACKET = 0x37;
+ public static final int MSG_SEND_DEBUG_INFO = 0x38;
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int index, int type, String answer) {
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte('K');
+ response.writeByte(3); // protocol version
+ response.writeLongLE(0); // reserved
+ response.writeShortLE(0); // encryption
+ response.writeIntLE(answer.length());
+ response.writeIntLE(0); // reserved
+ response.writeShortLE(index);
+ response.writeByte(Checksum.sum(ByteBuffer.wrap(answer.getBytes(StandardCharsets.US_ASCII))));
+ response.writeShortLE(type);
+ response.writeCharSequence(answer, StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private void decodeEvents(Position position, ByteBuf buf) {
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // info 1
+ buf.readUnsignedByte(); // info 2
+ buf.readUnsignedIntLE(); // timestamp
+ buf.readUnsignedByte(); // timezone
+ }
+
+ private void decodeTechInfo(Position position, ByteBuf buf) {
+
+ for (int i = 0; i < 5; i++) {
+ buf.readUnsignedByte(); // index
+ int type = buf.readUnsignedByte();
+ switch (type) {
+ case 0x00:
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1);
+ position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
+ case 0x01:
+ position.set("interiorTemp", buf.readByte());
+ position.set("engineTemp", buf.readByte());
+ buf.readUnsignedByte(); // reserved
+ break;
+ default:
+ buf.skipBytes(3);
+ break;
+ }
+ }
+ }
+
+ private void decodeGpsData(Position position, ByteBuf buf) throws ParseException {
+
+ String content = buf.readCharSequence(buf.readableBytes(), StandardCharsets.US_ASCII).toString();
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ int flags = Integer.parseInt(json.getString("f"), 16);
+ if (!BitUtil.check(flags, 3)) {
+ return;
+ }
+
+ position.setValid(BitUtil.check(flags, 1));
+ if (!BitUtil.check(flags, 6)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ if (!BitUtil.check(flags, 7)) {
+ position.setLongitude(-position.getLongitude());
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("ddMMyyHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(json.getString("d") + json.getString("t").split("\\.")[0]));
+
+ String lat = json.getString("la");
+ position.setLatitude(Integer.parseInt(lat.substring(0, 2)) + Double.parseDouble(lat.substring(2)) / 60);
+ String lon = json.getString("lo");
+ position.setLongitude(Integer.parseInt(lon.substring(0, 3)) + Double.parseDouble(lon.substring(3)) / 60);
+
+ position.setAltitude(Double.parseDouble(json.getString("a")));
+ position.setSpeed(Double.parseDouble(json.getString("s")));
+ position.setCourse(Double.parseDouble(json.getString("c")));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(json.getString("sv")));
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ byte header = buf.readByte();
+ if (header == 'P') {
+ if (channel != null) {
+ ByteBuf response = Unpooled.wrappedBuffer(new byte[] {0x50, 0x4f});
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ return null;
+ }
+
+ buf.readUnsignedByte(); // protocol version
+ buf.readUnsignedIntLE(); // system bonus identifier
+
+ String serialNumber = String.valueOf(buf.readUnsignedIntLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serialNumber);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedShortLE(); // encryption
+ int length = buf.readIntLE();
+ buf.readUnsignedByte(); // flags
+ buf.readUnsignedMediumLE(); // reserved
+ int index = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // checksum
+ int type = buf.readUnsignedShortLE();
+
+ if (type == MSG_HELLO) {
+ sendResponse(channel, remoteAddress, index, type, "hello");
+ } else {
+ sendResponse(channel, remoteAddress, index, type, "OK");
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (type) {
+ case MSG_SEND_EVENTS:
+ decodeEvents(position, buf);
+ getLastLocation(position, null); // TODO read timestamp
+ return position;
+ case MSG_SEND_TECH_INFO:
+ decodeTechInfo(position, buf);
+ getLastLocation(position, null);
+ return position;
+ case MSG_SEND_GPS_DATA:
+ decodeGpsData(position, buf.readSlice(length));
+ return position;
+ case MSG_SEND_CONCATENATED_PACKET:
+ int count = buf.readUnsignedShortLE();
+ for (int i = 0; i < count; i++) {
+ int partType = buf.readUnsignedShortLE();
+ int partLength = buf.readUnsignedShortLE();
+ switch (partType) {
+ case MSG_SEND_EVENTS:
+ decodeEvents(position, buf);
+ break;
+ case MSG_SEND_TECH_INFO:
+ decodeTechInfo(position, buf);
+ break;
+ case MSG_SEND_GPS_DATA:
+ decodeGpsData(position, buf.readSlice(partLength));
+ break;
+ default:
+ buf.skipBytes(partLength);
+ break;
+ }
+ }
+ if (position.getFixTime() == null) {
+ getLastLocation(position, null);
+ }
+ return position;
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 53c4a5d02..c9d6f16ef 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -295,6 +295,60 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private int decodeSerialData(Position position, String[] values, int index) {
+
+ int remaining = Integer.parseInt(values[index++]);
+ double totalFuel = 0;
+ while (remaining > 0) {
+ String attribute = values[index++];
+ if (attribute.startsWith("CabAVL")) {
+ String[] data = attribute.split(",");
+ double fuel1 = Double.parseDouble(data[2]);
+ if (fuel1 > 0) {
+ totalFuel += fuel1;
+ position.set("fuel1", fuel1);
+ }
+ double fuel2 = Double.parseDouble(data[3]);
+ if (fuel2 > 0) {
+ totalFuel += fuel2;
+ position.set("fuel2", fuel2);
+ }
+ } else if (attribute.startsWith("GTSL")) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]);
+ } else if (attribute.contains("=")) {
+ String[] pair = attribute.split("=");
+ if (pair.length >= 2) {
+ String value = pair[1].trim();
+ if (value.contains(".")) {
+ value = value.substring(0, value.indexOf('.'));
+ }
+ switch (pair[0].charAt(0)) {
+ case 't':
+ position.set(Position.PREFIX_TEMP + pair[0].charAt(2), Integer.parseInt(value, 16));
+ break;
+ case 'N':
+ int fuel = Integer.parseInt(value, 16);
+ totalFuel += fuel;
+ position.set("fuel" + pair[0].charAt(2), fuel);
+ break;
+ case 'Q':
+ position.set("drivingQuality", Integer.parseInt(value, 16));
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ position.set("serial", attribute.trim());
+ }
+ remaining -= attribute.length() + 1;
+ }
+ if (totalFuel > 0) {
+ position.set(Position.KEY_FUEL_LEVEL, totalFuel);
+ }
+ return index + 1; // checksum
+ }
+
private Position decode2356(
Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException {
int index = 0;
@@ -371,56 +425,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
break;
case "UEX":
- int remaining = Integer.parseInt(values[index++]);
- double totalFuel = 0;
- while (remaining > 0) {
- String attribute = values[index++];
- if (attribute.startsWith("CabAVL")) {
- String[] data = attribute.split(",");
- double fuel1 = Double.parseDouble(data[2]);
- if (fuel1 > 0) {
- totalFuel += fuel1;
- position.set("fuel1", fuel1);
- }
- double fuel2 = Double.parseDouble(data[3]);
- if (fuel2 > 0) {
- totalFuel += fuel2;
- position.set("fuel2", fuel2);
- }
- } else if (attribute.startsWith("GTSL")) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]);
- } else if (attribute.contains("=")) {
- String[] pair = attribute.split("=");
- if (pair.length >= 2) {
- String value = pair[1].trim();
- if (value.contains(".")) {
- value = value.substring(0, value.indexOf('.'));
- }
- switch (pair[0].charAt(0)) {
- case 't':
- position.set(Position.PREFIX_TEMP + pair[0].charAt(2), Integer.parseInt(value, 16));
- break;
- case 'N':
- int fuel = Integer.parseInt(value, 16);
- totalFuel += fuel;
- position.set("fuel" + pair[0].charAt(2), fuel);
- break;
- case 'Q':
- position.set("drivingQuality", Integer.parseInt(value, 16));
- break;
- default:
- break;
- }
- }
- } else {
- position.set("serial", attribute.trim());
- }
- remaining -= attribute.length() + 1;
- }
- if (totalFuel > 0) {
- position.set(Position.KEY_FUEL_LEVEL, totalFuel);
- }
- index += 1; // checksum
+ index = decodeSerialData(position, values, index);
break;
default:
break;
@@ -482,7 +487,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
String type = values[index++];
- if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES")) {
+ if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES")
+ && !type.equals("UEX")) {
return null;
}
@@ -601,34 +607,40 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++]));
}
- if (type.equals("ALT")) {
- if (BitUtil.check(mask, 19)) {
- int alertId = Integer.parseInt(values[index++]);
- position.set(Position.KEY_ALARM, decodeAlert(alertId));
- }
- if (BitUtil.check(mask, 20)) {
- position.set("alertModifier", values[index++]);
- }
- if (BitUtil.check(mask, 21)) {
- position.set("alertData", values[index++]);
- }
- } else {
- if (BitUtil.check(mask, 19)) {
- position.set("mode", Integer.parseInt(values[index++]));
- }
- if (BitUtil.check(mask, 20)) {
- position.set("reason", Integer.parseInt(values[index++]));
- }
- if (BitUtil.check(mask, 21)) {
- position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
- }
+ switch (type) {
+ case "ALT":
+ if (BitUtil.check(mask, 19)) {
+ int alertId = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_ALARM, decodeAlert(alertId));
+ }
+ if (BitUtil.check(mask, 20)) {
+ position.set("alertModifier", values[index++]);
+ }
+ if (BitUtil.check(mask, 21)) {
+ position.set("alertData", values[index++]);
+ }
+ break;
+ case "UEX":
+ index = decodeSerialData(position, values, index);
+ break;
+ default:
+ if (BitUtil.check(mask, 19)) {
+ position.set("mode", Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(mask, 20)) {
+ position.set("reason", Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(mask, 21)) {
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ }
+ break;
}
if (BitUtil.check(mask, 22)) {
index += 1; // reserved
}
- if (BitUtil.check(mask, 23)) {
+ if (BitUtil.check(mask, 23) && !type.equals("UEX")) {
int assignMask = Integer.parseInt(values[index++], 16);
for (int i = 0; i <= 30; i++) {
if (BitUtil.check(assignMask, i)) {
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 6197c6c13..de42031d7 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -199,7 +199,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
var fmbXXX = Set.of(
"FMB001", "FMB010", "FMB002", "FMB020", "FMB003", "FMB110", "FMB120", "FMB122", "FMB125", "FMB130",
"FMB140", "FMU125", "FMB900", "FMB920", "FMB962", "FMB964", "FM3001", "FMB202", "FMB204", "FMB206",
- "FMT100", "MTB100", "FMP100", "MSP500");
+ "FMT100", "MTB100", "FMP100", "MSP500", "FMC125", "FMM125", "FMU130", "FMC130", "FMM130", "FMB150",
+ "FMC150", "FMM150");
register(1, null, (p, b) -> p.set(Position.PREFIX_IN + 1, b.readUnsignedByte() > 0));
register(2, null, (p, b) -> p.set(Position.PREFIX_IN + 2, b.readUnsignedByte() > 0));
@@ -231,7 +232,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1));
register(78, null, (p, b) -> {
long driverUniqueId = b.readLongLE();
- if (driverUniqueId > 0) {
+ if (driverUniqueId != 0) {
p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId));
}
});
@@ -243,9 +244,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
register(85, fmbXXX, (p, b) -> p.set(Position.KEY_RPM, b.readUnsignedShort()));
register(87, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_ODOMETER, b.readUnsignedInt()));
register(89, fmbXXX, (p, b) -> p.set("fuelLevelPercentage", b.readUnsignedByte()));
- register(90, null, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedShort()));
register(110, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.1));
register(113, fmbXXX, (p, b) -> p.set(Position.KEY_BATTERY_LEVEL, b.readUnsignedByte()));
+ register(115, fmbXXX, (p, b) -> p.set("engineTemp", b.readShort() * 0.1));
register(179, null, (p, b) -> p.set(Position.PREFIX_OUT + 1, b.readUnsignedByte() > 0));
register(180, null, (p, b) -> p.set(Position.PREFIX_OUT + 2, b.readUnsignedByte() > 0));
register(181, null, (p, b) -> p.set(Position.KEY_PDOP, b.readUnsignedShort() * 0.1));
@@ -292,6 +293,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
});
register(636, fmbXXX, (p, b) -> p.set("cid4g", b.readUnsignedInt()));
+ register(662, fmbXXX, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedByte() > 0));
}
private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) {
diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
index 02744f8ab..1187250f8 100644
--- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -64,6 +64,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // mnc
.number("(d+),") // lac
.number("(d+)") // cell
+ .number(",(dd)").optional() // alarm
.groupBegin()
.text(",")
.expression("(")
@@ -77,7 +78,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
- private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder()
+ private static final Pattern PATTERN_CP01 = new PatternBuilder()
.expression("[A-Z]{2,3}")
.text("CP01,")
.number("(ddd)") // gsm
@@ -99,7 +100,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
- private static final Pattern PATTERN_LBS = new PatternBuilder()
+ private static final Pattern PATTERN_AP02 = new PatternBuilder()
.expression("[A-Z]{2,3}")
.text("AP02,")
.expression("[^,]+,") // language
@@ -118,6 +119,19 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.expression("(.*)") // wifi
.compile();
+ private static final Pattern PATTERN_AP03 = new PatternBuilder()
+ .expression("[A-Z]{2,3}")
+ .text("AP03,")
+ .number("(ddd)") // rssi
+ .number("(ddd)") // satellites
+ .number("(ddd)") // battery level
+ .number("d") // space
+ .number("xx") // fortification state
+ .number("dd,") // working mode
+ .number("(d+),") // steps
+ .number("d+") // rolls frequency
+ .compile();
+
private Boolean decodeOptionalValue(Parser parser, int activeValue) {
int value = parser.nextInt();
if (value != 0) {
@@ -183,7 +197,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
if (type.equals("CP01")) {
- Parser parser = new Parser(PATTERN_HEARTBEAT, sentence);
+ Parser parser = new Parser(PATTERN_CP01, sentence);
if (!parser.matches()) {
return null;
}
@@ -234,6 +248,20 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()));
if (parser.hasNext()) {
+ switch (parser.nextInt()) {
+ case 1:
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ break;
+ case 5:
+ case 6:
+ position.set(Position.KEY_ALARM, Position.ALARM_FALL_DOWN);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (parser.hasNext()) {
decodeWifi(network, parser.next());
}
@@ -243,7 +271,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
} else if (type.equals("AP02")) {
- Parser parser = new Parser(PATTERN_LBS, sentence);
+ Parser parser = new Parser(PATTERN_AP02, sentence);
if (!parser.matches()) {
return null;
}
@@ -275,6 +303,61 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type.equals("AP03")) {
+
+ Parser parser = new Parser(PATTERN_AP03, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_STEPS, parser.nextInt());
+
+ return position;
+
+ } else if (type.equals("AP49") || type.equals("APHT") || type.equals("APHP") || type.equals("AP50")) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ String[] values = sentence.split(",");
+
+ switch (type) {
+ case "AP49":
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1]));
+ break;
+ case "APHT":
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1]));
+ position.set("pressureSystolic", Integer.parseInt(values[2]));
+ position.set("pressureDiastolic", Integer.parseInt(values[3]));
+ break;
+ case "APHP":
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1]));
+ position.set("pressureSystolic", Integer.parseInt(values[2]));
+ position.set("pressureDiastolic", Integer.parseInt(values[3]));
+ position.set("spo2", Integer.parseInt(values[4]));
+ position.set("bloodSugar", Double.parseDouble(values[5]));
+ position.set("temperature", Double.parseDouble(values[6]));
+ break;
+ case "AP50":
+ position.set("temperature", Double.parseDouble(values[1]));
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[2]));
+ break;
+ default:
+ break;
+ }
+
+ return position;
+
}
return null;