aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/protocol')
-rw-r--r--src/main/java/org/traccar/protocol/BstplProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/BstplProtocolDecoder.java137
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/G1rusFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/G1rusProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java161
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java66
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java50
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java20
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java32
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java203
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java38
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java61
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java297
-rw-r--r--src/main/java/org/traccar/protocol/ThurayaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java195
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolDecoder.java9
29 files changed, 1525 insertions, 212 deletions
diff --git a/src/main/java/org/traccar/protocol/BstplProtocol.java b/src/main/java/org/traccar/protocol/BstplProtocol.java
new file mode 100644
index 000000000..dde14a2ca
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BstplProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.
+ * 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.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class BstplProtocol extends BaseProtocol {
+
+ @Inject
+ public BstplProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new BstplProtocolDecoder(BstplProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java
new file mode 100644
index 000000000..15c114642
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 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.
+ * 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.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class BstplProtocolDecoder extends BaseProtocolDecoder {
+
+ public BstplProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("BSTPL$") // header
+ .number("(d),") // type
+ .expression("([^,]+),") // device id
+ .expression("([AV]),") // validity
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+),([0NS]),") // latitude
+ .number("(d+.d+),([0EW]),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // odometer
+ .number("(d+),") // course
+ .number("(d+),") // satellites
+ .number("([01]),") // box open
+ .number("(d+),") // rssi
+ .number("([01]),") // charge
+ .number("([01]),") // ignition
+ .number("([01]),") // engine
+ .number("([01]),") // locked
+ .number("(d+.d+),") // adc
+ .number("d+,") // reserved
+ .number("(d+.d+),") // battery
+ .expression("([^,]+),") // firmware
+ .number("([^,]+),") // iccid
+ .number("(d+.d+)") // power
+ .compile();
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 4:
+ return Position.ALARM_LOW_BATTERY;
+ case 5:
+ return Position.ALARM_ACCELERATION;
+ case 6:
+ return Position.ALARM_BRAKING;
+ case 7:
+ return Position.ALARM_OVERSPEED;
+ case 9:
+ return Position.ALARM_SOS;
+ default:
+ return null;
+ }
+ }
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ int type = parser.nextInt();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(type));
+
+ position.setValid(parser.next().equals("A"));
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt() * 1000L);
+
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ boolean boxOpen = parser.nextInt() > 0;
+ if (type == 8 && boxOpen) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+ position.set("boxOpen", boxOpen);
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ boolean charge = parser.nextInt() > 0;
+ if (type == 3) {
+ position.set(Position.KEY_ALARM, charge ? Position.ALARM_POWER_RESTORED : Position.ALARM_POWER_CUT);
+ }
+ position.set(Position.KEY_CHARGE, charge);
+
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ position.set("engine", parser.nextInt() > 0);
+ position.set(Position.KEY_BLOCKED, parser.nextInt() > 0);
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ICCID, parser.next());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 741f4b35a..a9d77b46e 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -34,6 +34,10 @@ import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
import java.util.regex.Pattern;
public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
@@ -78,7 +82,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.text("$$")
.number("d+,") // length
.number("(d+),") // imei
- .number("x+,") // index
+ .number("(x+),") // index
.text("A03,") // type
.number("(d+)?,") // alarm
.number("(dd)(dd)(dd)") // date (yymmdd)
@@ -137,16 +141,20 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("xx")
.compile();
- private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String imei, String content) {
if (channel != null) {
- String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
int length = 1 + imei.length() + 1 + content.length();
String response = String.format("##%02d,%s,%s*", length, imei, content);
response += Checksum.sum(response) + "\r\n";
- channel.writeAndFlush(new NetworkMessage(response, socketAddress));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
+ private void requestPhoto(Channel channel, SocketAddress remoteAddress, String imei, String file) {
+ String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
+ sendResponse(channel, remoteAddress, imei, content);
+ }
+
private String decodeAlarm(Integer alarm) {
if (alarm != null) {
switch (alarm) {
@@ -200,11 +208,14 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
+ String index = parser.next();
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -243,6 +254,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(network);
+ DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String response = index + ",A03," + dateFormat.format(new Date());
+ sendResponse(channel, remoteAddress, imei, response);
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java
new file mode 100644
index 000000000..8c67207ad
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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.
+ * 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 io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class G1rusFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ ByteBuf result = Unpooled.buffer();
+
+ while (buf.isReadable()) {
+ int b = buf.readUnsignedByte();
+ if (b == 0x1B) {
+ int ext = buf.readUnsignedByte();
+ if (ext == 0x00) {
+ result.writeByte(0x1B);
+ } else {
+ result.writeByte(0xF8);
+ }
+ } else {
+ result.writeByte(b);
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/G1rusProtocol.java b/src/main/java/org/traccar/protocol/G1rusProtocol.java
new file mode 100644
index 000000000..f1823762d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.
+ * 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.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class G1rusProtocol extends BaseProtocol {
+
+ @Inject
+ public G1rusProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new G1rusFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new G1rusProtocolDecoder(G1rusProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java
new file mode 100644
index 000000000..e974e446d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 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.
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class G1rusProtocolDecoder extends BaseProtocolDecoder {
+ public G1rusProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HEARTBEAT = 0;
+ public static final int MSG_REGULAR = 1;
+ public static final int MSG_SMS_FORWARD = 2;
+ public static final int MSG_SERIAL = 3;
+ public static final int MSG_MIXED = 4;
+
+ private String readString(ByteBuf buf) {
+ int length = buf.readUnsignedByte() & 0xF;
+ return buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ }
+
+ private Position decodeRegular(DeviceSession deviceSession, ByteBuf buf, int type) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(new Date((buf.readUnsignedIntLE() + 946684800) * 1000L));
+
+ if (BitUtil.check(type, 6)) {
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ }
+
+ int dataMask = buf.readUnsignedShort();
+
+ if (BitUtil.check(dataMask, 0)) {
+ buf.readUnsignedByte(); // length
+ readString(buf); // device name
+ position.set(Position.KEY_VERSION_FW, readString(buf));
+ position.set(Position.KEY_VERSION_HW, readString(buf));
+ }
+
+ if (BitUtil.check(dataMask, 1)) {
+ buf.readUnsignedByte(); // length
+ int locationMask = buf.readUnsignedShort();
+ if (BitUtil.check(locationMask, 0)) {
+ int validity = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.to(validity, 5));
+ position.setValid(BitUtil.between(validity, 5, 7) == 2);
+ }
+ if (BitUtil.check(locationMask, 1)) {
+ position.setLatitude(buf.readInt() / 1000000.0);
+ position.setLongitude(buf.readInt() / 1000000.0);
+ }
+ if (BitUtil.check(locationMask, 2)) {
+ position.setSpeed(buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 3)) {
+ position.setCourse(buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 4)) {
+ position.setAltitude(buf.readShort());
+ }
+ if (BitUtil.check(locationMask, 5)) {
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 6)) {
+ position.set(Position.KEY_VDOP, buf.readUnsignedShort());
+ }
+ }
+
+ if (BitUtil.check(dataMask, 2)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 3)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 4)) {
+ buf.readUnsignedByte(); // length
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 110 / 4096 - 10);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 110 / 4096 - 10);
+ position.set(Position.KEY_DEVICE_TEMP, buf.readUnsignedShort() * 110 / 4096 - 10);
+ }
+
+ if (BitUtil.check(dataMask, 5)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 7)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int type = buf.readUnsignedByte();
+ String imei = String.valueOf(buf.readLong());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (BitUtil.to(type, 6) == MSG_REGULAR) {
+
+ return decodeRegular(deviceSession, buf, type);
+
+ } else if (BitUtil.to(type, 6) == MSG_MIXED) {
+
+ List<Position> positions = new LinkedList<>();
+ while (buf.readableBytes() > 5) {
+ int length = buf.readUnsignedShort();
+ int subtype = buf.readUnsignedByte();
+ if (BitUtil.to(subtype, 6) == MSG_REGULAR) {
+ positions.add(decodeRegular(deviceSession, buf, subtype));
+ } else {
+ buf.skipBytes(length);
+ }
+ }
+ return positions.isEmpty() ? null : positions;
+
+ }
+
+ buf.skipBytes(2);
+ buf.readUnsignedByte(); // tail
+
+ return null;
+
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index d8a753abe..fc8a49cf5 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -20,14 +20,16 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitBuffer;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -35,6 +37,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TimeZone;
public class GalileoProtocolDecoder extends BaseProtocolDecoder {
@@ -229,7 +232,11 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
int header = buf.readUnsignedByte();
if (header == 0x01) {
- return decodePositions(channel, remoteAddress, buf);
+ if (buf.getUnsignedMedium(buf.readerIndex() + 2) == 0x01001c) {
+ return decodeIridiumPosition(channel, remoteAddress, buf);
+ } else {
+ return decodePositions(channel, remoteAddress, buf);
+ }
} else if (header == 0x07) {
return decodePhoto(channel, remoteAddress, buf);
}
@@ -237,9 +244,58 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodePositions(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+ private void decodeMinimalDataSet(Position position, ByteBuf buf) {
+ BitBuffer bits = new BitBuffer(buf.readSlice(10));
+ bits.readUnsigned(1);
+
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ calendar.set(Calendar.DAY_OF_YEAR, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY));
+ calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE));
+ calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND));
+ calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND));
+ calendar.add(Calendar.SECOND, bits.readUnsigned(25));
+ position.setTime(calendar.getTime());
+
+ position.setValid(bits.readUnsigned(1) == 0);
+ position.setLongitude(360 * bits.readUnsigned(22) / 4194304.0 - 180);
+ position.setLatitude(360 * bits.readUnsigned(21) / 2097152.0 - 90);
+ if (bits.readUnsigned(1) > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ }
+
+ private Position decodeIridiumPosition(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ buf.readUnsignedShortLE(); // length
+
+ buf.skipBytes(3); // identification header
+ buf.readUnsignedIntLE(); // index
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedByte(); // session status
+ buf.skipBytes(4); // reserved
+ buf.readUnsignedIntLE(); // date and time
+
+ buf.skipBytes(23); // coordinates block
+
+ buf.skipBytes(3); // data tag header
+ decodeMinimalDataSet(position, buf);
+
+ return position;
+ }
+
+ private List<Position> decodePositions(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- int length = (buf.readUnsignedShortLE() & 0x7fff) + 3;
+ int endIndex = (buf.readUnsignedShortLE() & 0x7fff) + buf.readerIndex();
List<Position> positions = new LinkedList<>();
Set<Integer> tags = new HashSet<>();
@@ -248,7 +304,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
DeviceSession deviceSession = null;
Position position = new Position(getProtocolName());
- while (buf.readerIndex() < length) {
+ while (buf.readerIndex() < endIndex) {
int tag = buf.readUnsignedByte();
if (tags.contains(tag)) {
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index b63bcd0c0..28efa3c30 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -56,9 +56,12 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
.groupEnd()
.expression("([^,]+)?,") // rfid
.groupBegin()
- .text("L,,,")
+ .text("L,")
+ .groupBegin()
+ .text(",,")
.number("(x+),,") // lac
.number("(x+),,,") // cid
+ .groupEnd("?")
.or()
.text("F,")
.groupBegin()
@@ -218,13 +221,11 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext(2)) {
-
- getLastLocation(position, null);
-
position.setNetwork(new Network(CellTower.fromLacCid(
getConfig(), parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
- } else {
+ if (parser.hasNext(20)) {
String utcHours = parser.next();
String utcMinutes = parser.next();
@@ -262,6 +263,10 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
position.set("fuel2", parser.nextDouble());
position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ } else {
+
+ getLastLocation(position, null);
+
}
return position;
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index b65e4a4b8..a93c11cbc 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -410,10 +410,14 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_LOW_BATTERY;
case 0x11:
return Position.ALARM_POWER_OFF;
+ case 0x0C:
case 0x13:
+ case 0x25:
return Position.ALARM_TAMPERING;
case 0x14:
return Position.ALARM_DOOR;
+ case 0x23:
+ return Position.ALARM_FALL_DOWN;
case 0x29:
return Position.ALARM_ACCELERATION;
case 0x30:
@@ -423,8 +427,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_CORNERING;
case 0x2C:
return Position.ALARM_ACCIDENT;
- case 0x23:
- return Position.ALARM_FALL_DOWN;
default:
return null;
}
@@ -986,6 +988,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (subType == 0x05) {
+ if (buf.readableBytes() >= 6 + 1 + 6) {
+ DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setDeviceTime(dateBuilder.getDate());
+ }
+
int flags = buf.readUnsignedByte();
position.set(Position.KEY_DOOR, BitUtil.check(flags, 0));
position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2));
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
index e1c17710b..dc5dd446f 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -23,6 +23,7 @@ import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
+import org.traccar.model.Device;
import java.nio.charset.StandardCharsets;
@@ -44,7 +45,7 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
buf.writeByte(1 + 1 + 4 + content.length() + 2 + 2 + (language ? 2 : 0)); // message length
- buf.writeByte(0x80); // message type
+ buf.writeByte(Gt06ProtocolDecoder.MSG_COMMAND_0);
buf.writeByte(4 + content.length()); // command length
buf.writeInt(0);
@@ -73,13 +74,25 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
String password = AttributeUtil.getDevicePassword(
getCacheManager(), command.getDeviceId(), getProtocolName(), "123456");
+ Device device = getCacheManager().getObject(Device.class, command.getDeviceId());
+
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeContent(command.getDeviceId(),
- alternative ? "DYD," + password + "#" : "Relay,1#");
+ if ("G109".equals(device.getModel())) {
+ return encodeContent(command.getDeviceId(), "DYD#");
+ } else if (alternative) {
+ return encodeContent(command.getDeviceId(), "DYD," + password + "#");
+ } else {
+ return encodeContent(command.getDeviceId(), "Relay,1#");
+ }
case Command.TYPE_ENGINE_RESUME:
- return encodeContent(command.getDeviceId(),
- alternative ? "HFYD," + password + "#" : "Relay,0#");
+ if ("G109".equals(device.getModel())) {
+ return encodeContent(command.getDeviceId(), "HFYD#");
+ } else if (alternative) {
+ return encodeContent(command.getDeviceId(), "HFYD," + password + "#");
+ } else {
+ return encodeContent(command.getDeviceId(), "Relay,0#");
+ }
case Command.TYPE_CUSTOM:
return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA));
default:
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index b891bc388..0639b9dcf 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -490,7 +490,6 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
break;
case 0xD4:
- case 0xFE:
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
break;
case 0xD5:
@@ -514,6 +513,18 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2));
position.set("cover", BitUtil.check(deviceStatus, 3));
break;
+ case 0xE6:
+ while (buf.readerIndex() < endIndex) {
+ int sensorIndex = buf.readUnsignedByte();
+ buf.skipBytes(6); // mac
+ position.set(
+ Position.PREFIX_TEMP + sensorIndex,
+ buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+ position.set(
+ "humidity" + sensorIndex,
+ buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+ }
+ break;
case 0xEB:
if (buf.getUnsignedShort(buf.readerIndex()) > 200) {
Network network = new Network();
@@ -561,6 +572,43 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0xFE:
+ if (length == 1) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ } else {
+ int mark = buf.readUnsignedByte();
+ if (mark == 0x7C) {
+ while (buf.readerIndex() < endIndex) {
+ int extendedType = buf.readUnsignedByte();
+ int extendedLength = buf.readUnsignedByte();
+ switch (extendedType) {
+ case 0x01:
+ long alarms = buf.readUnsignedInt();
+ if (BitUtil.check(alarms, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ }
+ if (BitUtil.check(alarms, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+ if (BitUtil.check(alarms, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+ if (BitUtil.check(alarms, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ }
+ if (BitUtil.check(alarms, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+ break;
+ default:
+ buf.skipBytes(extendedLength);
+ break;
+ }
+ }
+ }
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ }
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
index 57e4c736f..7bbe6c8de 100644
--- a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 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.
@@ -260,7 +260,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
MqttSubscribeMessage message = (MqttSubscribeMessage) msg;
MqttMessage response = MqttMessageBuilders.subAck()
- .packetId((short) message.variableHeader().messageId())
+ .packetId(message.variableHeader().messageId())
.build();
if (channel != null) {
@@ -339,7 +339,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // checksum
MqttMessage response = MqttMessageBuilders.pubAck()
- .packetId((short) message.variableHeader().packetId())
+ .packetId(message.variableHeader().packetId())
.build();
if (channel != null) {
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 88b7d12c8..8a58ebc5e 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -204,11 +204,11 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
- String deviceModel = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
- if (deviceModel == null) {
- deviceModel = "";
+ String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
+ if (model == null) {
+ model = "";
}
- switch (deviceModel.toUpperCase()) {
+ switch (model.toUpperCase()) {
case "MVT340":
case "MVT380":
position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.0 * 2.0 / 1024.0);
diff --git a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
index 53631bd4e..77158b315 100644
--- a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -24,7 +24,6 @@ import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Checksum.Algorithm;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -591,10 +590,8 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder {
private void sendFlexReply(Channel channel, ByteBuf data) {
if (channel != null) {
- ByteBuf cs = Unpooled.buffer(1);
- cs.writeByte(Checksum.crc8(new Algorithm(8, 0x31, 0xFF, false, false, 0x00), data.nioBuffer()));
-
- channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(data, cs), channel.remoteAddress()));
+ data.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, data.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(data, channel.remoteAddress()));
}
}
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
index 9122eb362..08b1a8d0f 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 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.
@@ -196,7 +196,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
for (int j = 0; j < bits.length(); j++) {
if (bits.get(j)) {
- int value = 0;
+ int value;
switch (j + 1) {
case 1:
@@ -278,11 +278,11 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 42:
case 43:
value = buf.readUnsignedShortLE();
- position.set("rs485Fuel" + (j + 2 - 38), (value < 65500) ? value : null);
+ position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null);
break;
case 44:
value = buf.readUnsignedShortLE();
- position.set("rs232Fuel", (value < 65500) ? value : null);
+ position.set(Position.KEY_FUEL_LEVEL, (value < 65500) ? value : null);
break;
case 45:
case 46:
@@ -293,7 +293,17 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 51:
case 52:
value = buf.readByte();
- position.set(Position.PREFIX_TEMP + (j + 2 - 45), (value != 0x80) ? value : null);
+ position.set(
+ Position.PREFIX_TEMP + (j + 2 - 45),
+ (value != (byte) 0x80) ? value : null);
+ break;
+ case 78:
+ case 79:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte());
break;
default:
buf.skipBytes(getItemLength(j + 1));
diff --git a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
new file mode 100644
index 000000000..5b610013a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.
+ * 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 NdtpV6FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ return buf;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
new file mode 100644
index 000000000..ce0dbbef2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ * 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.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class NdtpV6Protocol extends BaseProtocol {
+
+ @Inject
+ public NdtpV6Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new NdtpV6ProtocolDecoder(NdtpV6Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
new file mode 100644
index 000000000..35cb0bae8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 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.
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class NdtpV6ProtocolDecoder extends BaseProtocolDecoder {
+
+ public NdtpV6ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final byte[] SIGNATURE = {0x7E, 0x7E};
+
+ private static final int NPL_FLAG_CRC = 2;
+ private static final int NPH_RESULT_OK = 0x00000000;
+ private static final int NPL_TYPE_NPH = 2;
+ private static final int NPL_ADDRESS_SERVER = 0;
+
+ private static final int NPH_RESULT = 0;
+
+ private static final int NPH_SRV_GENERIC_CONTROLS = 0;
+ private static final int NPH_SRV_NAVDATA = 1;
+
+ private static final int NPH_SGC_RESULT = NPH_RESULT;
+ private static final int NPH_SGC_CONN_REQUEST = 100;
+
+ private static final int NPH_SND_RESULT = NPH_RESULT;
+
+ private void sendResponse(
+ Channel channel, int serviceId, long requestId) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeShortLE(serviceId);
+ content.writeIntLE(NPH_SND_RESULT);
+ content.writeIntLE((int) requestId);
+ content.writeIntLE(NPH_RESULT_OK);
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes(SIGNATURE);
+ response.writeShortLE(content.readableBytes());
+ response.writeShortLE(NPL_FLAG_CRC); // flags
+ response.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, content.nioBuffer()));
+ response.writeByte(NPL_TYPE_NPH); // type
+ response.writeIntLE(NPL_ADDRESS_SERVER); // peer address
+ response.writeShortLE(0); // request id
+ response.writeBytes(content);
+ content.release();
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ private static final short MAIN_NAV_DATA = 0;
+ private static final short ADDITIONAL_NAV_DATA = 2;
+
+ private void decodeData(ByteBuf buf, Position position) {
+
+ short itemType;
+ short itemIndex;
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == MAIN_NAV_DATA && (itemIndex == 0 || itemIndex == 1)) {
+
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+
+ short flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20);
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedShortLE());
+ position.setAltitude(buf.readShortLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_PDOP, buf.readUnsignedByte());
+ }
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == ADDITIONAL_NAV_DATA && itemIndex == 0) {
+
+ position.set(Position.KEY_BATTERY_LEVEL, Math.max((buf.readUnsignedShortLE() - 3600) / 6, 100));
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE());
+
+ buf.readUnsignedByte(); // inputs
+ buf.readUnsignedByte(); // outputs
+ buf.readUnsignedShortLE(); // in1 count
+ buf.readUnsignedShortLE(); // in2 count
+ buf.readUnsignedShortLE(); // in3 count
+ buf.readUnsignedShortLE(); // in4 count
+ buf.readUnsignedIntLE(); // track length
+
+ position.set(Position.KEY_ANTENNA, buf.readUnsignedByte());
+ position.set(Position.KEY_GPS, buf.readUnsignedByte());
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+
+ buf.skipBytes(2); // signature
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // connection flags
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedByte(); // type
+ buf.readUnsignedIntLE(); // address
+ buf.readUnsignedShortLE(); // identification
+
+ int serviceId = buf.readUnsignedShortLE();
+ int serviceType = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // request flags
+ long requestId = buf.readUnsignedIntLE();
+
+ if (deviceSession == null && serviceId == NPH_SRV_GENERIC_CONTROLS && serviceType == NPH_SGC_CONN_REQUEST) {
+
+ buf.readUnsignedShortLE(); // version major
+ buf.readUnsignedShortLE(); // version minor
+ buf.readUnsignedShortLE(); // connection flags
+
+ int deviceId = buf.readUnsignedShortLE();
+ Position position = new Position(getProtocolName());
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT));
+ position.setTime(new Date());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+
+ return position;
+
+ }
+
+ if (serviceId == NPH_SRV_NAVDATA) {
+
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ decodeData(buf, position);
+
+ return position;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
new file mode 100644
index 000000000..7aac8658a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.
+ * 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 org.traccar.BaseProtocolEncoder;
+import org.traccar.Protocol;
+import org.traccar.model.Command;
+
+public class NdtpV6ProtocolEncoder extends BaseProtocolEncoder {
+
+ public NdtpV6ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_IDENTIFICATION:
+ return "BB+IDNT";
+ case Command.TYPE_REBOOT_DEVICE:
+ return "BB+RESET";
+ case Command.TYPE_POSITION_SINGLE:
+ return "BB+RRCD";
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
index 5f8e7424b..c8968023a 100644
--- a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -143,6 +143,9 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
case "driverUniqueId":
position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
break;
+ case "charge":
+ position.set(Position.KEY_CHARGE, Boolean.parseBoolean(value));
+ break;
default:
try {
position.set(entry.getKey(), Double.parseDouble(value));
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
index 244df6806..34c879cb8 100644
--- a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -22,16 +22,19 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
+import java.util.regex.Pattern;
public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
@@ -47,6 +50,21 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
public static final int MSG_GPS_SENSORS = 0xF2;
public static final int MSG_EVENTS = 0xF3;
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("[^$]+")
+ .text("$GPRMC,")
+ .number("(dd)(dd)(dd).d+,") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d{2,3})(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .any()
+ .compile();
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -150,6 +168,42 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
}
return positions;
+
+ } else if (uri.startsWith("/push.do")) {
+
+ sendResponse(channel, "PUSH.DO: OK");
+
+ String sentence = request.content().toString(StandardCharsets.US_ASCII);
+
+ String[] parts = sentence.split("&");
+ String phone = parts[1].substring(16);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, phone);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Parser parser = new Parser(PATTERN, parts[2]);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
index 53c02f28c..b2fcd5452 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -42,6 +42,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("d+,") // length
.number("(d+),") // imei
.expression("(.+)") // content
+ .number("xx") // checksum
.compile();
private static final Pattern PATTERN_POSITION = new PatternBuilder()
@@ -73,22 +74,26 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.groupBegin()
.text(",")
.number("d,") // extended
- .expression("([^,]+)?,") // fuel
+ .expression("([^,]+)?") // fuel
+ .groupBegin()
+ .text(",")
.expression("([^,]+)?") // temperature
.groupBegin()
.text(",")
- .number("(d+)|") // rpm
- .number("(d+)|") // engine load
- .number("d+|") // maf flow
- .number("d+|") // intake pressure
- .number("d+|") // intake temperature
- .number("(d+)|") // throttle
- .number("(d+)|") // coolant temperature
- .number("(d+)|") // instant fuel
- .number("(d+)") // fuel level
+ .groupBegin()
+ .number("(d+)?|") // rpm
+ .number("(d+)?|") // engine load
+ .number("d*|") // maf flow
+ .number("d*|") // intake pressure
+ .number("d*|") // intake temperature
+ .number("(d+)?|") // throttle
+ .number("(d+)?|") // coolant temperature
+ .number("(d+)?|") // instant fuel
+ .number("(d+)[%L]").optional() // fuel level
+ .groupEnd("?")
+ .groupEnd("?")
.groupEnd("?")
.groupEnd("?")
- .any()
.compile();
private String decodeAlarm(int value) {
@@ -122,9 +127,6 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
}
String content = parser.next();
- if (content.charAt(content.length() - 2 - 1) != '|') {
- content = content.substring(0, content.length() - 2);
- }
if (content.length() < 100) {
Position position = new Position(getProtocolName());
@@ -223,8 +225,12 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_ENGINE_LOAD, parser.nextInt());
position.set(Position.KEY_THROTTLE, parser.nextInt());
- position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40);
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1);
+ }
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 52fdaa05c..f1097abac 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -570,7 +570,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(mask, 17)) {
- position.set(Position.KEY_INPUT, Integer.parseInt(values[index++]));
+ int input = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
}
if (BitUtil.check(mask, 18)) {
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index 409c7e768..1529aae29 100644
--- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -125,6 +125,30 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.expression("([01])") // ignition
.compile();
+ private static final Pattern PATTERN_PUBX = new PatternBuilder()
+ .text("$PUBX,")
+ .number("(d+),") // index
+ .number("(dd)(dd)(dd).d+,") // time (hhmmss)
+ .number("(dd)(dd.d+),([NS]),") // latitude
+ .number("(ddd)(dd.d+),([EW]),") // longitude
+ .number("(-?d+.d+),") // altitude
+ .expression("(..),") // status
+ .number("(d+.d+),") // horizontal accuracy
+ .number("d+.d+,") // vertical accuracy
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("-?d+.d+,") // vertical velocity
+ .expression("[^,]*,") // corrections age
+ .number("(d+.d+),") // hdop
+ .number("(d+.d+),") // vdop
+ .number("d+.d+,") // tdop
+ .number("(d+),") // satellites
+ .number("(d+),") // device id
+ .number("d+")
+ .text("*")
+ .number("xx") // checksum
+ .compile();
+
private Position position = null;
private Position decodeGprmc(
@@ -303,6 +327,39 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodePubx(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_PUBX, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+
+ position.set(Position.KEY_INDEX, parser.nextInt());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setAltitude(parser.nextDouble());
+ position.setValid(!parser.next().equals("NF"));
+ position.setAccuracy(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_VDOP, parser.nextDouble());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+
+ return null;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -357,6 +414,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return decodeGpiop(deviceSession, sentence);
} else if (sentence.startsWith("QZE")) {
return decodeQze(channel, remoteAddress, sentence);
+ } else if (sentence.startsWith("$PUBX")) {
+ return decodePubx(channel, remoteAddress, sentence);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 77047fe26..4671a1088 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,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.model.Device;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -38,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<>();
@@ -184,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.readShort() * 0.1));
+ register(73, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 2, b.readShort() * 0.1));
+ register(74, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 3, b.readShort() * 0.1));
+ register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readShort() * 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 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(getConfig(), lac, cid);
- long operator = position.getInteger(Position.KEY_OPERATOR);
- if (operator >= 1000) {
- cellTower.setOperator(operator);
+ 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),
+ (Integer) position.getAttributes().remove(mncKey),
+ (Integer) position.getAttributes().remove(lacKey),
+ (Integer) position.getAttributes().remove(cidKey));
+ cellTower.setSignalStrength((Integer) position.getAttributes().remove(rssiKey));
+ network.addCellTower(cellTower);
+ }
+ }
+
+ 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));
}
}
@@ -387,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;
@@ -484,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);
}
}
@@ -492,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);
}
}
@@ -500,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);
}
}
@@ -508,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);
}
}
@@ -562,7 +569,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- decodeNetwork(position);
+ decodeNetwork(position, model);
}
@@ -578,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());
@@ -603,7 +610,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
} else if (codec == CODEC_12) {
decodeSerial(channel, remoteAddress, deviceSession, position, buf);
} else {
- decodeLocation(position, buf, codec);
+ decodeLocation(position, buf, codec, model);
}
if (!position.getOutdated() || !position.getAttributes().isEmpty()) {
diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
new file mode 100644
index 000000000..f709a1183
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ * 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.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class ThurayaProtocol extends BaseProtocol {
+
+ @Inject
+ public ThurayaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
+ pipeline.addLast(new ThurayaProtocolDecoder(ThurayaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java
new file mode 100644
index 000000000..a287ece34
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 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.
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ThurayaProtocolDecoder extends BaseProtocolDecoder {
+
+ public ThurayaProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_EVENT = 0x5101;
+ public static final int MSG_PERIODIC_REPORT = 0x7101;
+ public static final int MSG_SETTING_RESPONSE = 0x8115;
+ public static final int MSG_ACK = 0x9901;
+
+ private static int checksum(ByteBuffer buf) {
+ int crc = 0;
+ while (buf.hasRemaining()) {
+ crc += buf.get();
+ }
+ crc = ~crc;
+ crc += 1;
+ return crc;
+ }
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, long id, int type) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence("#T", StandardCharsets.US_ASCII);
+ response.writeShort(15); // length
+ response.writeShort(MSG_ACK);
+ response.writeInt((int) id);
+ response.writeShort(type);
+ response.writeShort(1); // server ok
+ response.writeShort(checksum(response.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private void decodeLocation(ByteBuf buf, Position position) {
+
+ position.setValid(true);
+
+ DateBuilder dateBuilder = new DateBuilder();
+
+ int date = buf.readInt();
+ dateBuilder.setDay(date % 100);
+ date /= 100;
+ dateBuilder.setMonth(date % 100);
+ date /= 100;
+ dateBuilder.setYear(date);
+
+ int time = buf.readInt();
+ dateBuilder.setSecond(time % 100);
+ time /= 100;
+ dateBuilder.setMinute(time % 100);
+ time /= 100;
+ dateBuilder.setHour(time);
+
+ position.setTime(dateBuilder.getDate());
+
+ position.setLongitude(buf.readInt() / 1000000.0);
+ position.setLatitude(buf.readInt() / 1000000.0);
+
+ int data = buf.readUnsignedShort();
+
+ int ignition = BitUtil.from(data, 12);
+ if (ignition == 1) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (ignition == 2) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+
+ position.setCourse(BitUtil.to(data, 12));
+ position.setSpeed(buf.readShort());
+
+ position.set(Position.KEY_RPM, buf.readShort());
+
+ position.set("data", readString(buf));
+ }
+
+ private String decodeAlarm(int event) {
+ switch (event) {
+ case 10:
+ return Position.ALARM_VIBRATION;
+ case 11:
+ return Position.ALARM_OVERSPEED;
+ case 12:
+ return Position.ALARM_POWER_CUT;
+ case 13:
+ return Position.ALARM_LOW_BATTERY;
+ case 18:
+ return Position.ALARM_GPS_ANTENNA_CUT;
+ case 20:
+ return Position.ALARM_ACCELERATION;
+ case 21:
+ return Position.ALARM_BRAKING;
+ default:
+ return null;
+ }
+ }
+
+ private String readString(ByteBuf buf) {
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ CharSequence value = buf.readCharSequence(endIndex - buf.readerIndex(), StandardCharsets.US_ASCII);
+ buf.readUnsignedByte(); // delimiter
+ return value.toString();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // service
+ buf.readUnsignedShort(); // length
+ int type = buf.readUnsignedShort();
+ long id = buf.readUnsignedInt();
+
+ sendResponse(channel, remoteAddress, id, type);
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (type == MSG_EVENT) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(buf, position);
+
+ int event = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+ position.set("eventData", readString(buf));
+
+ return position;
+
+ } else if (type == MSG_PERIODIC_REPORT) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(buf, position);
+
+ positions.add(position);
+
+ }
+
+ return positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index e197a8a41..b343c3b33 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -99,6 +99,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_CELL = new PatternBuilder()
+ .text("(")
+ .number("(d{12})") // device id
+ .expression(".{4}") // type
+ .number("(?:d{15})?,") // imei
+ .expression("(.+),") // cell
+ .number("(d{8})") // odometer
+ .text(")")
+ .compile();
+
private static final Pattern PATTERN_NETWORK = new PatternBuilder()
.text("(").optional()
.number("(d{12})") // device id
@@ -297,6 +307,39 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeCell(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_CELL, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ String[] cells = parser.next().split("\n");
+ for (String cell : cells) {
+ String[] values = cell.substring(1, cell.length() - 1).split(",");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+ Integer.parseInt(values[2]), Integer.parseInt(values[3])));
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
+
+ return position;
+ }
+
private Position decodeNetwork(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_NETWORK, sentence);
if (!parser.matches()) {
@@ -422,7 +465,9 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
}
}
- if (sentence.contains("ZC20")) {
+ if (sentence.indexOf('{') > 0 && sentence.indexOf('}') > 0) {
+ return decodeCell(channel, remoteAddress, sentence);
+ } else if (sentence.contains("ZC20")) {
return decodeBattery(channel, remoteAddress, sentence);
} else if (sentence.contains("BZ00")) {
return decodeNetwork(channel, remoteAddress, sentence);
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
index 22d7bade3..46aa65a5d 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 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.
@@ -74,6 +74,9 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
|| buf.toString(StandardCharsets.US_ASCII).startsWith("$MSG:")) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -89,12 +92,12 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime());