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/AdmProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java94
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java24
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java56
-rw-r--r--src/main/java/org/traccar/protocol/FlexibleReportProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java169
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java177
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java40
-rw-r--r--src/main/java/org/traccar/protocol/Gs100Protocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java138
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java53
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocolDecoder.java207
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java35
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java61
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolEncoder.java36
-rw-r--r--src/main/java/org/traccar/protocol/Mavlink2Protocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java85
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java45
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java72
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/R12wProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/R12wProtocolDecoder.java62
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java78
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java35
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocol.java46
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java171
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolEncoder.java56
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TaipPrefixEncoder.java47
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocol.java4
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/protocol/ThinkPowerProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java182
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java101
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java29
-rw-r--r--src/main/java/org/traccar/protocol/Tr20Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocol.java6
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocolEncoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocolDecoder.java95
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolDecoder.java26
54 files changed, 2406 insertions, 304 deletions
diff --git a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
index 31064286e..7e3478704 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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.
@@ -97,7 +97,7 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(type, 5)) {
for (int i = 1; i <= 3; i++) {
- buf.readUnsignedShortLE(); // fuel level
+ position.set("fuel" + i, buf.readUnsignedShortLE());
}
for (int i = 1; i <= 3; i++) {
position.set(Position.PREFIX_TEMP + i, buf.readUnsignedByte());
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
index ff7ef6c4a..186b81470 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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.
@@ -16,6 +16,7 @@
package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
@@ -24,6 +25,8 @@ import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -115,6 +118,89 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
return result;
}
+ private void decodeBeaconData(Position position, int mode, int mask, ByteBuf data) {
+ int i = 1;
+ while (data.isReadable()) {
+ if (BitUtil.check(mask, 7)) {
+ position.set("tag" + i + "Id", ByteBufUtil.hexDump(data.readSlice(6)));
+ }
+ switch (mode) {
+ case 1:
+ if (BitUtil.check(mask, 6)) {
+ data.readUnsignedShort(); // major
+ }
+ if (BitUtil.check(mask, 5)) {
+ data.readUnsignedShort(); // minor
+ }
+ if (BitUtil.check(mask, 4)) {
+ data.readUnsignedByte(); // tx power
+ }
+ if (BitUtil.check(mask, 3)) {
+ position.set("tag" + i + "Rssi", data.readUnsignedByte());
+ }
+ break;
+ case 2:
+ if (BitUtil.check(mask, 6)) {
+ data.readUnsignedShort(); // battery voltage
+ }
+ if (BitUtil.check(mask, 5)) {
+ position.set("tag" + i + "Temp", data.readUnsignedShort());
+ }
+ if (BitUtil.check(mask, 4)) {
+ data.readUnsignedByte(); // tx power
+ }
+ if (BitUtil.check(mask, 3)) {
+ position.set("tag" + i + "Rssi", data.readUnsignedByte());
+ }
+ break;
+ case 3:
+ if (BitUtil.check(mask, 6)) {
+ position.set("tag" + i + "Humidity", data.readUnsignedShort());
+ }
+ if (BitUtil.check(mask, 5)) {
+ position.set("tag" + i + "Temp", data.readUnsignedShort());
+ }
+ if (BitUtil.check(mask, 3)) {
+ position.set("tag" + i + "Rssi", data.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 2)) {
+ data.readUnsignedShort();
+ }
+ break;
+ case 4:
+ if (BitUtil.check(mask, 6)) {
+ int hardwareId = data.readUnsignedByte();
+ if (BitUtil.check(mask, 5)) {
+ switch (hardwareId) {
+ case 1:
+ case 4:
+ data.skipBytes(11); // fuel
+ break;
+ case 2:
+ data.skipBytes(2); // temperature
+ break;
+ case 3:
+ data.skipBytes(6); // temperature and luminosity
+ break;
+ case 5:
+ data.skipBytes(10); // temperature, humidity, luminosity and pressure
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (BitUtil.check(mask, 4)) {
+ data.skipBytes(9); // name
+ }
+ break;
+ default:
+ break;
+ }
+ i += 1;
+ }
+ }
+
private void readTextCustomData(Position position, String data, String form) {
CellTower cellTower = new CellTower();
String[] keys = form.substring(1).split("%");
@@ -208,6 +294,12 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
case "MT":
position.set(Position.KEY_MOTION, Integer.parseInt(values[i]) > 0);
break;
+ case "BC":
+ String[] beaconValues = values[i].split(":");
+ decodeBeaconData(
+ position, Integer.parseInt(beaconValues[0]), Integer.parseInt(beaconValues[1]),
+ Unpooled.wrappedBuffer(DataConverter.parseHex(beaconValues[2])));
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
index 75fdc3253..96b06557a 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2021 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.
@@ -197,7 +197,8 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder {
} else if (fieldId == 6) {
while (buf.readerIndex() < fieldEnd) {
- switch (buf.readUnsignedByte()) {
+ int number = buf.readUnsignedByte();
+ switch (number) {
case 1:
position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
break;
@@ -214,7 +215,7 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder {
position.set("solarPower", buf.readUnsignedShortLE() * 0.001);
break;
default:
- buf.readUnsignedShortLE(); // other
+ position.set(Position.PREFIX_IO + number, buf.readUnsignedShortLE());
break;
}
}
diff --git a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
index 56d9314b2..e882c2378 100644
--- a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2021 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.
@@ -17,9 +17,11 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -43,7 +45,7 @@ public class DolphinProtocolDecoder extends BaseProtocolDecoder {
ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedShort(); // header
- buf.readUnsignedIntLE(); // index
+ int index = (int) buf.readUnsignedIntLE();
buf.readUnsignedShort(); // version
buf.readUnsignedShort(); // flags
int type = buf.readUnsignedShortLE();
@@ -61,6 +63,24 @@ public class DolphinProtocolDecoder extends BaseProtocolDecoder {
DolphinMessages.DataPackRequest message = DolphinMessages.DataPackRequest.parseFrom(
ByteBufUtil.getBytes(buf, buf.readerIndex(), length, false));
+ if (channel != null) {
+ byte[] responseData = DolphinMessages.DataPackResponse.newBuilder()
+ .setResponse(DolphinMessages.DataPackResponseCode.DataPack_OK)
+ .build()
+ .toByteArray();
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(0xABAB); // header
+ response.writeIntLE(index);
+ response.writeShort(0); // flags
+ response.writeShortLE(DolphinMessages.MessageType.DataPack_Response.getNumber());
+ response.writeIntLE(responseData.length);
+ response.writeIntLE(0); // reserved
+ response.writeBytes(responseData);
+
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
List<Position> positions = new LinkedList<>();
for (int i = 0; i < message.getPointsCount(); i++) {
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index fe42a44d7..70972f847 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 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.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -95,6 +96,16 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // size
.compile();
+ private static final Pattern PATTERN_RESULT = new PatternBuilder()
+ .text("$$")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .any()
+ .expression(",([A-Z]+)") // result
+ .text("*")
+ .number("xx")
+ .compile();
+
private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
if (channel != null) {
String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
@@ -177,7 +188,12 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextLong());
- position.set(Position.KEY_STATUS, parser.nextHexLong());
+
+ long status = parser.nextHexLong();
+ position.set(Position.KEY_RSSI, BitUtil.between(status, 3, 8));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(status, 28));
+ position.set(Position.KEY_STATUS, status);
+
position.set(Position.KEY_INPUT, parser.nextHexInt());
position.set(Position.KEY_OUTPUT, parser.nextHexInt());
@@ -203,6 +219,27 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Object decodeResult(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_RESULT, 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());
+
+ position.set(Position.KEY_RESULT, parser.next());
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -213,7 +250,12 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
typeIndex = buf.indexOf(typeIndex, buf.writerIndex(), (byte) ',') + 1;
String type = buf.toString(typeIndex, 3, StandardCharsets.US_ASCII);
- if (type.equals("D05")) {
+ if (type.startsWith("B")) {
+
+ return decodeResult(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+
+ } else if (type.equals("D05")) {
+
String sentence = buf.toString(StandardCharsets.US_ASCII);
Parser parser = new Parser(PATTERN_PHOTO, sentence);
if (parser.matches()) {
@@ -223,7 +265,9 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
photo = Unpooled.buffer(length);
requestPhoto(channel, remoteAddress, imei, photoId);
}
+
} else if (type.equals("D06")) {
+
if (photo == null) {
return null;
}
@@ -251,9 +295,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return position;
}
}
+
} else {
- String sentence = buf.toString(StandardCharsets.US_ASCII);
- return decodeLocation(channel, remoteAddress, sentence);
+
+ return decodeLocation(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
new file mode 100644
index 000000000..0cd55343a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class FlexibleReportProtocol extends BaseProtocol {
+
+ public FlexibleReportProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FlexibleReportProtocolDecoder(FlexibleReportProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java
new file mode 100644
index 000000000..759f2cd6f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021 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.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class FlexibleReportProtocolDecoder extends BaseProtocolDecoder {
+
+ public FlexibleReportProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_GENERAL = 0x00;
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x7E); // header
+ response.writeShort(2); // length
+ response.writeByte(0xE0);
+ response.writeByte(BitUtil.check(index, 0) ? 0x4F : 0x0F);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private Date decodeTime(ByteBuf buf) {
+ int timestamp = buf.readInt();
+ return new DateBuilder()
+ .setSecond(timestamp % 60)
+ .setMinute((timestamp / 60) % 60)
+ .setHour((timestamp / (60 * 60)) % 24)
+ .setDay(1 + timestamp / (60 * 60 * 24) % 31)
+ .setMonth(1 + timestamp / (60 * 60 * 24 * 31) % 12)
+ .setYear(2000 + timestamp / (60 * 60 * 24 * 31 * 12))
+ .getDate();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ int flags = buf.readUnsignedByte();
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int index = buf.readUnsignedShort();
+
+ if (BitUtil.to(flags, 2) > 0) {
+ sendResponse(channel, remoteAddress, index);
+ }
+
+ Date time = decodeTime(buf);
+ int event = buf.readUnsignedByte();
+
+ buf.readUnsignedByte(); // length
+
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_GENERAL) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(time);
+
+ position.set(Position.KEY_EVENT, event);
+
+ buf.readUnsignedByte(); // length
+ long mask = buf.readUnsignedInt();
+
+ if (BitUtil.check(mask, 0)) {
+ buf.readUnsignedByte(); // product id
+ }
+ if (BitUtil.check(mask, 1)) {
+ position.setFixTime(decodeTime(buf));
+ }
+ if (BitUtil.check(mask, 2)) {
+ position.setValid(true);
+ position.setLatitude(buf.readUnsignedInt() / 1000000.0 - 90);
+ position.setLongitude(buf.readUnsignedInt() / 1000000.0 - 180);
+ }
+ if (BitUtil.check(mask, 3)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.setCourse(buf.readUnsignedShort());
+ }
+ if (BitUtil.check(mask, 4)) {
+ position.setAltitude(buf.readShort());
+ }
+ if (BitUtil.check(mask, 5)) {
+ buf.readUnsignedShort(); // gps accuracy
+ }
+ if (BitUtil.check(mask, 6)) {
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ }
+ if (BitUtil.check(mask, 7)) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ }
+ if (BitUtil.check(mask, 8)) {
+ position.set("auxPower", buf.readUnsignedShort() * 0.001);
+ }
+ if (BitUtil.check(mask, 9)) {
+ position.set("solarPower", buf.readUnsignedShort() * 0.001);
+ }
+ if (BitUtil.check(mask, 10)) {
+ int cellService = buf.readUnsignedByte();
+ position.set(Position.KEY_ROAMING, BitUtil.check(cellService, 7));
+ position.set("service", BitUtil.to(cellService, 7));
+ buf.skipBytes(4); // cell info
+ }
+ if (BitUtil.check(mask, 11)) {
+ buf.readUnsignedByte(); // rssi
+ }
+ if (BitUtil.check(mask, 12)) {
+ int inputs = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, BitUtil.check(inputs, 0));
+ position.set(Position.PREFIX_IO + 1, inputs);
+ }
+ if (BitUtil.check(mask, 13)) {
+ position.set(Position.PREFIX_IO + 2, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 14)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
+ }
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() * 0.01);
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
index b6d7f4e45..aded35823 100644
--- a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2021 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.
@@ -75,9 +75,9 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder {
}
private Object decodePosition(
- Channel channel, SocketAddress remoteAddress, String sentence) throws Exception {
+ Channel channel, SocketAddress remoteAddress, String sentence, String id) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
@@ -88,94 +88,104 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder {
for (String pair : sentence.split(",")) {
String[] data = pair.split("[=:]");
- int key = Integer.parseInt(data[0], 16);
+ int key;
+ try {
+ key = Integer.parseInt(data[0], 16);
+ } catch (NumberFormatException e) {
+ continue;
+ }
String value = data[1];
- switch (key) {
- case 0x0:
- if (position != null) {
- position.setTime(dateBuilder.getDate());
- positions.add(position);
- }
- position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- position.setValid(true);
- dateBuilder = new DateBuilder(new Date());
- break;
- case 0x11:
- value = ("000000" + value).substring(value.length());
- dateBuilder.setDateReverse(
- Integer.parseInt(value.substring(0, 2)),
- Integer.parseInt(value.substring(2, 4)),
- Integer.parseInt(value.substring(4)));
- break;
- case 0x10:
- value = ("00000000" + value).substring(value.length());
- dateBuilder.setTime(
- Integer.parseInt(value.substring(0, 2)),
- Integer.parseInt(value.substring(2, 4)),
- Integer.parseInt(value.substring(4, 6)),
- Integer.parseInt(value.substring(6)) * 10);
- break;
- case 0xA:
- position.setLatitude(Double.parseDouble(value));
- break;
- case 0xB:
- position.setLongitude(Double.parseDouble(value));
- break;
- case 0xC:
- position.setAltitude(Double.parseDouble(value));
- break;
- case 0xD:
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(value)));
- break;
- case 0xE:
- position.setCourse(Integer.parseInt(value));
- break;
- case 0xF:
- position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
- break;
- case 0x12:
- position.set(Position.KEY_HDOP, Integer.parseInt(value));
- break;
- case 0x20:
- position.set(Position.KEY_ACCELERATION, value);
- break;
- case 0x24:
- position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.01);
- break;
- case 0x81:
- position.set(Position.KEY_RSSI, Integer.parseInt(value));
- break;
- case 0x82:
- position.set(Position.KEY_DEVICE_TEMP, Integer.parseInt(value) * 0.1);
- break;
- case 0x104:
- position.set(Position.KEY_ENGINE_LOAD, Integer.parseInt(value));
- break;
- case 0x105:
- position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(value));
- break;
- case 0x10c:
- position.set(Position.KEY_RPM, Integer.parseInt(value));
- break;
- case 0x10d:
- position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(value)));
- break;
- case 0x111:
- position.set(Position.KEY_THROTTLE, Integer.parseInt(value));
- break;
- default:
- position.set(Position.PREFIX_IO + key, value);
- break;
+ if (key == 0x0) {
+ if (position != null) {
+ position.setTime(dateBuilder.getDate());
+ positions.add(position);
+ }
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ dateBuilder = new DateBuilder(new Date());
+ } else if (position != null) {
+ switch (key) {
+ case 0x11:
+ value = ("000000" + value).substring(value.length());
+ dateBuilder.setDateReverse(
+ Integer.parseInt(value.substring(0, 2)),
+ Integer.parseInt(value.substring(2, 4)),
+ Integer.parseInt(value.substring(4)));
+ break;
+ case 0x10:
+ value = ("00000000" + value).substring(value.length());
+ dateBuilder.setTime(
+ Integer.parseInt(value.substring(0, 2)),
+ Integer.parseInt(value.substring(2, 4)),
+ Integer.parseInt(value.substring(4, 6)),
+ Integer.parseInt(value.substring(6)) * 10);
+ break;
+ case 0xA:
+ position.setValid(true);
+ position.setLatitude(Double.parseDouble(value));
+ break;
+ case 0xB:
+ position.setValid(true);
+ position.setLongitude(Double.parseDouble(value));
+ break;
+ case 0xC:
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case 0xD:
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(value)));
+ break;
+ case 0xE:
+ position.setCourse(Integer.parseInt(value));
+ break;
+ case 0xF:
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
+ break;
+ case 0x12:
+ position.set(Position.KEY_HDOP, Integer.parseInt(value));
+ break;
+ case 0x20:
+ position.set(Position.KEY_ACCELERATION, value);
+ break;
+ case 0x24:
+ position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.01);
+ break;
+ case 0x81:
+ position.set(Position.KEY_RSSI, Integer.parseInt(value));
+ break;
+ case 0x82:
+ position.set(Position.KEY_DEVICE_TEMP, Integer.parseInt(value) * 0.1);
+ break;
+ case 0x104:
+ position.set(Position.KEY_ENGINE_LOAD, Integer.parseInt(value));
+ break;
+ case 0x105:
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(value));
+ break;
+ case 0x10c:
+ position.set(Position.KEY_RPM, Integer.parseInt(value));
+ break;
+ case 0x10d:
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(value)));
+ break;
+ case 0x111:
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(value));
+ break;
+ default:
+ position.set(Position.PREFIX_IO + key, value);
+ break;
+ }
}
}
if (position != null) {
+ if (!position.getValid()) {
+ getLastLocation(position, null);
+ }
position.setTime(dateBuilder.getDate());
positions.add(position);
}
- return positions;
+ return positions.isEmpty() ? null : positions;
}
@Override
@@ -187,12 +197,13 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder {
int endIndex = sentence.indexOf('*');
if (startIndex > 0 && endIndex > 0) {
+ String id = sentence.substring(0, startIndex);
sentence = sentence.substring(startIndex + 1, endIndex);
if (sentence.startsWith("EV")) {
return decodeEvent(channel, remoteAddress, sentence);
} else {
- return decodePosition(channel, remoteAddress, sentence);
+ return decodePosition(channel, remoteAddress, sentence, id);
}
}
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index d438aa33d..683ba476e 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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.
@@ -75,7 +75,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression("(?:[0-9Ff]{20})?,") // iccid
.number("(d{1,2}),") // rssi
.number("d{1,2},")
- .expression("[01],") // external power
+ .expression("[01]{1,2},") // external power
.number("([d.]+)?,") // odometer or external power
.number("d*,") // backup battery or lightness
.number("(d+.d+),") // battery
@@ -97,6 +97,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(xx)?,") // digital output
.number("[-+]dddd,") // timezone
.expression("[01],") // daylight saving
+ .or()
+ .any()
.groupEnd()
.number("(dddd)(dd)(dd)") // date (yyyymmdd)
.number("(dd)(dd)(dd),") // time (hhmmss)
@@ -237,8 +239,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
.number("(x+)?,").optional() // adc 2
+ .groupBegin()
+ .number("(x+)?,") // adc 3
+ .number("(xx),") // inputs
+ .number("(xx),") // outputs
+ .or()
.number("(d{1,3})?,") // battery
.number("(?:(xx)(xx)(xx))?,") // device status
+ .groupEnd()
.expression("(.*)") // additional data
.or()
.number("d*,,")
@@ -920,15 +928,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, power * 0.001);
}
- if (parser.hasNext(9)) {
+ if (parser.hasNext(12)) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
position.set(Position.KEY_HOURS, parseHours(parser.next()));
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- decodeStatus(position, parser);
+ position.set(Position.PREFIX_ADC + 3, parser.next());
+ if (parser.hasNext(2)) {
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ }
+ if (parser.hasNext(4)) {
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ decodeStatus(position, parser);
+ }
int index = 0;
String[] data = parser.next().split(",");
@@ -1147,10 +1161,6 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
decodeDeviceTime(position, parser);
- if (channel != null && Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
- channel.writeAndFlush(new NetworkMessage("+SACK:" + parser.next() + "$", remoteAddress));
- }
-
return position;
}
@@ -1315,6 +1325,16 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
}
+ if (channel != null && Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
+ String checksum;
+ if (sentence.endsWith("$")) {
+ checksum = sentence.substring(sentence.length() - 1 - 4, sentence.length() - 1);
+ } else {
+ checksum = sentence.substring(sentence.length() - 4);
+ }
+ channel.writeAndFlush(new NetworkMessage("+SACK:" + checksum + "$", remoteAddress));
+ }
+
return result;
}
diff --git a/src/main/java/org/traccar/protocol/Gs100Protocol.java b/src/main/java/org/traccar/protocol/Gs100Protocol.java
new file mode 100644
index 000000000..a701815d0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gs100Protocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Gs100Protocol extends BaseProtocol {
+
+ public Gs100Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Gs100ProtocolDecoder(Gs100Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java
new file mode 100644
index 000000000..2496aad48
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2021 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.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Gs100ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Gs100ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ String header = buf.readCharSequence(2, StandardCharsets.US_ASCII).toString();
+
+ if (header.equals("GL")) {
+
+ buf.skipBytes(1);
+ String imei = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+
+ if (channel != null && deviceSession != null) {
+ ByteBuf response = Unpooled.copiedBuffer("GS100", StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return null;
+
+ } else {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+
+ int endIndex = buf.readUnsignedByte() + buf.readerIndex();
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int status = buf.readUnsignedMedium();
+ position.set(Position.KEY_STATUS, status);
+
+ if (BitUtil.check(status, 8 + 8 + 7)) {
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2))
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setYear(BcdUtil.readInteger(buf, 2));
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(true);
+
+ String coordinates = ByteBufUtil.hexDump(buf.readSlice(9));
+ position.setLongitude(Integer.parseInt(coordinates.substring(0, 3))
+ + Integer.parseInt(coordinates.substring(3, 9)) * 0.0001 / 60);
+ position.setLatitude(Integer.parseInt(coordinates.substring(10, 12))
+ + Integer.parseInt(coordinates.substring(12, 18)) * 0.0001 / 60);
+ int flags = Integer.parseInt(coordinates.substring(9, 10), 16);
+ if (!BitUtil.check(flags, 3)) {
+ position.setLongitude(-position.getLongitude());
+ }
+ if (!BitUtil.check(flags, 2)) {
+ position.setLatitude(-position.getLatitude());
+ }
+
+ String other = ByteBufUtil.hexDump(buf.readSlice(4));
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(other.substring(0, 5)) * 0.01));
+ position.setCourse(Integer.parseInt(other.substring(5, 8)));
+
+ } else {
+
+ getLastLocation(position, null);
+
+ }
+
+ positions.add(position);
+
+ buf.readerIndex(endIndex);
+
+ }
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence("GS100", StandardCharsets.US_ASCII);
+ response.writeByte(count);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return positions.isEmpty() ? null : positions;
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index c5e8809e3..6d49be0ce 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -295,7 +295,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return true;
}
- private boolean decodeStatus(Position position, ByteBuf buf) {
+ private boolean decodeStatus(Position position, ByteBuf buf, boolean batteryLevel) {
int status = buf.readUnsignedByte();
@@ -324,7 +324,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
break;
}
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6);
+ if (batteryLevel) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6);
+ } else {
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ }
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
@@ -838,7 +842,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // satellites
buf.readUnsignedByte(); // alarm
buf.readUnsignedByte(); // language
- buf.readUnsignedByte(); // battery
+
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+
buf.readUnsignedByte(); // working mode
buf.readUnsignedShort(); // working voltage
buf.readUnsignedByte(); // reserved
@@ -864,7 +870,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
if (hasStatus(type)) {
- decodeStatus(position, buf);
+ decodeStatus(position, buf, true);
}
if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
@@ -875,6 +881,17 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set("driverLicense", data.trim());
}
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 18) {
+ decodeStatus(position, buf, false);
+ position.set("oil", buf.readUnsignedShort());
+ int temperature = buf.readUnsignedByte();
+ if (BitUtil.check(temperature, 7)) {
+ temperature = -BitUtil.to(temperature, 7);
+ }
+ position.set(Position.PREFIX_TEMP + 1, temperature);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10);
+ }
+
if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
int mask = buf.readUnsignedShort();
position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 3ad70bdca..2e1ddf5f2 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 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.
@@ -234,8 +234,14 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
int length = buf.readUnsignedShort() - 4;
switch (subtype) {
case 0x0001:
- position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
- position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ int coolantTemperature = buf.readUnsignedByte() - 40;
+ if (coolantTemperature <= 215) {
+ position.set(Position.KEY_COOLANT_TEMP, coolantTemperature);
+ }
+ int rpm = buf.readUnsignedShort();
+ if (rpm <= 65535) {
+ position.set(Position.KEY_RPM, rpm);
+ }
position.set("averageSpeed", buf.readUnsignedByte());
buf.readUnsignedShort(); // interval fuel consumption
position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01);
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index a3d16cb62..675a08aef 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -45,6 +45,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GENERAL_RESPONSE = 0x8001;
public static final int MSG_GENERAL_RESPONSE_2 = 0x4401;
+ public static final int MSG_HEARTBEAT = 0x0002;
public static final int MSG_TERMINAL_REGISTER = 0x0100;
public static final int MSG_TERMINAL_REGISTER_RESPONSE = 0x8100;
public static final int MSG_TERMINAL_CONTROL = 0x8105;
@@ -171,7 +172,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress));
}
- } else if (type == MSG_TERMINAL_AUTH) {
+ } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT) {
sendGeneralResponse(channel, remoteAddress, id, type, index);
@@ -334,24 +335,36 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set("cover", BitUtil.check(deviceStatus, 3));
break;
case 0xEB:
- while (buf.readerIndex() < endIndex) {
- int extendedLength = buf.readUnsignedShort();
- int extendedType = buf.readUnsignedShort();
- switch (extendedType) {
- case 0x0001:
- position.set("fuel1", buf.readUnsignedShort() * 0.1);
- buf.readUnsignedByte(); // unused
- break;
- case 0x0023:
- position.set("fuel2", Double.parseDouble(
- buf.readCharSequence(6, StandardCharsets.US_ASCII).toString()));
- break;
- case 0x00CE:
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
- break;
- default:
- buf.skipBytes(extendedLength - 2);
- break;
+ if (buf.getUnsignedShort(buf.readerIndex()) > 200) {
+ Network network = new Network();
+ int mcc = buf.readUnsignedShort();
+ int mnc = buf.readUnsignedByte();
+ while (buf.readerIndex() < endIndex) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedByte()));
+ }
+ position.setNetwork(network);
+ } else {
+ while (buf.readerIndex() < endIndex) {
+ int extendedLength = buf.readUnsignedShort();
+ int extendedType = buf.readUnsignedShort();
+ switch (extendedType) {
+ case 0x0001:
+ position.set("fuel1", buf.readUnsignedShort() * 0.1);
+ buf.readUnsignedByte(); // unused
+ break;
+ case 0x0023:
+ position.set("fuel2", Double.parseDouble(
+ buf.readCharSequence(6, StandardCharsets.US_ASCII).toString()));
+ break;
+ case 0x00CE:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ break;
+ default:
+ buf.skipBytes(extendedLength - 2);
+ break;
+ }
}
}
break;
diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
index bbb63fb65..9c94ffd4b 100644
--- a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
@@ -84,6 +84,156 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void decodeSensor(Position position, ByteBuf record, int sensorType, int sensorId) {
+ String key;
+ switch (sensorId) {
+ case 0x0002:
+ position.set(Position.KEY_MOTION, sensorType > 0);
+ break;
+ case 0x0008:
+ case 0x009B:
+ if (sensorType > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ }
+ break;
+ case 0x0010:
+ case 0x0011:
+ case 0x0012:
+ case 0x0013:
+ case 0x0014:
+ case 0x0015:
+ key = Position.PREFIX_IN + (sensorId - 0x0010 + 2);
+ position.set(key, sensorType > 0);
+ break;
+ case 0x0062:
+ position.set("doorFL", sensorType > 0);
+ break;
+ case 0x0063:
+ position.set("doorFR", sensorType > 0);
+ break;
+ case 0x0064:
+ position.set("doorRL", sensorType > 0);
+ break;
+ case 0x0065:
+ position.set("doorRR", sensorType > 0);
+ break;
+ case 0x001E:
+ position.set("buttonPresent", sensorType > 0);
+ break;
+ case 0x006D:
+ position.set(Position.KEY_IGNITION, sensorType > 0);
+ break;
+ case 0x008B:
+ position.set("handBrake", sensorType > 0);
+ break;
+ case 0x008C:
+ position.set("footBrake", sensorType > 0);
+ break;
+ case 0x0094:
+ case 0x0095:
+ case 0x0096:
+ key = Position.PREFIX_OUT + (sensorId - 0x0094 + 1);
+ position.set(key, sensorType > 0);
+ break;
+ case 0x009A:
+ position.set(Position.PREFIX_OUT + 4, sensorType > 0);
+ break;
+ case 0x2000:
+ position.set(Position.KEY_OBD_SPEED, record.readUnsignedByte());
+ break;
+ case 0x2001:
+ position.set(Position.KEY_SATELLITES, record.readUnsignedByte());
+ break;
+ case 0x2006:
+ position.set(Position.KEY_THROTTLE, record.readUnsignedByte());
+ break;
+ case 0x2007:
+ position.set(Position.KEY_FUEL_LEVEL, record.readUnsignedByte());
+ break;
+ case 0x2008:
+ position.set(Position.KEY_COOLANT_TEMP, record.readUnsignedByte());
+ break;
+ case 0x2009:
+ position.set("fuel2", record.readUnsignedByte());
+ break;
+ case 0x200A:
+ position.set(Position.KEY_ENGINE_LOAD, record.readUnsignedByte());
+ break;
+ case 0x2041:
+ position.set(Position.KEY_BATTERY_LEVEL, record.readUnsignedByte());
+ break;
+ case 0x3000:
+ position.set(Position.KEY_POWER, record.readUnsignedShortLE() * 0.001);
+ break;
+ case 0x3001:
+ case 0x3002:
+ case 0x3003:
+ key = Position.PREFIX_ADC + (0x3003 - sensorId + 3);
+ position.set(key, record.readUnsignedShortLE() * 0.001);
+ break;
+ case 0x3004:
+ position.set(Position.KEY_BATTERY, record.readUnsignedShortLE() * 0.001);
+ break;
+ case 0x300C:
+ position.set(Position.KEY_RPM, record.readUnsignedShortLE());
+ break;
+ case 0x3021:
+ position.set(Position.KEY_FUEL_CONSUMPTION, record.readUnsignedShortLE() * 0.05);
+ break;
+ case 0x3037:
+ position.set("cargoWeight", record.readUnsignedShortLE() * 2);
+ break;
+ case 0x4001:
+ position.set(Position.KEY_FUEL_USED, record.readUnsignedIntLE());
+ break;
+ case 0x4002:
+ position.set(Position.KEY_HOURS, record.readUnsignedIntLE());
+ break;
+ case 0x4003:
+ position.set(Position.KEY_ODOMETER, record.readUnsignedIntLE() * 5);
+ break;
+ case 0x4063:
+ position.set(Position.KEY_AXLE_WEIGHT, record.readUnsignedIntLE());
+ break;
+ case 0x5000:
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(record.readLongLE()));
+ break;
+ case 0x5004:
+ case 0x5005:
+ case 0x5006:
+ case 0x5007:
+ key = Position.PREFIX_TEMP + (sensorId - 0x5004 + 1);
+ position.set(key, record.readLongLE());
+ break;
+ case 0x500D:
+ position.set("trailerId", String.valueOf(record.readLongLE()));
+ break;
+ case 0xA000:
+ position.set(Position.KEY_DEVICE_TEMP, record.readFloatLE());
+ break;
+ case 0xA001:
+ position.set(Position.KEY_ACCELERATION, record.readFloatLE());
+ break;
+ case 0xA002:
+ position.set("cornering", record.readFloatLE());
+ break;
+ case 0xA017:
+ case 0xA018:
+ case 0xA019:
+ case 0xA01A:
+ key = Position.PREFIX_TEMP + (sensorId - 0xA017 + 1);
+ position.set(key, record.readFloatLE());
+ break;
+ case 0xB002:
+ position.set(Position.KEY_OBD_ODOMETER, record.readDoubleLE());
+ break;
+ default:
+ key = Position.PREFIX_IO + sensorId;
+ position.getAttributes().put(key, readValue(record, sensorType));
+ break;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -163,62 +313,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
continue;
}
- String key;
- switch (sensorId) {
- case 0x0008:
- if (sensorType > 0) {
- position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
- }
- break;
- case 0x0010:
- case 0x0011:
- case 0x0012:
- case 0x0013:
- key = Position.PREFIX_IN + (sensorId - 0x0010 + 2);
- position.set(key, sensorType > 0);
- break;
- case 0x001E:
- position.set("buttonPresent", sensorType > 0);
- break;
- case 0x006D:
- position.set(Position.KEY_IGNITION, sensorType > 0);
- break;
- case 0x3000:
- position.set(Position.KEY_POWER, record.readUnsignedShortLE() * 0.001);
- break;
- case 0x3001:
- case 0x3002:
- case 0x3003:
- key = Position.PREFIX_ADC + (0x3003 - sensorId + 3);
- position.set(key, record.readUnsignedShortLE() * 0.001);
- break;
- case 0x3004:
- position.set(Position.KEY_BATTERY, record.readUnsignedShortLE() * 0.001);
- break;
- case 0x300C:
- position.set(Position.KEY_RPM, record.readUnsignedShortLE());
- break;
- case 0x4003:
- position.set(Position.KEY_ODOMETER, record.readUnsignedIntLE() * 5);
- break;
- case 0x5000:
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(record.readLongLE()));
- break;
- case 0xA001:
- position.set(Position.KEY_ACCELERATION, record.readFloatLE());
- break;
- case 0xA002:
- position.set("cornering", record.readFloatLE());
- break;
- case 0xA017:
- key = Position.PREFIX_TEMP + (sensorId - 0xA017 + 1);
- position.set(key, record.readFloatLE());
- break;
- default:
- key = Position.PREFIX_IO + sensorId;
- position.getAttributes().put(key, readValue(record, sensorType));
- break;
- }
+ decodeSensor(position, record, sensorType, sensorId);
}
}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index 94c9a3038..9eed58347 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2021 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.
@@ -28,6 +28,10 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
import java.util.regex.Pattern;
public class ItsProtocolDecoder extends BaseProtocolDecoder {
@@ -75,7 +79,7 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // power
.number("(d+.?d*),") // battery
.number("([01]),") // emergency
- .expression("[CO]?,") // tamper
+ .expression("[COYN]?,") // tamper
.expression("(.*),") // cells
.number("([012]{4}),") // inputs
.number("([01]{2}),") // outputs
@@ -96,6 +100,12 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("d+,") // index
.number("(d+.?d*),") // adc1
.number("(d+.?d*),") // adc2
+ .or()
+ .number("(d+.d+),") // adc1
+ .number("(d+),") // odometer
+ .number("(d{6}),") // index
+ .expression("([^,]+),") // response format
+ .expression("([^,]+),") // response
.groupEnd("?")
.groupEnd("?")
.or()
@@ -137,8 +147,17 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
- if (channel != null && sentence.startsWith("$,01,")) {
- channel.writeAndFlush(new NetworkMessage("$,1,*", remoteAddress));
+ if (channel != null) {
+ if (sentence.startsWith("$,01,")) {
+ channel.writeAndFlush(new NetworkMessage("$,1,*", remoteAddress));
+ } else if (sentence.startsWith("$,LGN,")) {
+ DateFormat dateFormat = new SimpleDateFormat("ddMMyyyyHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ String time = dateFormat.format(new Date());
+ channel.writeAndFlush(new NetworkMessage("$LGN" + time + "*", remoteAddress));
+ } else if (sentence.startsWith("$,HBT,")) {
+ channel.writeAndFlush(new NetworkMessage("$HBT*", remoteAddress));
+ }
}
Parser parser = new Parser(PATTERN, sentence);
@@ -257,6 +276,14 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
}
+ if (parser.hasNext(5)) {
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set("responseFormat", parser.next());
+ position.set("response", parser.next());
+ }
+
if (parser.hasNext(2)) {
position.setAltitude(parser.nextDouble());
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
diff --git a/src/main/java/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java
index f77f4c311..60a2aea7f 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocol.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -26,7 +26,13 @@ public class KhdProtocol extends BaseProtocol {
public KhdProtocol() {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
- Command.TYPE_ENGINE_RESUME);
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_FACTORY_RESET,
+ Command.TYPE_SET_SPEED_LIMIT,
+ Command.TYPE_SET_ODOMETER,
+ Command.TYPE_POSITION_SINGLE);
+
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index 16ad616ed..251351a74 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -24,6 +24,7 @@ import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -65,6 +66,44 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_SMS_ALARM_SWITCH = 0x86;
public static final int MSG_PERIPHERAL = 0xA3;
+ private void decodeAlarmStatus(Position position, byte[] status) {
+ if (BitUtil.check(status[0], 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ } else if (BitUtil.check(status[0], 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT);
+ } else if (BitUtil.check(status[0], 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER);
+ } else if (BitUtil.check(status[1], 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ } else if (BitUtil.check(status[1], 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ } else if (BitUtil.check(status[1], 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ } else if (BitUtil.check(status[1], 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ } else if (BitUtil.check(status[1], 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_DOOR);
+ } else if (BitUtil.check(status[2], 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
+ } else if (BitUtil.check(status[2], 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ } else if (BitUtil.check(status[2], 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING);
+ } else if (BitUtil.check(status[2], 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_IDLE);
+ } else if (BitUtil.check(status[6], 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ } else if (BitUtil.check(status[6], 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ } else if (BitUtil.check(status[6], 5)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ } else if (BitUtil.check(status[6], 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ } else if (BitUtil.check(status[6], 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -125,11 +164,15 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- position.set(Position.KEY_HDOP, buf.readUnsignedByte());
- position.set(Position.KEY_VDOP, buf.readUnsignedByte());
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- buf.skipBytes(5); // other location data
+ buf.readUnsignedShort();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte();
+
+ position.set(Position.KEY_RESULT, buf.readUnsignedByte());
if (type == MSG_PERIPHERAL) {
@@ -174,6 +217,16 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
}
+ } else {
+
+ buf.readUnsignedByte(); // overloaded state
+ buf.readUnsignedByte(); // logging status
+
+ byte[] alarmStatus = new byte[8];
+ buf.readBytes(alarmStatus);
+
+ decodeAlarmStatus(position, alarmStatus);
+
}
return position;
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
index 4a8df26c8..8aeb9660d 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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,14 +24,19 @@ import org.traccar.Protocol;
public class KhdProtocolEncoder extends BaseProtocolEncoder {
+ public static final int MSG_ON_DEMAND_TRACK = 0x30;
public static final int MSG_CUT_OIL = 0x39;
public static final int MSG_RESUME_OIL = 0x38;
+ public static final int MSG_CHECK_VERSION = 0x3D;
+ public static final int MSG_FACTORY_RESET = 0xC3;
+ public static final int MSG_SET_OVERSPEED = 0x3F;
+ public static final int MSG_DELETE_MILEAGE = 0x66;
public KhdProtocolEncoder(Protocol protocol) {
super(protocol);
}
- private ByteBuf encodeCommand(int command, String uniqueId) {
+ private ByteBuf encodeCommand(int command, String uniqueId, ByteBuf content) {
ByteBuf buf = Unpooled.buffer();
@@ -39,7 +44,12 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
buf.writeByte(0x29);
buf.writeByte(command);
- buf.writeShort(6); // size
+
+ int length = 6;
+ if (content != null) {
+ length += content.readableBytes();
+ }
+ buf.writeShort(length);
uniqueId = "00000000".concat(uniqueId);
uniqueId = uniqueId.substring(uniqueId.length() - 8);
@@ -48,6 +58,10 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
buf.writeByte(Integer.parseInt(uniqueId.substring(4, 6)) + 0x80);
buf.writeByte(Integer.parseInt(uniqueId.substring(6, 8)));
+ if (content != null) {
+ buf.writeBytes(content);
+ }
+
buf.writeByte(Checksum.xor(buf.nioBuffer()));
buf.writeByte(0x0D); // ending
@@ -61,9 +75,21 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeCommand(MSG_CUT_OIL, uniqueId);
+ return encodeCommand(MSG_CUT_OIL, uniqueId, null);
case Command.TYPE_ENGINE_RESUME:
- return encodeCommand(MSG_RESUME_OIL, uniqueId);
+ return encodeCommand(MSG_RESUME_OIL, uniqueId, null);
+ case Command.TYPE_GET_VERSION:
+ return encodeCommand(MSG_CHECK_VERSION, uniqueId, null);
+ case Command.TYPE_FACTORY_RESET:
+ return encodeCommand(MSG_FACTORY_RESET, uniqueId, null);
+ case Command.TYPE_SET_SPEED_LIMIT:
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(Integer.parseInt(command.getString(Command.KEY_DATA)));
+ return encodeCommand(MSG_RESUME_OIL, uniqueId, content);
+ case Command.TYPE_SET_ODOMETER:
+ return encodeCommand(MSG_DELETE_MILEAGE, uniqueId, null);
+ case Command.TYPE_POSITION_SINGLE:
+ return encodeCommand(MSG_ON_DEMAND_TRACK, uniqueId, null);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
new file mode 100644
index 000000000..d779648e4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 Chipeng Li (chplee@gmail.com)
+ *
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+public class Mavlink2Protocol extends BaseProtocol {
+
+ public Mavlink2Protocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 1, 10, 0));
+ pipeline.addLast(new Mavlink2ProtocolDecoder(Mavlink2Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java
new file mode 100644
index 000000000..431258388
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 Chipeng Li (chplee@gmail.com)
+ *
+ * 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.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class Mavlink2ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Mavlink2ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ if (buf.readUnsignedByte() != 0xFD) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // length
+ buf.readUnsignedByte(); // incompatibility flags
+ buf.readUnsignedByte(); // compatibility flags
+ buf.readUnsignedByte(); // index
+
+ int senderSystemId = buf.readUnsignedByte();
+ buf.readUnsignedByte(); // component id
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, Integer.toString(senderSystemId));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = buf.readUnsignedMediumLE();
+ if (type == 33) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set("timeBoot", buf.readUnsignedIntLE()); // time since system boot
+
+ position.setValid(true);
+ position.setTime(new Date());
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setAltitude(buf.readIntLE() / 1000.0);
+ position.set("relativeAltitude", buf.readIntLE() / 1000.0);
+
+ int groundSpeedX = buf.readShortLE();
+ int groundSpeedY = buf.readShortLE();
+ buf.readShortLE(); // ground speed z
+ double speed = Math.sqrt(Math.pow(groundSpeedX, 2) + Math.pow(groundSpeedY, 2));
+ position.setSpeed(UnitsConverter.knotsFromCps(speed));
+
+ position.setCourse(buf.readUnsignedShortLE() / 100.0);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
index d81cc0eda..fae73d931 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -275,9 +275,10 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.or().text(" ")
.groupEnd("?").text(",")
.number("(d+)?,") // rfid
+ .number("([01])(d)?").optional() // charge and belt status
.expression("[^,]*,")
.number("(d+)?,") // battery
- .expression("([^,]*)") // alert
+ .expression("([^,]*)[,;]") // alert
.any()
.compile();
@@ -355,6 +356,13 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
+ }
+ if (parser.hasNext()) {
+ position.set("belt", parser.nextInt());
+ }
+
String battery = parser.next();
if (battery != null) {
position.set(Position.KEY_BATTERY, Integer.parseInt(battery));
@@ -375,10 +383,11 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
}
switch (value) {
+ case "pw on":
case "poweron":
return Position.ALARM_POWER_ON;
case "poweroff":
- return Position.ALARM_POWER_ON;
+ return Position.ALARM_POWER_OFF;
case "sos":
case "help":
return Position.ALARM_SOS;
@@ -390,12 +399,32 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
case "low battery":
case "lowbattery":
return Position.ALARM_LOW_BATTERY;
+ case "low extern voltage":
+ return Position.ALARM_LOW_POWER;
+ case "gps cut":
+ return Position.ALARM_GPS_ANTENNA_CUT;
case "vib":
return Position.ALARM_VIBRATION;
case "move in":
return Position.ALARM_GEOFENCE_ENTER;
case "move out":
return Position.ALARM_GEOFENCE_EXIT;
+ case "corner":
+ return Position.ALARM_CORNERING;
+ case "fatigue":
+ return Position.ALARM_FATIGUE_DRIVING;
+ case "psd":
+ return Position.ALARM_POWER_CUT;
+ case "psr":
+ return Position.ALARM_POWER_RESTORED;
+ case "hit":
+ return Position.ALARM_SHOCK;
+ case "belt on":
+ case "belton":
+ return Position.ALARM_LOCK;
+ case "belt off":
+ case "beltoff":
+ return Position.ALARM_UNLOCK;
case "error":
return Position.ALARM_FAULT;
default:
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
index 5ea9f148c..992b5c43a 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
* Copyright 2020 Roeland Boeters (roeland@geodelta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -168,8 +168,48 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
if (sentence.startsWith("MT")) {
return decodeStandard(channel, remoteAddress, sentence);
- } else {
+ } else if (sentence.contains("$")) {
return decodeLowAltitude(channel, remoteAddress, sentence);
+ } else {
+ return decodeResult(channel, remoteAddress, sentence);
+ }
+ }
+
+ private Object decodeResult(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ if (sentence.matches("\\d{15} .+")) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(0, 15));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RESULT, sentence.substring(16, sentence.length() - 1));
+
+ return position;
+
+ } else {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RESULT, sentence.substring(0, sentence.length() - 1));
+
+ return position;
+
}
}
@@ -184,6 +224,7 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, Integer.parseInt(fragments[1]));
switch (fragments[3]) {
case "R0":
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index 89217a39b..68e9e8dd5 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -189,13 +189,17 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
case 0x28:
int beaconFlags = buf.readUnsignedByte();
position.set("tagId", ByteBufUtil.hexDump(buf.readSlice(6)));
- buf.readUnsignedByte(); // rssi
+ position.set("tagRssi", buf.readUnsignedByte());
buf.readUnsignedByte(); // 1m rssi
if (BitUtil.check(beaconFlags, 7)) {
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
hasLocation = true;
}
+ if (BitUtil.check(beaconFlags, 6)) {
+ position.set("description", buf.readCharSequence(
+ endIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString());
+ }
break;
case 0x30:
buf.readUnsignedInt(); // timestamp
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
new file mode 100644
index 000000000..c76b42768
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 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;
+
+public class NavtelecomProtocol extends BaseProtocol {
+
+ public NavtelecomProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0));
+ pipeline.addLast(new Gt02ProtocolDecoder(NavtelecomProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
new file mode 100644
index 000000000..2362b1870
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 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.Checksum;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
+
+ public NavtelecomProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(4); // preamble
+ int receiver = buf.readIntLE();
+ int sender = buf.readIntLE();
+ int length = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // data checksum
+ buf.readUnsignedByte(); // header checksum
+
+ String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+
+ if (sentence.startsWith("*>S")) {
+
+ String data = "*<S";
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence("@NTC", StandardCharsets.US_ASCII);
+ response.writeIntLE(sender);
+ response.writeIntLE(receiver);
+ response.writeShortLE(data.length());
+ response.writeByte(Checksum.xor(data));
+ response.writeByte(Checksum.xor(response.nioBuffer()));
+ response.writeCharSequence(data, StandardCharsets.US_ASCII);
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
index ea0966a5d..c9044fa2b 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 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.
@@ -83,6 +83,11 @@ public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speedAndCourse, 12) * 0.1));
position.set(Position.KEY_INDEX, buf.readUnsignedShort());
break;
+ case 0x20:
+ int voltage = buf.readUnsignedMedium();
+ position.set(Position.KEY_BATTERY, BitUtil.between(voltage, 0, 12) * 0.01);
+ position.set(Position.KEY_POWER, BitUtil.between(voltage, 12, 24) * 0.01);
+ break;
case 0x92:
while (buf.readerIndex() < segmentEnd) {
int field = buf.readUnsignedByte();
@@ -106,6 +111,24 @@ public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
case 0b00001:
position.set(Position.KEY_RPM, buf.readUnsignedByte() * 32);
break;
+ case 0b00011:
+ position.set("oilPressure", buf.readUnsignedByte() * 4);
+ break;
+ case 0b00100:
+ position.set("oilLevel", buf.readUnsignedByte() * 0.4);
+ break;
+ case 0b00101:
+ position.set("oilTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0b00110:
+ position.set("coolantLevel", buf.readUnsignedByte() * 0.4);
+ break;
+ case 0b00111:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ break;
+ case 0b01000:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
+ break;
case 0b01001:
position.set("defLevel", buf.readUnsignedByte() * 0.4);
break;
diff --git a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
index 949c994ee..65de211ac 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 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.
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
@@ -75,6 +76,10 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("$$hb,1#", remoteAddress));
+ }
+
Parser parser = new Parser(PATTERN, (String) msg);
if (!parser.matches()) {
return null;
@@ -98,7 +103,7 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_ODOMETER, (long) (parser.nextDouble() * 1000));
- int status = parser.nextInt();
+ long status = parser.nextLong();
position.setValid(BitUtil.check(status, 0));
position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
for (int i = 0; i < 4; i++) {
diff --git a/src/main/java/org/traccar/protocol/R12wProtocol.java b/src/main/java/org/traccar/protocol/R12wProtocol.java
new file mode 100644
index 000000000..3726233b4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/R12wProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 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.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class R12wProtocol extends BaseProtocol {
+
+ public R12wProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new R12wProtocolDecoder(R12wProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java
new file mode 100644
index 000000000..d60318447
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2021 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+
+import java.net.SocketAddress;
+
+public class R12wProtocolDecoder extends BaseProtocolDecoder {
+
+ public R12wProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, String type, String id, String data) {
+ if (channel != null) {
+ String sentence = String.format("$HX,%s,%s,%s,#", type, id, data);
+ sentence += String.format(",%02x,\r\n", Checksum.xor(sentence));
+ channel.writeAndFlush(new NetworkMessage(sentence, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ String[] values = sentence.split(",");
+ String type = values[1];
+ String id = values[2];
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (type.equals("0001")) {
+ sendResponse(channel, "1001", id, values[3] + ",OK");
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
index 05601ed51..9e3261a04 100644
--- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 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.
@@ -43,6 +43,7 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
.number("(d{9});") // serial number
.number("(d+);") // index
.number("(d+);") // type
+ .groupBegin()
.number("(dd)-(dd)-(dddd) ") // event date
.number("(dd):(dd):(dd);") // event time
.number("(dd)-(dd)-(dddd) ") // fix date
@@ -68,6 +69,7 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
.number("x{4};") // sensors
.number("(xx);") // status 1
.number("(xx);") // status 2
+ .groupEnd("?")
.any()
.compile();
@@ -80,14 +82,14 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- String archive = parser.next();
+ parser.next(); // archive
String model = parser.next();
String firmware = parser.next();
String serial = parser.next();
int index = parser.nextInt();
- parser.nextInt(); // type
+ int type = parser.nextInt();
- if (channel != null && archive.equals("A")) {
+ if (channel != null) {
String response = "RST;A;" + model + ";" + firmware + ";" + serial + ";" + index + ";6;FIM;";
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -97,36 +99,44 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.setDeviceTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
- position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
- position.setLatitude(parser.nextDouble());
- position.setLongitude(parser.nextDouble());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
- position.setCourse(parser.nextInt());
- position.setAltitude(parser.nextInt());
- position.setValid(parser.nextInt() > 0);
-
- position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.KEY_HDOP, parser.nextInt());
- position.set(Position.PREFIX_IN + 1, parser.nextHexInt());
- position.set(Position.PREFIX_IN + 2, parser.nextHexInt());
- position.set(Position.PREFIX_IN + 3, parser.nextHexInt());
- position.set(Position.PREFIX_OUT + 1, parser.nextHexInt());
- position.set(Position.PREFIX_OUT + 2, parser.nextHexInt());
- position.set(Position.KEY_POWER, parser.nextDouble());
- position.set(Position.KEY_BATTERY, parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextInt());
- position.set(Position.KEY_RSSI, parser.nextInt());
- position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue());
-
- int status = (parser.nextHexInt() << 8) + parser.nextHexInt();
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 7));
- position.set(Position.KEY_STATUS, status);
-
- return position;
+ if (parser.hasNext()) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextInt());
+ position.set(Position.PREFIX_IN + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 2, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 3, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 2, parser.nextHexInt());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue());
+
+ int status = (parser.nextHexInt() << 8) + parser.nextHexInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 7));
+ position.set(Position.KEY_STATUS, status);
+
+ return position;
+
+ } else {
+
+ return null;
+
+ }
}
}
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 227a9ac91..b6378f416 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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.
@@ -110,6 +110,16 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
case 80:
position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1);
break;
+ case 198:
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case 199:
+ case 200:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 201:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
diff --git a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
index 6b97f5fe0..bf8bfab77 100644
--- a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2021 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.
@@ -38,12 +38,12 @@ public class SiwiProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // device id
.number("d+,") // unit no
.expression("([A-Z]),") // reason
- .number("d+,") // command code
+ .number("d*,") // command code
.number("[^,]*,") // command value
.expression("([01]),") // ignition
.expression("[01],") // power cut
- .expression("[01],") // box open
- .number("d+,") // message key
+ .number("d+,") // flags
+ .number("[^,]+,")
.number("(d+),") // odometer
.number("(d+),") // speed
.number("(d+),") // satellites
@@ -54,6 +54,19 @@ public class SiwiProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // course
.number("(dd)(dd)(dd),") // time (hhmmss)
.number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("d+,") // signal strength
+ .number("d+,") // gsm status
+ .number("d+,") // error code
+ .number("d+,") // internal status
+ .number("(d+),") // battery
+ .number("(d+),") // adc
+ .number("(d+),") // digital inputs
+ .number("(d+),") // sensor 1
+ .number("(d+),") // sensor 2
+ .number("(d+),") // sensor 3
+ .number("(d+),") // sensor 4
+ .expression("([^,]+),") // hw version
+ .expression("([^,]+),") // sw version
.any()
.compile();
@@ -90,6 +103,20 @@ public class SiwiProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY, "IST"));
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
+ position.set(Position.PREFIX_ADC + 1, parser.nextInt() * 0.01);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+
+ for (int i = 1; i <= 4; i++) {
+ int value = parser.nextInt();
+ if (value != 0) {
+ position.set(Position.PREFIX_IO + i, value);
+ }
+ }
+
+ position.set(Position.KEY_VERSION_HW, parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index 882bed81b..82f0e4061 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2021 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.
@@ -193,6 +193,12 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
case "#TVI#":
position.set(Position.KEY_DEVICE_TEMP, Double.parseDouble(data[i]));
break;
+ case "#CFL#":
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(data[i]));
+ break;
+ case "#CFL2#":
+ position.set("fuel2", Integer.parseInt(data[i]));
+ break;
case "#IN1#":
case "#IN2#":
case "#IN3#":
diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java
new file mode 100644
index 000000000..32f1c5a29
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StartekProtocol.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 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.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class StartekProtocol extends BaseProtocol {
+
+ public StartekProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_OUTPUT_CONTROL,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StartekProtocolEncoder(StartekProtocol.this));
+ pipeline.addLast(new StartekProtocolDecoder(StartekProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
new file mode 100644
index 000000000..1cc69c6e6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2021 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.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class StartekProtocolDecoder extends BaseProtocolDecoder {
+
+ public StartekProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("&&")
+ .expression(".") // index
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .number("xxx,") // command
+ .number("(d+),") // event
+ .expression("([^,]+)?,") // event data
+ .number("(dd)(dd)(dd)") // date (yyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AV]),") // valid
+ .number("(-?d+.d+),") // longitude
+ .number("(-?d+.d+),") // latitude
+ .number("(d+),") // satellites
+ .number("(d+.d+),") // hdop
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(d+),") // odometer
+ .number("(d+)|") // mcc
+ .number("(d+)|") // mnc
+ .number("(x+)|") // lac
+ .number("(x+),") // cid
+ .number("(d+),") // rssi
+ .number("(x+),") // status
+ .number("(x+),") // inputs
+ .number("(x+),") // outputs
+ .number("(x+)|") // power
+ .number("(x+)|") // battery
+ .expression("([^,]+),") // adc
+ .number("d,") // extended
+ .expression("([^,]+)?,") // fuel
+ .expression("([^,]+)?") // temperature
+ .number("xx") // checksum
+ .compile();
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 5:
+ case 6:
+ return Position.ALARM_DOOR;
+ case 39:
+ return Position.ALARM_ACCELERATION;
+ case 40:
+ return Position.ALARM_BRAKING;
+ case 41:
+ return Position.ALARM_CORNERING;
+ 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;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int event = parser.nextInt();
+ String eventData = parser.next();
+ position.set(Position.KEY_EVENT, event);
+ if (event == 53) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, eventData);
+ } else {
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ }
+
+ position.setTime(parser.nextDateTime());
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), parser.nextInt())));
+
+ position.set(Position.KEY_STATUS, parser.nextHexInt());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+
+ position.set(Position.KEY_POWER, parser.nextHexInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 0.01);
+
+ String[] adc = parser.next().split("\\|");
+ for (int i = 0; i < adc.length; i++) {
+ position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16) * 0.01);
+ }
+
+ if (parser.hasNext()) {
+ String[] fuels = parser.next().split("\\|");
+ for (String fuel : fuels) {
+ int index = Integer.parseInt(fuel.substring(0, 2));
+ int value = Integer.parseInt(fuel.substring(2), 16);
+ position.set("fuel" + index, value * 0.1);
+ }
+ }
+
+ if (parser.hasNext()) {
+ String[] temperatures = parser.next().split("\\|");
+ for (String temperature : temperatures) {
+ int index = Integer.parseInt(temperature.substring(0, 2));
+ int value = Integer.parseInt(temperature.substring(2), 16);
+ double convertedValue = BitUtil.to(value, 15);
+ if (BitUtil.check(value, 15)) {
+ convertedValue = -convertedValue;
+ }
+ position.set(Position.PREFIX_TEMP + index, convertedValue * 0.1);
+ }
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolEncoder.java b/src/main/java/org/traccar/protocol/StartekProtocolEncoder.java
new file mode 100644
index 000000000..011a8dfae
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StartekProtocolEncoder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 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.Protocol;
+import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+
+public class StartekProtocolEncoder extends StringProtocolEncoder {
+
+ public StartekProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected String formatCommand(Command command, String format, String... keys) {
+ String uniqueId = getUniqueId(command.getDeviceId());
+ String payload = super.formatCommand(command, format, keys);
+ int length = 1 + uniqueId.length() + 1 + payload.length();
+ String sentence = "$$:" + length + "," + uniqueId + "," + payload;
+ return sentence + Checksum.sum(sentence) + "\r\n";
+ }
+
+ @Override
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "%s", Command.KEY_DATA);
+ case Command.TYPE_OUTPUT_CONTROL:
+ return formatCommand(command, "900,%s,%s", Command.KEY_INDEX, Command.KEY_DATA);
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "900,1,1");
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "900,1,0");
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 76e3e6ecc..d8710a899 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -356,6 +356,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
totalFuel += fuel2;
position.set("fuel2", fuel2);
}
+ } else if (attribute.startsWith("GTSL")) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]);
} else {
String[] pair = attribute.split("=");
if (pair.length >= 2) {
diff --git a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
new file mode 100644
index 000000000..02c111b01
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 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.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageEncoder;
+import org.traccar.Context;
+import org.traccar.Protocol;
+import org.traccar.config.Keys;
+
+import java.util.List;
+
+@ChannelHandler.Sharable
+public class TaipPrefixEncoder extends MessageToMessageEncoder<ByteBuf> {
+
+ private final Protocol protocol;
+
+ public TaipPrefixEncoder(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
+ if (Context.getConfig().getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) {
+ out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain()));
+ } else {
+ out.add(msg.retain());
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java
index b8f40a183..0966cfd7c 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocol.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -29,6 +29,7 @@ public class TaipProtocol extends BaseProtocol {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<'));
+ pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
@@ -37,6 +38,7 @@ public class TaipProtocol extends BaseProtocol {
addServer(new TrackerServer(true, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
index 6f71b6c08..ec0ce1931 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
@@ -313,7 +313,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
if (messageIndex != null) {
String response;
if (messageIndex.startsWith("#IP")) {
- response = "\u0020\u0020\u0006\u0000>SAK;ID=" + uniqueId + ";" + messageIndex + "<";
+ response = ">SAK;ID=" + uniqueId + ";" + messageIndex + "<";
} else {
response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*";
response += String.format("%02X", Checksum.xor(response)) + "<";
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 716589e00..6ba183f9b 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 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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.
@@ -312,6 +312,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
break;
}
break;
+ case 389:
+ if (BitUtil.between(readValue(buf, length, false), 4, 8) == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
@@ -537,7 +542,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
int id = buf.readUnsignedShort();
int length = buf.readUnsignedShort();
if (id == 256) {
- position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_VIN,
+ buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ } else if (id == 281) {
+ position.set(Position.KEY_DTCS,
+ buf.readSlice(length).toString(StandardCharsets.US_ASCII).replace(',', ' '));
} else if (id == 385) {
ByteBuf data = buf.readSlice(length);
data.readUnsignedByte(); // data part
@@ -617,19 +626,17 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (channel != null && codec != CODEC_12 && codec != CODEC_13) {
+ ByteBuf response = Unpooled.buffer();
if (connectionless) {
- ByteBuf response = Unpooled.buffer();
response.writeShort(5);
response.writeShort(0);
response.writeByte(0x01);
response.writeByte(locationPacketId);
response.writeByte(count);
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else {
- ByteBuf response = Unpooled.buffer();
response.writeInt(count);
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
return positions.isEmpty() ? null : positions;
diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
new file mode 100644
index 000000000..ece1b0544
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 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;
+
+public class ThinkPowerProtocol extends BaseProtocol {
+
+ public ThinkPowerProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, 2, 0));
+ pipeline.addLast(new ThinkPowerProtocolDecoder(ThinkPowerProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java
new file mode 100644
index 000000000..b3f943078
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2021 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class ThinkPowerProtocolDecoder extends BaseProtocolDecoder {
+
+ public ThinkPowerProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN_REQUEST = 0x01;
+ public static final int MSG_LOGIN_RESPONSE = 0x02;
+ public static final int MSG_HEARTBEAT_REQUEST = 0x03;
+ public static final int MSG_HEARTBEAT_RESPONSE = 0x04;
+ public static final int MSG_RECORD_REPORT = 0x05;
+ public static final int MSG_RECORD_RESPONSE = 0x06;
+
+ private void sendResponse(Channel channel, int type, int index, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(type);
+ response.writeByte(index);
+ if (content != null) {
+ response.writeShort(content.readableBytes());
+ response.writeBytes(content);
+ content.release();
+ } else {
+ response.writeShort(0);
+ }
+ response.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, response.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private void decodeValue(Position position, int type, ByteBuf buf) {
+ switch (type) {
+ case 0x01:
+ position.setValid(true);
+ position.setLatitude(buf.readInt() * 0.0000001);
+ position.setLongitude(buf.readInt() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setCourse(buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x02:
+ position.setValid(buf.readUnsignedByte() > 0);
+ break;
+ case 0x03:
+ buf.skipBytes(3); // geofence
+ break;
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ buf.skipBytes(2); // g-sensor x/y/z
+ break;
+ case 0x09:
+ buf.readUnsignedByte(); // collision alarm
+ break;
+ case 0x0A:
+ buf.readUnsignedByte(); // drop alarm
+ break;
+ case 0x10:
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ break;
+ case 0x12:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x13:
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ }
+ break;
+ case 0x16:
+ buf.readUnsignedShort(); // temperature
+ break;
+ case 0x17:
+ buf.readUnsignedByte(); // humidity
+ break;
+ case 0x18:
+ buf.readUnsignedShort(); // high temperature
+ break;
+ case 0x19:
+ buf.readUnsignedByte(); // high humidity
+ break;
+ case 0x50:
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ }
+ break;
+ case 0x51:
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int type = buf.readUnsignedByte();
+ int index = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+
+ if (type == MSG_LOGIN_REQUEST) {
+
+ buf.readUnsignedByte(); // protocol major version
+ buf.readUnsignedByte(); // protocol minor version
+
+ String id = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(deviceSession != null ? 0 : 4);
+ sendResponse(channel, MSG_LOGIN_RESPONSE, index, content);
+
+ } else if (type == MSG_HEARTBEAT_REQUEST) {
+
+ sendResponse(channel, MSG_HEARTBEAT_RESPONSE, index, null);
+
+ } else if (type == MSG_RECORD_REPORT) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // count
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(buf.readUnsignedInt() * 1000));
+
+ while (buf.readableBytes() > 2) {
+ decodeValue(position, buf.readUnsignedByte(), buf);
+ }
+
+ sendResponse(channel, MSG_RECORD_RESPONSE, index, null);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index 5dad1458d..ff33cb103 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -60,9 +60,9 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.groupEnd()
.number("(dd)(dd)(dd),?") // date (mmddyy if comma-delimited, otherwise yyddmm)
.expression("([AV]),?") // validity
- .number(" *(d+)(dd.d+)") // latitude
+ .number(" *(d*)(dd.d+)") // latitude
.expression("([NS]),?")
- .number(" *(d+)(dd.d+)") // longitude
+ .number(" *(d*)(dd.d+)") // longitude
.expression("([EW]),?")
.number("([ d.]{1,5})(?:d*,)?") // speed
.number("(dd)(dd)(dd),?") // time (hhmmss)
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index 3ce6438f8..ad7dfa886 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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,7 +22,9 @@ import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.util.LinkedList;
@@ -51,10 +53,11 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_POSITION = new PatternBuilder()
- .number("#(x+)?") // cell info
+ .text("#")
+ .number("(?:(dd)|x*)") // cell or voltage
.text("$GPRMC,")
.number("(dd)(dd)(dd).d+,") // time (hhmmss.sss)
- .expression("([AV]),") // validity
+ .expression("([AVL]),") // validity
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
.number("(d+)(dd.d+),") // longitude
@@ -65,6 +68,18 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_WIFI = new PatternBuilder()
+ .text("#")
+ .number("(?:(dd)|x+)") // cell or voltage
+ .text("$WIFI,")
+ .number("(dd)(dd)(dd).d+,") // time (hhmmss.sss)
+ .expression("[AVL],") // validity
+ .expression("(.*)") // access points
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .text("*")
+ .number("xx") // checksum
+ .compile();
+
private void decodeStatus(Position position, String status) {
switch (status) {
case "AUTOSTART":
@@ -141,38 +156,74 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
List<Position> positions = new LinkedList<>();
for (String message : messages) {
- parser = new Parser(PATTERN_POSITION, message);
- if (parser.matches()) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (message.contains("$GPRMC")) {
+
+ parser = new Parser(PATTERN_POSITION, message);
+ if (parser.matches()) {
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+ }
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
- parser.next(); // base station info
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ } else {
+ continue;
+ }
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ } else if (message.contains("$WIFI")) {
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
+ parser = new Parser(PATTERN_WIFI, message);
+ if (parser.matches()) {
- position.set(Position.KEY_DOOR, door);
- position.set(Position.PREFIX_ADC + 1, adc);
- position.set(Position.KEY_POWER, power);
- position.set(Position.KEY_BATTERY, battery);
- position.set(Position.PREFIX_TEMP + 1, temperature);
- decodeStatus(position, status);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ String[] values = parser.next().split(",");
+ Network network = new Network();
+ for (int i = 0; i < values.length / 2; i++) {
+ String mac = values[i * 2 + 1].replaceAll("(..)", "$1:").substring(0, 17);
+ network.addWifiAccessPoint(WifiAccessPoint.from(mac, Integer.parseInt(values[i * 2])));
+ }
+ position.setNetwork(network);
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ getLastLocation(position, dateBuilder.getDate());
+ }
+
+ } else {
+
+ getLastLocation(position, null);
- positions.add(position);
}
+
+ position.set(Position.KEY_DOOR, door);
+ position.set(Position.PREFIX_ADC + 1, adc);
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_BATTERY, battery);
+ position.set(Position.PREFIX_TEMP + 1, temperature);
+ decodeStatus(position, status);
+
+ positions.add(position);
}
- return positions;
+ return positions.isEmpty() ? null : positions;
}
}
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
index 4042e348c..87db95946 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 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,6 +24,7 @@ import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
@@ -151,6 +152,21 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
Gt06ProtocolDecoder.decodeGps(position, buf, false, TimeZone.getTimeZone("UTC"));
+ if (buf.readableBytes() >= 5) {
+ position.setAltitude(buf.readShort());
+
+ int alarms = buf.readUnsignedByte();
+ if (BitUtil.check(alarms, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ }
+ if (BitUtil.check(alarms, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+ if (BitUtil.check(alarms, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ }
+ }
+
ByteBuf content = Unpooled.buffer();
content.writeBytes(time);
sendResponse(channel, length, type, content);
@@ -174,9 +190,18 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
buf.readUnsignedByte(); // timezone
int interval = buf.readUnsignedByte();
- if (length >= 7) {
+ if (buf.readableBytes() >= 1 + 2) {
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
}
+ if (buf.readableBytes() >= 3 + 2) {
+ buf.skipBytes(3); // temperature
+ }
+ if (buf.readableBytes() >= 1 + 2) {
+ position.set(Position.KEY_CHARGE, buf.readUnsignedByte() > 0);
+ }
+ if (buf.readableBytes() >= 1 + 2) {
+ position.set(Position.KEY_HEART_RATE, buf.readUnsignedByte());
+ }
ByteBuf content = Unpooled.buffer();
content.writeByte(interval);
diff --git a/src/main/java/org/traccar/protocol/Tr20Protocol.java b/src/main/java/org/traccar/protocol/Tr20Protocol.java
index 3eee9d9c3..1b71db03f 100644
--- a/src/main/java/org/traccar/protocol/Tr20Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -34,6 +34,14 @@ public class Tr20Protocol extends BaseProtocol {
pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this));
}
});
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
index c2e6c381f..2f11bd152 100644
--- a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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.
@@ -52,7 +52,7 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)(dd.d+),") // longitude
.number("(d+),") // speed
.number("(d+),") // course
- .number("(?:NA|[FC]?(-?d+)),") // temperature
+ .number("(?:NA|[FC]?(-?d+)[^,]*),") // temperature
.number("(x{8}),") // status
.number("(d+)") // event
.any()
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
index b99ec1cc6..dfe5235f0 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocol.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -18,14 +18,18 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
public class UlbotechProtocol extends BaseProtocol {
public UlbotechProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM);
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new UlbotechFrameDecoder());
+ pipeline.addLast(new UlbotechProtocolEncoder(UlbotechProtocol.this));
pipeline.addLast(new UlbotechProtocolDecoder(UlbotechProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolEncoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolEncoder.java
new file mode 100644
index 000000000..5528c7242
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.Protocol;
+import org.traccar.model.Command;
+
+import java.nio.charset.StandardCharsets;
+
+public class UlbotechProtocolEncoder extends BaseProtocolEncoder {
+
+ public UlbotechProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return Unpooled.copiedBuffer(
+ "*TS01," + command.getString(Command.KEY_DATA) + "#", StandardCharsets.US_ASCII);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocol.java b/src/main/java/org/traccar/protocol/UuxProtocol.java
new file mode 100644
index 000000000..41b59d829
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/UuxProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 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;
+
+public class UuxProtocol extends BaseProtocol {
+
+ public UuxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 1));
+ pipeline.addLast(new UuxProtocolDecoder(UuxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
new file mode 100644
index 000000000..cb8656545
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+
+public class UuxProtocolDecoder extends BaseProtocolDecoder {
+
+ public UuxProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_IMMOBILIZER = 0x9E;
+ public static final int MSG_ACK = 0xD0;
+ public static final int MSG_NACK = 0xF0;
+
+ private void sendResponse(Channel channel, int productCode, int protocolVersion, int type) {
+ if (channel != null && BitUtil.check(protocolVersion, 7)) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(productCode);
+ response.writeByte(BitUtil.to(protocolVersion, 7));
+ response.writeByte(1); // length
+ response.writeByte(type);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int productCode = buf.readUnsignedShort();
+ int protocolVersion = buf.readUnsignedByte();
+ buf.readUnsignedByte(); // length
+ int type = buf.readUnsignedByte();
+
+ String vehicleId = buf.readCharSequence(10, StandardCharsets.US_ASCII).toString();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, vehicleId);
+ if (deviceSession == null) {
+ sendResponse(channel, productCode, protocolVersion, MSG_NACK);
+ return null;
+ }
+
+ if (type == MSG_IMMOBILIZER) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(Calendar.getInstance().get(Calendar.YEAR), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ position.set("companyId", buf.readCharSequence(6, StandardCharsets.US_ASCII).toString());
+ position.set("tripId", buf.readUnsignedShort());
+
+ return position;
+
+ }
+
+ sendResponse(channel, productCode, protocolVersion, MSG_ACK);
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
index 223d032b8..80299ff08 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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.
@@ -49,12 +49,12 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.number("(dd)(dd)(dd);") // date (ddmmyy)
.number("(dd)(dd)(dd);") // time (hhmmss)
- .number("(dd)(dd.d+);") // latitude
- .expression("([NS]);")
- .number("(ddd)(dd.d+);") // longitude
- .expression("([EW]);")
+ .number("(?:NA|(dd)(dd.d+));") // latitude
+ .expression("(?:NA|([NS]));")
+ .number("(?:NA|(ddd)(dd.d+));") // longitude
+ .expression("(?:NA|([EW]));")
.number("(d+.?d*)?;") // speed
- .number("(d+.?d*)?;") // course
+ .number("(?:NA|(d+.?d*))?;") // course
.number("(?:NA|(-?d+.?d*));") // altitude
.number("(?:NA|(d+))") // satellites
.groupBegin().text(";")
@@ -97,11 +97,15 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
+ if (parser.hasNext(9)) {
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+ } else {
+ getLastLocation(position, position.getDeviceTime());
+ }
if (parser.hasNext()) {
int satellites = parser.nextInt(0);