aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java')
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java325
1 files changed, 169 insertions, 156 deletions
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 89ae48b3a..929eca8aa 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 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.
@@ -20,8 +20,8 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.model.Device;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -39,11 +39,15 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
private static final int IMAGE_PACKET_MAX = 2048;
+ private static final Map<Integer, Map<Set<String>, BiConsumer<Position, ByteBuf>>> PARAMETERS = new HashMap<>();
+
private final boolean connectionless;
private boolean extended;
private final Map<Long, ByteBuf> photos = new HashMap<>();
@@ -55,7 +59,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
public TeltonikaProtocolDecoder(Protocol protocol, boolean connectionless) {
super(protocol);
this.connectionless = connectionless;
- this.extended = Context.getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ this.extended = getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName()));
}
private void parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
@@ -116,7 +124,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
return printable;
}
- private void decodeSerial(Channel channel, SocketAddress remoteAddress, Position position, ByteBuf buf) {
+ private void decodeSerial(
+ Channel channel, SocketAddress remoteAddress, DeviceSession deviceSession, Position position, ByteBuf buf) {
getLastLocation(position, null);
@@ -145,10 +154,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
channel, remoteAddress, photoId,
photo.writerIndex(), Math.min(IMAGE_PACKET_MAX, photo.writableBytes()));
} else {
- String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId();
photos.remove(photoId);
try {
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
} finally {
photo.release();
}
@@ -181,191 +189,193 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private long readValue(ByteBuf buf, int length, boolean signed) {
+ private long readValue(ByteBuf buf, int length) {
switch (length) {
case 1:
- return signed ? buf.readByte() : buf.readUnsignedByte();
+ return buf.readUnsignedByte();
case 2:
- return signed ? buf.readShort() : buf.readUnsignedShort();
+ return buf.readUnsignedShort();
case 4:
- return signed ? buf.readInt() : buf.readUnsignedInt();
+ return buf.readUnsignedInt();
default:
return buf.readLong();
}
}
- private void decodeOtherParameter(Position position, int id, ByteBuf buf, int length) {
- switch (id) {
- case 1:
- case 2:
- case 3:
- case 4:
- position.set("di" + id, readValue(buf, length, false));
- break;
- case 9:
- position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false));
- break;
- case 10:
- position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false));
- break;
- case 16:
- position.set(Position.KEY_ODOMETER, readValue(buf, length, false));
- break;
- case 17:
- position.set("axisX", readValue(buf, length, true));
- break;
- case 18:
- position.set("axisY", readValue(buf, length, true));
- break;
- case 19:
- position.set("axisZ", readValue(buf, length, true));
- break;
- case 21:
- position.set(Position.KEY_RSSI, readValue(buf, length, false));
- break;
- case 25:
- case 26:
- case 27:
- case 28:
- position.set(Position.PREFIX_TEMP + (id - 24 + 4), readValue(buf, length, true) * 0.1);
- break;
- case 66:
- position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001);
- break;
- case 67:
- position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
- break;
- case 72:
- case 73:
- case 74:
- position.set(Position.PREFIX_TEMP + (id - 71), readValue(buf, length, true) * 0.1);
- break;
- case 78:
- long driverUniqueId = readValue(buf, length, false);
- if (driverUniqueId != 0) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId));
- }
- break;
- case 80:
- position.set("workMode", readValue(buf, length, false));
- break;
- case 90:
- position.set(Position.KEY_DOOR, readValue(buf, length, false));
- break;
- case 115:
- position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1);
- break;
- case 179:
- position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
- break;
- case 180:
- position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1);
- break;
- case 181:
- position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
- break;
- case 182:
- position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
- break;
- case 199:
- position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false));
- break;
- case 236:
- if (readValue(buf, length, false) == 1) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
- break;
- case 239:
- position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
- break;
- case 240:
- position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
- break;
- case 241:
- position.set(Position.KEY_OPERATOR, readValue(buf, length, false));
- break;
- case 253:
- switch ((int) readValue(buf, length, false)) {
- case 1:
- position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
- break;
- case 2:
- position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
- break;
- case 3:
- position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
- break;
- default:
- break;
- }
- break;
- default:
- position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
- break;
- }
+ private static void register(int id, Set<String> models, BiConsumer<Position, ByteBuf> handler) {
+ PARAMETERS.computeIfAbsent(id, key -> new HashMap<>()).put(models, handler);
+ }
+
+ static {
+ 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");
+
+ 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));
+ register(3, null, (p, b) -> p.set(Position.PREFIX_IN + 3, b.readUnsignedByte() > 0));
+ register(4, null, (p, b) -> p.set(Position.PREFIX_IN + 4, b.readUnsignedByte() > 0));
+ register(9, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 1, b.readUnsignedShort() * 0.001));
+ register(10, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 2, b.readUnsignedShort() * 0.001));
+ register(11, fmbXXX, (p, b) -> p.set(Position.KEY_ICCID, String.valueOf(b.readLong())));
+ register(12, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_USED, b.readUnsignedInt() * 0.001));
+ register(13, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.01));
+ register(16, null, (p, b) -> p.set(Position.KEY_ODOMETER, b.readUnsignedInt()));
+ register(17, null, (p, b) -> p.set("axisX", b.readShort()));
+ register(18, null, (p, b) -> p.set("axisY", b.readShort()));
+ register(19, null, (p, b) -> p.set("axisZ", b.readShort()));
+ register(21, null, (p, b) -> p.set(Position.KEY_RSSI, b.readUnsignedByte()));
+ register(24, fmbXXX, (p, b) -> p.setSpeed(UnitsConverter.knotsFromKph(b.readUnsignedShort())));
+ register(25, null, (p, b) -> p.set("bleTemp1", b.readShort() * 0.01));
+ register(26, null, (p, b) -> p.set("bleTemp2", b.readShort() * 0.01));
+ register(27, null, (p, b) -> p.set("bleTemp3", b.readShort() * 0.01));
+ register(28, null, (p, b) -> p.set("bleTemp4", b.readShort() * 0.01));
+ register(66, null, (p, b) -> p.set(Position.KEY_POWER, b.readUnsignedShort() * 0.001));
+ register(67, null, (p, b) -> p.set(Position.KEY_BATTERY, b.readUnsignedShort() * 0.001));
+ register(68, fmbXXX, (p, b) -> p.set("batteryCurrent", b.readUnsignedShort() * 0.001));
+ register(72, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 1, b.readInt() * 0.1));
+ register(73, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 2, b.readInt() * 0.1));
+ register(74, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 3, b.readInt() * 0.1));
+ register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1));
+ register(78, null, (p, b) -> {
+ long driverUniqueId = b.readLong();
+ if (driverUniqueId > 0) {
+ p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId));
+ }
+ });
+ register(80, fmbXXX, (p, b) -> p.set("dataMode", b.readUnsignedByte()));
+ register(90, null, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedShort()));
+ register(115, fmbXXX, (p, b) -> p.set(Position.KEY_COOLANT_TEMP, 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));
+ register(182, null, (p, b) -> p.set(Position.KEY_HDOP, b.readUnsignedShort() * 0.1));
+ register(199, null, (p, b) -> p.set(Position.KEY_ODOMETER_TRIP, b.readUnsignedInt()));
+ register(200, fmbXXX, (p, b) -> p.set("sleepMode", b.readUnsignedByte()));
+ register(205, fmbXXX, (p, b) -> p.set("cid", b.readUnsignedShort()));
+ register(206, fmbXXX, (p, b) -> p.set("lac", b.readUnsignedShort()));
+ register(236, null, (p, b) -> {
+ p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null);
+ });
+ register(239, null, (p, b) -> p.set(Position.KEY_IGNITION, b.readUnsignedByte() > 0));
+ register(240, null, (p, b) -> p.set(Position.KEY_MOTION, b.readUnsignedByte() > 0));
+ register(241, null, (p, b) -> p.set(Position.KEY_OPERATOR, b.readUnsignedInt()));
+ register(253, null, (p, b) -> {
+ switch (b.readUnsignedByte()) {
+ case 1:
+ p.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 2:
+ p.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 3:
+ p.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ break;
+ }
+ });
}
private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) {
switch (id) {
case 1:
- position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false));
+ position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length));
break;
case 2:
- position.set("usbConnected", readValue(buf, length, false) == 1);
+ position.set("usbConnected", readValue(buf, length) == 1);
break;
case 5:
- position.set("uptime", readValue(buf, length, false));
+ position.set("uptime", readValue(buf, length));
break;
case 20:
- position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_HDOP, readValue(buf, length) * 0.1);
break;
case 21:
- position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_VDOP, readValue(buf, length) * 0.1);
break;
case 22:
- position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_PDOP, readValue(buf, length) * 0.1);
break;
case 67:
- position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ position.set(Position.KEY_BATTERY, readValue(buf, length) * 0.001);
break;
case 221:
- position.set("button", readValue(buf, length, false));
+ position.set("button", readValue(buf, length));
break;
case 222:
- if (readValue(buf, length, false) == 1) {
+ if (readValue(buf, length) == 1) {
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
}
break;
case 240:
- position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ position.set(Position.KEY_MOTION, readValue(buf, length) == 1);
break;
case 244:
- position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1);
+ position.set(Position.KEY_ROAMING, readValue(buf, length) == 1);
break;
default:
- position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ position.set(Position.PREFIX_IO + id, readValue(buf, length));
break;
}
}
- private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec) {
+ private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec, String model) {
if (codec == CODEC_GH3000) {
decodeGh3000Parameter(position, id, buf, length);
} else {
- decodeOtherParameter(position, id, buf, length);
+ int index = buf.readerIndex();
+ boolean decoded = false;
+ for (var entry : PARAMETERS.getOrDefault(id, new HashMap<>()).entrySet()) {
+ if (entry.getKey() == null || model != null && entry.getKey().contains(model)) {
+ entry.getValue().accept(position, buf);
+ decoded = true;
+ break;
+ }
+ }
+ if (decoded) {
+ buf.readerIndex(index + length);
+ } else {
+ position.set(Position.PREFIX_IO + id, readValue(buf, length));
+ }
+ }
+ }
+
+ private void decodeCell(
+ Position position, Network network, String mncKey, String lacKey, String cidKey, String rssiKey) {
+ if (position.hasAttribute(mncKey) && position.hasAttribute(lacKey) && position.hasAttribute(cidKey)) {
+ CellTower cellTower = CellTower.from(
+ getConfig().getInteger(Keys.GEOLOCATION_MCC),
+ ((Number) position.getAttributes().remove(mncKey)).intValue(),
+ ((Number) position.getAttributes().remove(lacKey)).intValue(),
+ ((Number) position.getAttributes().remove(cidKey)).longValue());
+ cellTower.setSignalStrength(((Number) position.getAttributes().remove(rssiKey)).intValue());
+ network.addCellTower(cellTower);
}
}
- private void decodeNetwork(Position position) {
- long cid = position.getLong(Position.PREFIX_IO + 205);
- int lac = position.getInteger(Position.PREFIX_IO + 206);
- if (cid != 0 && lac != 0) {
- CellTower cellTower = CellTower.fromLacCid(lac, cid);
- long operator = position.getInteger(Position.KEY_OPERATOR);
- if (operator != 0) {
- cellTower.setOperator(operator);
+ private void decodeNetwork(Position position, String model) {
+ if ("TAT100".equals(model)) {
+ Network network = new Network();
+ decodeCell(position, network, "io1200", "io287", "io288", "io289");
+ decodeCell(position, network, "io1201", "io290", "io291", "io292");
+ decodeCell(position, network, "io1202", "io293", "io294", "io295");
+ decodeCell(position, network, "io1203", "io296", "io297", "io298");
+ if (network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+ } else {
+ Integer cid = (Integer) position.getAttributes().remove("cid");
+ Integer lac = (Integer) position.getAttributes().remove("lac");
+ if (cid != null && lac != null) {
+ CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid);
+ long operator = position.getInteger(Position.KEY_OPERATOR);
+ if (operator >= 1000) {
+ cellTower.setOperator(operator);
+ }
+ position.setNetwork(new Network(cellTower));
}
- position.setNetwork(new Network(cellTower));
}
}
@@ -384,7 +394,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeLocation(Position position, ByteBuf buf, int codec) {
+ private void decodeLocation(Position position, ByteBuf buf, int codec, String model) {
int globalMask = 0x0f;
@@ -422,7 +432,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(locationMask, 5)) {
- CellTower cellTower = CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort());
+ CellTower cellTower = CellTower.fromLacCid(
+ getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort());
if (BitUtil.check(locationMask, 6)) {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
@@ -480,7 +491,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 1)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec, model);
}
}
@@ -488,7 +499,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 2)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec, model);
}
}
@@ -496,7 +507,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 3)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec, model);
}
}
@@ -504,7 +515,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (codec == CODEC_8 || codec == CODEC_8_EXT || codec == CODEC_16) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeOtherParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8, codec, model);
}
}
@@ -558,7 +569,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- decodeNetwork(position);
+ decodeNetwork(position, model);
}
@@ -574,10 +585,10 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
int count = buf.readUnsignedByte();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
-
if (deviceSession == null) {
return null;
}
+ String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
for (int i = 0; i < count; i++) {
Position position = new Position(getProtocolName());
@@ -597,9 +608,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
ByteBufUtil.hexDump(buf.readSlice(length)));
}
} else if (codec == CODEC_12) {
- decodeSerial(channel, remoteAddress, position, buf);
+ decodeSerial(channel, remoteAddress, deviceSession, position, buf);
} else {
- decodeLocation(position, buf, codec);
+ decodeLocation(position, buf, codec, model);
}
if (!position.getOutdated() || !position.getAttributes().isEmpty()) {
@@ -636,9 +647,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- if (buf.getUnsignedShort(0) > 0) {
+ if (buf.readableBytes() == 1 && buf.readUnsignedByte() == 0xff) {
+ return null;
+ } else if (buf.getUnsignedShort(0) > 0) {
parseIdentification(channel, remoteAddress, buf);
} else {
buf.skipBytes(4);
@@ -648,7 +661,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
buf.readUnsignedShort(); // length
buf.readUnsignedShort(); // packet id