aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar/protocol
diff options
context:
space:
mode:
authorIvan Muratov <binakot@gmail.com>2017-10-24 14:52:48 +0300
committerGitHub <noreply@github.com>2017-10-24 14:52:48 +0300
commitdb02157dbb29539dda4b51a5e8b317293cfc536c (patch)
treeb974f082172406e16a92cb9da8136ef856f571a5 /src/org/traccar/protocol
parent09d3cf2b5416327700ad22b652cf4a0dca09aaf2 (diff)
parent96e15853b9c28bd31295ca2c014e226e4a50aaa1 (diff)
downloadtraccar-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.gz
traccar-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.bz2
traccar-server-db02157dbb29539dda4b51a5e8b317293cfc536c.zip
Merge branch 'master' into master
Diffstat (limited to 'src/org/traccar/protocol')
-rw-r--r--src/org/traccar/protocol/AdmProtocol.java9
-rw-r--r--src/org/traccar/protocol/AdmProtocolDecoder.java107
-rw-r--r--src/org/traccar/protocol/AdmProtocolEncoder.java42
-rw-r--r--src/org/traccar/protocol/AplicomProtocolDecoder.java128
-rw-r--r--src/org/traccar/protocol/AquilaProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/AstraProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/AtrackProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/CarscopProtocolDecoder.java8
-rw-r--r--src/org/traccar/protocol/CastelProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/CityeasyProtocolEncoder.java8
-rw-r--r--src/org/traccar/protocol/CradlepointProtocolDecoder.java36
-rw-r--r--src/org/traccar/protocol/EasyTrackProtocolDecoder.java5
-rw-r--r--src/org/traccar/protocol/EelinkProtocolDecoder.java111
-rw-r--r--src/org/traccar/protocol/EskyFrameDecoder.java39
-rw-r--r--src/org/traccar/protocol/EskyProtocol.java46
-rw-r--r--src/org/traccar/protocol/EskyProtocolDecoder.java89
-rw-r--r--src/org/traccar/protocol/FifotrackProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/GenxProtocol.java45
-rw-r--r--src/org/traccar/protocol/GenxProtocolDecoder.java79
-rw-r--r--src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java406
-rw-r--r--src/org/traccar/protocol/Gl200FrameDecoder.java96
-rw-r--r--src/org/traccar/protocol/Gl200Protocol.java8
-rw-r--r--src/org/traccar/protocol/Gl200ProtocolDecoder.java850
-rw-r--r--src/org/traccar/protocol/Gl200TextProtocolDecoder.java917
-rw-r--r--src/org/traccar/protocol/GnxProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/Gt06FrameDecoder.java20
-rw-r--r--src/org/traccar/protocol/Gt06ProtocolDecoder.java141
-rw-r--r--src/org/traccar/protocol/H02Protocol.java11
-rw-r--r--src/org/traccar/protocol/H02ProtocolDecoder.java120
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolDecoder.java145
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolEncoder.java6
-rw-r--r--src/org/traccar/protocol/MegastekProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolDecoder.java30
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolEncoder.java5
-rw-r--r--src/org/traccar/protocol/MeitrackProtocolDecoder.java34
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolEncoder.java6
-rw-r--r--src/org/traccar/protocol/MxtProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/OsmAndProtocolDecoder.java19
-rw-r--r--src/org/traccar/protocol/Pt502ProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/Pt502ProtocolEncoder.java20
-rw-r--r--src/org/traccar/protocol/RuptelaProtocolDecoder.java45
-rw-r--r--src/org/traccar/protocol/StarLinkProtocolDecoder.java26
-rw-r--r--src/org/traccar/protocol/Stl060ProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java252
-rw-r--r--src/org/traccar/protocol/T55ProtocolDecoder.java8
-rw-r--r--src/org/traccar/protocol/TaipProtocolDecoder.java57
-rw-r--r--src/org/traccar/protocol/TelicProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocol.java4
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocolDecoder.java160
-rw-r--r--src/org/traccar/protocol/Tk103ProtocolDecoder.java77
-rw-r--r--src/org/traccar/protocol/TlvProtocol.java43
-rw-r--r--src/org/traccar/protocol/TlvProtocolDecoder.java109
-rw-r--r--src/org/traccar/protocol/TmgProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TotemProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TramigoProtocolDecoder.java7
-rw-r--r--src/org/traccar/protocol/TrvProtocolDecoder.java75
-rw-r--r--src/org/traccar/protocol/TytanProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TzoneProtocolDecoder.java13
-rw-r--r--src/org/traccar/protocol/UlbotechProtocolDecoder.java3
-rw-r--r--src/org/traccar/protocol/VisiontekProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Vt200ProtocolDecoder.java60
-rw-r--r--src/org/traccar/protocol/VtfmsProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/WatchProtocol.java1
-rw-r--r--src/org/traccar/protocol/WatchProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/WatchProtocolEncoder.java10
-rw-r--r--src/org/traccar/protocol/WialonProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/XexunProtocolDecoder.java13
-rw-r--r--src/org/traccar/protocol/Xt2400ProtocolDecoder.java15
70 files changed, 3298 insertions, 1336 deletions
diff --git a/src/org/traccar/protocol/AdmProtocol.java b/src/org/traccar/protocol/AdmProtocol.java
index 442121f0a..4d2cbe7b3 100644
--- a/src/org/traccar/protocol/AdmProtocol.java
+++ b/src/org/traccar/protocol/AdmProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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,8 +18,10 @@ package org.traccar.protocol;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
import java.nio.ByteOrder;
import java.util.List;
@@ -28,6 +30,9 @@ public class AdmProtocol extends BaseProtocol {
public AdmProtocol() {
super("adm");
+ setSupportedDataCommands(
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_CUSTOM);
}
@Override
@@ -36,6 +41,8 @@ public class AdmProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, -3, 0));
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new AdmProtocolEncoder());
pipeline.addLast("objectDecoder", new AdmProtocolDecoder(AdmProtocol.this));
}
};
diff --git a/src/org/traccar/protocol/AdmProtocolDecoder.java b/src/org/traccar/protocol/AdmProtocolDecoder.java
index f4a21cad0..f93c55e18 100644
--- a/src/org/traccar/protocol/AdmProtocolDecoder.java
+++ b/src/org/traccar/protocol/AdmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 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.
@@ -33,76 +33,75 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public static final int CMD_RESPONSE_SIZE = 0x84;
public static final int MSG_IMEI = 0x03;
public static final int MSG_PHOTO = 0x0A;
public static final int MSG_ADM5 = 0x01;
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.readUnsignedShort(); // device id
- buf.readUnsignedByte(); // length
-
- int type = buf.readUnsignedByte();
-
- DeviceSession deviceSession;
- if (type == MSG_IMEI) {
- deviceSession = getDeviceSession(
- channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
- } else {
- deviceSession = getDeviceSession(channel, remoteAddress);
- }
+ private Position decodeData(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int type) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
if (BitUtil.to(type, 2) == 0) {
-
Position position = new Position();
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
- buf.readUnsignedShort(); // index
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
-
- position.setValid(true);
+ int status = buf.readUnsignedShort();
+ position.set(Position.KEY_STATUS, status);
+ position.setValid(!BitUtil.check(status, 5));
position.setLatitude(buf.readFloat());
position.setLongitude(buf.readFloat());
position.setCourse(buf.readUnsignedShort() * 0.1);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
- position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte());
-
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte() * 0.1);
position.setAltitude(buf.readUnsignedShort());
-
position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte() & 0x0f);
position.setTime(new Date(buf.readUnsignedInt() * 1000));
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
if (BitUtil.check(type, 2)) {
- buf.skipBytes(4);
+ buf.readUnsignedByte(); // vib
+ buf.readUnsignedByte(); // vib_count
+
+ int out = buf.readUnsignedByte();
+ for (int i = 0; i <= 3; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(out, i) ? 1 : 0);
+ }
+
+ buf.readUnsignedByte(); // in_alarm
}
if (BitUtil.check(type, 3)) {
- buf.skipBytes(12);
+ for (int i = 1; i <= 6; i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.001);
+ }
}
if (BitUtil.check(type, 4)) {
- buf.skipBytes(8);
+ for (int i = 1; i <= 2; i++) {
+ position.set(Position.PREFIX_COUNT + i, buf.readUnsignedInt());
+ }
}
if (BitUtil.check(type, 5)) {
- buf.skipBytes(9);
+ for (int i = 1; i <= 3; i++) {
+ buf.readUnsignedShort(); // fuel level
+ }
+ for (int i = 1; i <= 3; i++) {
+ position.set(Position.PREFIX_TEMP + i, buf.readUnsignedByte());
+ }
}
if (BitUtil.check(type, 6)) {
@@ -119,4 +118,46 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private Position parseCommandResponse(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int responseTextLength = buf.bytesBefore((byte) 0);
+ if (responseTextLength < 0) {
+ responseTextLength = CMD_RESPONSE_SIZE - 3;
+ }
+ position.set(Position.KEY_RESULT, buf.readBytes(responseTextLength).toString(StandardCharsets.UTF_8));
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ buf.readUnsignedShort(); // device id
+
+ int size = buf.readUnsignedByte();
+ if (size != CMD_RESPONSE_SIZE) {
+ int type = buf.readUnsignedByte();
+ if (type == MSG_IMEI) {
+ getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.UTF_8));
+ } else {
+ return decodeData(channel, remoteAddress, buf, type);
+ }
+ } else {
+ return parseCommandResponse(channel, remoteAddress, buf);
+ }
+
+ return null;
+ }
+
}
diff --git a/src/org/traccar/protocol/AdmProtocolEncoder.java b/src/org/traccar/protocol/AdmProtocolEncoder.java
new file mode 100644
index 000000000..8cbd8618d
--- /dev/null
+++ b/src/org/traccar/protocol/AdmProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Anatoliy Golubev (darth.naihil@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.StringProtocolEncoder;
+import org.traccar.helper.Log;
+import org.traccar.model.Command;
+
+public class AdmProtocolEncoder extends StringProtocolEncoder {
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_GET_DEVICE_STATUS:
+ return formatCommand(command, "STATUS\r\n");
+
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+
+ default:
+ Log.warning(new UnsupportedOperationException(command.getType()));
+ break;
+ }
+
+ return null;
+ }
+}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java
index eb8d77011..154451b5b 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -263,7 +263,8 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0200) != 0) {
- position.set(Position.KEY_RFID, (((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID,
+ String.valueOf(((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt());
}
if ((selector & 0x0400) != 0) {
@@ -351,15 +352,28 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_VIN, buf.readBytes(18).toString(StandardCharsets.US_ASCII).trim());
}
+ if ((selector & 0x2000) != 0) {
+ buf.readUnsignedByte(); // card 1 type
+ buf.readUnsignedByte(); // card 1 country code
+ String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ if (!card.isEmpty()) {
+ position.set("card1", card);
+ }
+ }
+
+ if ((selector & 0x4000) != 0) {
+ buf.readUnsignedByte(); // card 2 type
+ buf.readUnsignedByte(); // card 2 country code
+ String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ if (!card.isEmpty()) {
+ position.set("card2", card);
+ }
+ }
+
if ((selector & 0x10000) != 0) {
int count = buf.readUnsignedByte();
for (int i = 1; i <= count; i++) {
- ChannelBuffer driver = buf.readBytes(22);
- int endIndex = driver.indexOf(0, driver.writerIndex(), (byte) 0);
- if (endIndex < 0) {
- endIndex = driver.writerIndex();
- }
- position.set("driver" + i, driver.toString(0, endIndex, StandardCharsets.US_ASCII).trim());
+ position.set("driver" + i, buf.readBytes(22).toString(StandardCharsets.US_ASCII).trim());
position.set("driverTime" + i, buf.readUnsignedInt());
}
}
@@ -456,59 +470,59 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // length
while (buf.readableBytes() > 0) {
- position.set("towedPosition", buf.readUnsignedByte());
+ buf.readUnsignedByte(); // towed position
int type = buf.readUnsignedByte();
int length = buf.readUnsignedByte();
+ int end = buf.readerIndex() + length;
- if (type == 0x01) {
- position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x02) {
- position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
- position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0);
- position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5);
- position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
- } else if (type == 0x03) {
- position.set("axleLoadSum", buf.readUnsignedShort() * 2);
- } else if (type == 0x04) {
- position.set("tyrePressure", buf.readUnsignedByte() * 10);
- position.set("pneumaticPressure", buf.readUnsignedByte() * 5);
- } else if (type == 0x05) {
- position.set("brakeLining", buf.readUnsignedByte() * 0.4);
- position.set("brakeTemperature", buf.readUnsignedByte() * 10);
- } else if (type == 0x06) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5);
- position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5);
- position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5);
- } else if (type == 0x0A) {
- ChannelBuffer brakeData = buf.readBytes(length);
- position.set("absStatusCounter", brakeData.readUnsignedShort());
- position.set("atvbStatusCounter", brakeData.readUnsignedShort());
- position.set("vdcActiveCounter", brakeData.readUnsignedShort());
- } else if (type == 0x0B) {
- position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x0C) {
- position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x0D) {
- switch (buf.readUnsignedByte()) {
- case 1:
- position.set("brakeManufacturer", "Wabco");
- break;
- case 2:
- position.set("brakeManufacturer", "Knorr");
- break;
- case 3:
- position.set("brakeManufacturer", "Haldex");
- break;
- default:
- position.set("brakeManufacturer", "Unknown");
- break;
- }
- buf.readUnsignedByte();
- position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII));
- position.set("towedDetectionStatus", buf.readUnsignedByte());
- } else if (type == 0x0E) {
- buf.skipBytes(length);
+ switch (type) {
+ case 0x01:
+ position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x02:
+ position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
+ position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0);
+ position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5);
+ position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
+ break;
+ case 0x03:
+ position.set("axleLoadSum", buf.readUnsignedShort() * 2);
+ break;
+ case 0x04:
+ position.set("tyrePressure", buf.readUnsignedByte() * 10);
+ position.set("pneumaticPressure", buf.readUnsignedByte() * 5);
+ break;
+ case 0x05:
+ position.set("brakeLining", buf.readUnsignedByte() * 0.4);
+ position.set("brakeTemperature", buf.readUnsignedByte() * 10);
+ break;
+ case 0x06:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5L);
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5L);
+ position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5L);
+ break;
+ case 0x0A:
+ position.set("absStatusCounter", buf.readUnsignedShort());
+ position.set("atvbStatusCounter", buf.readUnsignedShort());
+ position.set("vdcActiveCounter", buf.readUnsignedShort());
+ break;
+ case 0x0B:
+ position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x0C:
+ position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x0D:
+ buf.readUnsignedByte();
+ position.set("towedDetectionStatus", buf.readUnsignedInt());
+ buf.skipBytes(17); // vin
+ break;
+ case 0x0E:
+ default:
+ break;
}
+
+ buf.readerIndex(end);
}
}
@@ -554,7 +568,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
position.set("speedMax", buf.readUnsignedByte());
position.set("speedMin", buf.readUnsignedByte());
- position.set("hardBreaking", buf.readUnsignedByte());
+ position.set("hardBraking", buf.readUnsignedByte());
}
if ((selector & 0x0200) != 0) {
diff --git a/src/org/traccar/protocol/AquilaProtocolDecoder.java b/src/org/traccar/protocol/AquilaProtocolDecoder.java
index 5ff974a7d..773210b04 100644
--- a/src/org/traccar/protocol/AquilaProtocolDecoder.java
+++ b/src/org/traccar/protocol/AquilaProtocolDecoder.java
@@ -81,7 +81,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("[01],") // corner packet
.number("(?:d+,){6}") // reserved
.number("[01],") // hard acceleration
- .number("[01],") // hard breaking
+ .number("[01],") // hard braking
.number("[01],[01],[01],[01],") // course bits
.number("(d+),") // external voltage
.number("(d+),") // internal voltage
@@ -115,7 +115,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("[01],") // do 1
.number("[01],") // reserved
.number("[01],") // hard acceleration
- .number("[01],") // hard breaking
+ .number("[01],") // hard braking
.number("(?:[01],){4}") // reserved
.number("(d+),") // external voltage
.number("(d+),") // internal voltage
diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/org/traccar/protocol/AstraProtocolDecoder.java
index ea6aa7b30..8d86cd2be 100644
--- a/src/org/traccar/protocol/AstraProtocolDecoder.java
+++ b/src/org/traccar/protocol/AstraProtocolDecoder.java
@@ -105,7 +105,7 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // geofence events
if (BitUtil.check(status, 8)) {
- position.set(Position.KEY_RFID, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium() * 1000);
position.set(Position.KEY_HOURS, buf.readUnsignedShort());
}
diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java
index 79b3c36cc..23cb67e15 100644
--- a/src/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -327,7 +327,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001);
- position.set("driver", readString(buf));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, readString(buf));
position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1);
position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1);
diff --git a/src/org/traccar/protocol/CarscopProtocolDecoder.java b/src/org/traccar/protocol/CarscopProtocolDecoder.java
index ac3df1cd7..2a081bcdd 100644
--- a/src/org/traccar/protocol/CarscopProtocolDecoder.java
+++ b/src/org/traccar/protocol/CarscopProtocolDecoder.java
@@ -44,8 +44,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd.d)") // speed
.number("(dd)(dd)(dd)") // date (yymmdd)
.number("(ddd.dd)") // course
+ .groupBegin()
.number("(d{8})") // state
.number("L(d{6})") // odometer
+ .groupEnd("?")
.compile();
@Override
@@ -88,8 +90,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble(0));
- position.set(Position.KEY_STATUS, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ if (parser.hasNext(2)) {
+ position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ }
return position;
}
diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java
index 83664fa5a..3a0ccea78 100644
--- a/src/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/org/traccar/protocol/CastelProtocolDecoder.java
@@ -440,7 +440,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
}
- } else if (version == 4) {
+ } else if (version == 3 || version == 4) {
return decodeSc(channel, remoteAddress, buf, version, id, type, deviceSession);
diff --git a/src/org/traccar/protocol/CityeasyProtocolEncoder.java b/src/org/traccar/protocol/CityeasyProtocolEncoder.java
index c800131d6..387926e03 100644
--- a/src/org/traccar/protocol/CityeasyProtocolEncoder.java
+++ b/src/org/traccar/protocol/CityeasyProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.traccar.BaseProtocolEncoder;
@@ -56,13 +58,13 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
content.writeShort(0);
return encodeContent(CityeasyProtocolDecoder.MSG_LOCATION_INTERVAL, content);
case Command.TYPE_SET_TIMEZONE:
- int timezone = command.getInteger(Command.KEY_TIMEZONE);
+ int timezone = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
if (timezone < 0) {
content.writeByte(1);
} else {
content.writeByte(0);
}
- content.writeShort(Math.abs(timezone) / 60);
+ content.writeShort(Math.abs(timezone));
return encodeContent(CityeasyProtocolDecoder.MSG_TIMEZONE, content);
default:
Log.warning(new UnsupportedOperationException(command.getType()));
diff --git a/src/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/org/traccar/protocol/CradlepointProtocolDecoder.java
index 1ba50f04b..e8f95a60c 100644
--- a/src/org/traccar/protocol/CradlepointProtocolDecoder.java
+++ b/src/org/traccar/protocol/CradlepointProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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,11 +18,13 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.Date;
import java.util.regex.Pattern;
public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
@@ -33,18 +35,18 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.expression("([^,]+),") // id
- .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d{1,6}),") // time (hhmmss)
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
.number("(d+)(dd.d+),") // longitude
.expression("([EW]),")
.number("(d+.d+)?,") // speed
.number("(d+.d+)?,") // course
- .expression("([^,]+),") // carrier
+ .expression("([^,]+)?,") // carrier
.expression("([^,]+)?,") // serdis
- .number("(-?d+),") // rsrp
- .number("(-?d+),") // dbm
- .number("(-?d+),") // rsrq
+ .number("(-?d+)?,") // rsrp
+ .number("(-?d+)?,") // rssi
+ .number("(-?d+)?,") // rsrq
.expression("([^,]+)?,") // ecio
.expression("([^,]+)?") // wan ip
.compile();
@@ -58,16 +60,21 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS));
+ int time = parser.nextInt();
+ DateBuilder dateBuilder = new DateBuilder(new Date());
+ dateBuilder.setHour(time / 100 / 100);
+ dateBuilder.setMinute(time / 100 % 100);
+ dateBuilder.setSecond(time % 100);
+ position.setTime(dateBuilder.getDate());
position.setValid(true);
position.setLatitude(parser.nextCoordinate());
@@ -75,9 +82,12 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(parser.nextDouble(0));
position.setCourse(parser.nextDouble(0));
- parser.skip(4);
-
- position.set(Position.KEY_RSSI, parser.nextDouble());
+ position.set("carrid", parser.next());
+ position.set("serdis", parser.next());
+ position.set("rsrp", parser.next());
+ position.set("dbm", parser.next());
+ position.set("rsrq", parser.next());
+ position.set("ecio", parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
index f44c91c4a..799254b65 100644
--- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2017 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,6 +22,7 @@ import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -92,7 +93,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(parser.nextHexInt(0) / 600000.0);
}
- position.setSpeed(parser.nextHexInt(0) / 100.0);
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt(0) / 100.0));
position.setCourse(parser.nextHexInt(0) / 100.0);
position.set(Position.KEY_STATUS, parser.next());
diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java
index 0f6551cc3..8d0f8016a 100644
--- a/src/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -96,6 +96,22 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void decodeStatus(Position position, int status) {
+ if (BitUtil.check(status, 1)) {
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ }
+ if (BitUtil.check(status, 3)) {
+ position.set(Position.KEY_ARMED, BitUtil.check(status, 4));
+ }
+ if (BitUtil.check(status, 5)) {
+ position.set(Position.KEY_BLOCKED, !BitUtil.check(status, 6));
+ }
+ if (BitUtil.check(status, 7)) {
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 8));
+ }
+ position.set(Position.KEY_STATUS, status);
+ }
+
private Position decodeOld(DeviceSession deviceSession, ChannelBuffer buf, int type, int index) {
Position position = new Position();
@@ -115,27 +131,39 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
position.setValid((buf.readUnsignedByte() & 0x01) != 0);
- if (type == MSG_ALARM) {
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
- }
-
- if (buf.readableBytes() >= 2 * 5) {
+ if (type == MSG_GPS) {
- int status = buf.readUnsignedShort();
- if (BitUtil.check(status, 1)) {
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ if (buf.readableBytes() >= 2) {
+ decodeStatus(position, buf.readUnsignedShort());
}
- if (BitUtil.check(status, 7)) {
- position.set(Position.KEY_CHARGE, BitUtil.check(status, 8));
+
+ if (buf.readableBytes() >= 2 * 4) {
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+
+ position.set(Position.KEY_RSSI, buf.readUnsignedShort());
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+
}
- position.set(Position.KEY_STATUS, status);
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ } else if (type == MSG_ALARM) {
- position.set(Position.KEY_RSSI, buf.readUnsignedShort());
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ } else if (type == MSG_STATE) {
+
+ int statusType = buf.readUnsignedByte();
+
+ position.set(Position.KEY_EVENT, statusType);
+
+ if (statusType == 0x01 || statusType == 0x02 || statusType == 0x03) {
+ buf.readUnsignedInt(); // device time
+ if (buf.readableBytes() >= 2) {
+ decodeStatus(position, buf.readUnsignedShort());
+ }
+ }
}
@@ -189,6 +217,45 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(7); // bss2
}
+ if (buf.readableBytes() >= 2) {
+ int status = buf.readUnsignedShort();
+ position.setValid(BitUtil.check(status, 0));
+ if (BitUtil.check(status, 1)) {
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ }
+ position.set(Position.KEY_STATUS, status);
+ }
+
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ buf.readUnsignedShort(); // gsm counter
+ buf.readUnsignedShort(); // gps counter
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_STEPS, buf.readUnsignedShort());
+ buf.readUnsignedShort(); // walking time
+ }
+
+ if (buf.readableBytes() >= 12) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0);
+ position.set("humidity", buf.readUnsignedShort() * 0.1);
+ position.set("illuminance", buf.readUnsignedInt() / 256.0);
+ position.set("co2", buf.readUnsignedInt());
+ }
+
return position;
}
@@ -212,6 +279,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1));
} else {
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
@@ -221,7 +289,20 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return decodeOld(deviceSession, buf, type, index);
} else if (type >= MSG_NORMAL && type <= MSG_OBD_CODE) {
return decodeNew(deviceSession, buf, index);
+ } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) {
+
+ Position position = new Position();
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setProtocol(getProtocolName());
+
+ getLastLocation(position, null);
+
+ decodeStatus(position, buf.readUnsignedShort());
+
+ return position;
+
}
+
}
return null;
diff --git a/src/org/traccar/protocol/EskyFrameDecoder.java b/src/org/traccar/protocol/EskyFrameDecoder.java
new file mode 100644
index 000000000..3175698fd
--- /dev/null
+++ b/src/org/traccar/protocol/EskyFrameDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+public class EskyFrameDecoder extends FrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+
+ buf.readerIndex(buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 'E'));
+
+ int endIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 'E');
+ if (endIndex > 0) {
+ return buf.readBytes(endIndex - buf.readerIndex());
+ } else {
+ return buf.readBytes(buf.readableBytes()); // assume full frame
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyProtocol.java b/src/org/traccar/protocol/EskyProtocol.java
new file mode 100644
index 000000000..4c1d11f7d
--- /dev/null
+++ b/src/org/traccar/protocol/EskyProtocol.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class EskyProtocol extends BaseProtocol {
+
+ public EskyProtocol() {
+ super("esky");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new EskyFrameDecoder());
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyProtocolDecoder.java b/src/org/traccar/protocol/EskyProtocolDecoder.java
new file mode 100644
index 000000000..d524224af
--- /dev/null
+++ b/src/org/traccar/protocol/EskyProtocolDecoder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 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.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class EskyProtocolDecoder extends BaseProtocolDecoder {
+
+ public EskyProtocolDecoder(EskyProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("EO;") // header
+ .number("d+;") // index
+ .number("(d+);") // imei
+ .text("R;") // data type
+ .number("(d+)").text("+") // satellites
+ .number("(dd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd)").text("+") // time
+ .number("(-?d+.d+)").text("+") // latitude
+ .number("(-?d+.d+)").text("+") // longitude
+ .number("(d+.d+)").text("+") // speed
+ .number("(d+)").text("+") // course
+ .text("0x").number("(d+)").text("+") // input
+ .number("(d+)").text("+") // message type
+ .number("(d+)").text("+") // odometer
+ .number("(d+)") // voltage
+ .any()
+ .compile();
+
+ @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();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setValid(true);
+ position.setTime(parser.nextDateTime());
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromMps(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/org/traccar/protocol/FifotrackProtocolDecoder.java
index f8f4fb078..304f6a2c3 100644
--- a/src/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -110,7 +110,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16));
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
String[] sensors = parser.next().split("\\|");
diff --git a/src/org/traccar/protocol/GenxProtocol.java b/src/org/traccar/protocol/GenxProtocol.java
new file mode 100644
index 000000000..2b5b1a43d
--- /dev/null
+++ b/src/org/traccar/protocol/GenxProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class GenxProtocol extends BaseProtocol {
+
+ public GenxProtocol() {
+ super("genx");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new GenxProtocolDecoder(GenxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GenxProtocolDecoder.java b/src/org/traccar/protocol/GenxProtocolDecoder.java
new file mode 100644
index 000000000..3b716796c
--- /dev/null
+++ b/src/org/traccar/protocol/GenxProtocolDecoder.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 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.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+
+public class GenxProtocolDecoder extends BaseProtocolDecoder {
+
+ private int[] reportColumns;
+
+ public GenxProtocolDecoder(GenxProtocol protocol) {
+ super(protocol);
+ setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4"));
+ }
+
+ public void setReportColumns(String format) {
+ String[] columns = format.split(",");
+ reportColumns = new int[columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ reportColumns[i] = Integer.parseInt(columns[i]);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String[] values = ((String) msg).split(",");
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setValid(true);
+
+ for (int i = 0; i < Math.min(values.length, reportColumns.length); i++) {
+ switch (reportColumns[i]) {
+ case 1:
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+ break;
+ case 2:
+ position.setTime(new SimpleDateFormat("MM/dd/yy HH:mm:ss").parse(values[i]));
+ break;
+ case 3:
+ position.setLatitude(Double.parseDouble(values[i]));
+ break;
+ case 4:
+ position.setLongitude(Double.parseDouble(values[i]));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return position.getDeviceId() != 0 ? position : null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
new file mode 100644
index 000000000..071960e49
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.BitBuffer;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+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.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
+
+ public Gl200BinaryProtocolDecoder(Gl200Protocol protocol) {
+ super(protocol);
+ }
+
+ private Date decodeTime(ChannelBuffer buf) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ return dateBuilder.getDate();
+ }
+
+ public static final int MSG_RSP_LCB = 3;
+ public static final int MSG_RSP_GEO = 8;
+ public static final int MSG_RSP_COMPRESSED = 100;
+
+ private List<Position> decodeLocation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+ buf.readUnsignedShort(); // firmware version
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int battery = buf.readUnsignedByte();
+ int power = buf.readUnsignedShort();
+
+ if (type == MSG_RSP_GEO) {
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte(); // reserved
+ }
+
+ buf.readUnsignedByte(); // motion status
+ int satellites = buf.readUnsignedByte();
+
+ if (type != MSG_RSP_COMPRESSED) {
+ buf.readUnsignedByte(); // index
+ }
+
+ if (type == MSG_RSP_LCB) {
+ buf.readUnsignedByte(); // phone length
+ for (int b = buf.readUnsignedByte();; b = buf.readUnsignedByte()) {
+ if ((b & 0xf) == 0xf || (b & 0xf0) == 0xf0) {
+ break;
+ }
+ }
+ }
+
+ if (type == MSG_RSP_COMPRESSED) {
+
+ int count = buf.readUnsignedShort();
+
+ BitBuffer bits;
+ int speed = 0;
+ int heading = 0;
+ int latitude = 0;
+ int longitude = 0;
+ long time = 0;
+
+ for (int i = 0; i < count; i++) {
+
+ if (time > 0) {
+ time += 1;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (BitUtil.from(buf.getUnsignedByte(buf.readerIndex()), 8 - 2)) {
+ case 1:
+ bits = new BitBuffer(buf.readBytes(3));
+ bits.readUnsigned(2); // point attribute
+ bits.readUnsigned(1); // fix type
+ speed = bits.readUnsigned(12);
+ heading = bits.readUnsigned(9);
+ longitude = buf.readInt();
+ latitude = buf.readInt();
+ if (time == 0) {
+ time = buf.readUnsignedInt();
+ }
+ break;
+ case 2:
+ bits = new BitBuffer(buf.readBytes(5));
+ bits.readUnsigned(2); // point attribute
+ bits.readUnsigned(1); // fix type
+ speed += bits.readSigned(7);
+ heading += bits.readSigned(7);
+ longitude += bits.readSigned(12);
+ latitude += bits.readSigned(11);
+ break;
+ default:
+ buf.readUnsignedByte(); // invalid or same
+ continue;
+ }
+
+ position.setValid(true);
+ position.setTime(new Date(time * 1000));
+ position.setSpeed(UnitsConverter.knotsFromKph(speed * 0.1));
+ position.setCourse(heading);
+ position.setLongitude(longitude * 0.000001);
+ position.setLatitude(latitude * 0.000001);
+
+ positions.add(position);
+
+ }
+
+ } else {
+
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_SATELLITES, satellites);
+
+ int hdop = buf.readUnsignedByte();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+ position.setAltitude(buf.readShort());
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readInt() * 0.000001);
+
+ position.setTime(decodeTime(buf));
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+
+ buf.readUnsignedByte(); // reserved
+
+ positions.add(position);
+
+ }
+
+ }
+
+ return positions;
+ }
+
+ public static final int MSG_EVT_BPL = 6;
+ public static final int MSG_EVT_VGN = 45;
+ public static final int MSG_EVT_VGF = 46;
+ public static final int MSG_EVT_UPD = 15;
+ public static final int MSG_EVT_IDF = 17;
+ public static final int MSG_EVT_GSS = 21;
+ public static final int MSG_EVT_GES = 26;
+ public static final int MSG_EVT_GPJ = 31;
+ public static final int MSG_EVT_RMD = 35;
+ public static final int MSG_EVT_JDS = 33;
+ public static final int MSG_EVT_CRA = 23;
+ public static final int MSG_EVT_UPC = 34;
+
+ private Position decodeEvent(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+
+ position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort()));
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort());
+
+ buf.readUnsignedByte(); // motion status
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ switch (type) {
+ case MSG_EVT_BPL:
+ buf.readUnsignedShort(); // backup battery voltage
+ break;
+ case MSG_EVT_VGN:
+ case MSG_EVT_VGF:
+ buf.readUnsignedShort(); // reserved
+ buf.readUnsignedByte(); // report type
+ buf.readUnsignedInt(); // ignition duration
+ break;
+ case MSG_EVT_UPD:
+ buf.readUnsignedShort(); // code
+ buf.readUnsignedByte(); // retry
+ break;
+ case MSG_EVT_IDF:
+ buf.readUnsignedInt(); // idling duration
+ break;
+ case MSG_EVT_GSS:
+ buf.readUnsignedByte(); // gps signal status
+ buf.readUnsignedInt(); // reserved
+ break;
+ case MSG_EVT_GES:
+ buf.readUnsignedShort(); // trigger geo id
+ buf.readUnsignedByte(); // trigger geo enable
+ buf.readUnsignedByte(); // trigger mode
+ buf.readUnsignedInt(); // radius
+ buf.readUnsignedInt(); // check interval
+ break;
+ case MSG_EVT_GPJ:
+ buf.readUnsignedByte(); // cw jamming value
+ buf.readUnsignedByte(); // gps jamming state
+ break;
+ case MSG_EVT_RMD:
+ buf.readUnsignedByte(); // roaming state
+ break;
+ case MSG_EVT_JDS:
+ buf.readUnsignedByte(); // jamming state
+ break;
+ case MSG_EVT_CRA:
+ buf.readUnsignedByte(); // crash counter
+ break;
+ case MSG_EVT_UPC:
+ buf.readUnsignedByte(); // command id
+ buf.readUnsignedShort(); // result
+ break;
+ default:
+ break;
+ }
+
+ buf.readUnsignedByte(); // count
+
+ int hdop = buf.readUnsignedByte();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+ position.setAltitude(buf.readShort());
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readInt() * 0.000001);
+
+ position.setTime(decodeTime(buf));
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+
+ buf.readUnsignedByte(); // reserved
+
+ return position;
+ }
+
+ public static final int MSG_INF_GPS = 2;
+ public static final int MSG_INF_CID = 4;
+ public static final int MSG_INF_CSQ = 5;
+ public static final int MSG_INF_VER = 6;
+ public static final int MSG_INF_BAT = 7;
+ public static final int MSG_INF_TMZ = 9;
+ public static final int MSG_INF_GIR = 10;
+
+ private Position decodeInformation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+
+ position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort()));
+
+ if (type == MSG_INF_VER) {
+ buf.readUnsignedShort(); // hardware version
+ buf.readUnsignedShort(); // mcu version
+ buf.readUnsignedShort(); // reserved
+ }
+
+ buf.readUnsignedByte(); // motion status
+ buf.readUnsignedByte(); // reserved
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ buf.readUnsignedByte(); // mode
+ buf.skipBytes(7); // last fix time
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte();
+ buf.readUnsignedShort(); // response report mask
+ buf.readUnsignedShort(); // ign interval
+ buf.readUnsignedShort(); // igf interval
+ buf.readUnsignedInt(); // reserved
+ buf.readUnsignedByte(); // reserved
+
+ if (type == MSG_INF_BAT) {
+ position.set(Position.KEY_CHARGE, buf.readUnsignedByte() != 0);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ }
+
+ buf.skipBytes(10); // iccid
+
+ if (type == MSG_INF_CSQ) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ buf.readUnsignedByte();
+ }
+
+ buf.readUnsignedByte(); // time zone flags
+ buf.readUnsignedShort(); // time zone offset
+
+ if (type == MSG_INF_GIR) {
+ buf.readUnsignedByte(); // gir trigger
+ buf.readUnsignedByte(); // cell number
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+ buf.readUnsignedByte(); // ta
+ buf.readUnsignedByte(); // rx level
+ }
+
+ getLastLocation(position, decodeTime(buf));
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ switch (buf.readBytes(4).toString(StandardCharsets.US_ASCII)) {
+ case "+RSP":
+ return decodeLocation(channel, remoteAddress, buf);
+ case "+INF":
+ return decodeInformation(channel, remoteAddress, buf);
+ case "+EVT":
+ return decodeEvent(channel, remoteAddress, buf);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200FrameDecoder.java b/src/org/traccar/protocol/Gl200FrameDecoder.java
new file mode 100644
index 000000000..960c3779a
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200FrameDecoder.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Gl200FrameDecoder extends FrameDecoder {
+
+ private static final int MINIMUM_LENGTH = 11;
+
+ private static final Set<String> BINARY_HEADERS = new HashSet<>(
+ Arrays.asList("+RSP", "+BSP", "+EVT", "+BVT", "+INF", "+BNF", "+HBD", "+CRD", "+BRD"));
+
+ public static boolean isBinary(ChannelBuffer buf) {
+ String header = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII);
+ if (header.equals("+ACK")) {
+ return buf.getByte(buf.readerIndex() + header.length()) != (byte) ':';
+ } else {
+ return BINARY_HEADERS.contains(header);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+
+ if (buf.readableBytes() < MINIMUM_LENGTH) {
+ return null;
+ }
+
+ if (isBinary(buf)) {
+
+ int length;
+ switch (buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII)) {
+ case "+ACK":
+ length = buf.getUnsignedByte(buf.readerIndex() + 6);
+ break;
+ case "+INF":
+ case "+BNF":
+ length = buf.getUnsignedShort(buf.readerIndex() + 7);
+ break;
+ case "+HBD":
+ length = buf.getUnsignedByte(buf.readerIndex() + 5);
+ break;
+ case "+CRD":
+ case "+BRD":
+ length = buf.getUnsignedShort(buf.readerIndex() + 6);
+ break;
+ default:
+ length = buf.getUnsignedShort(buf.readerIndex() + 9);
+ break;
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readBytes(length);
+ }
+
+ } else {
+
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '$');
+ if (endIndex < 0) {
+ endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ }
+ if (endIndex > 0) {
+ ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex());
+ buf.readByte(); // delimiter
+ return frame;
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java
index b3743042c..799d7fe36 100644
--- a/src/org/traccar/protocol/Gl200Protocol.java
+++ b/src/org/traccar/protocol/Gl200Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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,10 +18,8 @@ package org.traccar.protocol;
import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
@@ -44,9 +42,8 @@ public class Gl200Protocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(4096, "$", "\0"));
+ pipeline.addLast("frameDecoder", new Gl200FrameDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
@@ -55,7 +52,6 @@ public class Gl200Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
index a3062c942..0de7bb926 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 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.
@@ -15,864 +15,34 @@
*/
package org.traccar.protocol;
+import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
-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 org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
- private boolean ignoreFixTime;
+ private final Gl200TextProtocolDecoder textProtocolDecoder;
+ private final Gl200BinaryProtocolDecoder binaryProtocolDecoder;
public Gl200ProtocolDecoder(Gl200Protocol protocol) {
super(protocol);
-
- ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
- }
-
- private static final Pattern PATTERN_ACK = new PatternBuilder()
- .text("+ACK:GT")
- .expression("...,") // type
- .number("([0-9A-Z]{2}xxxx),") // protocol version
- .number("(d{15}|x{14}),") // imei
- .any().text(",")
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_INF = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTINF,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:[0-9A-Z]{17},)?") // vin
- .expression("(?:[^,]+)?,") // device name
- .number("(xx),") // state
- .expression("(?:[0-9F]{20})?,") // iccid
- .number("d{1,2},")
- .number("d{1,2},")
- .expression("[01],") // external power
- .number("([d.]+)?,") // odometer or external power
- .number("d*,") // backup battery or lightness
- .number("(d+.d+),") // battery
- .expression("([01]),") // charging
- .number("(?:d),") // led
- .number("(?:d)?,") // gps on need
- .number("(?:d)?,") // gps antenna type
- .number("(?:d),").optional() // gps antenna state
- .number("d{14},") // last fix time
- .groupBegin()
- .number("(d+),") // battery percentage
- .expression("[01]?,") // flash type
- .number("(-?[d.]+)?,,,") // temperature
- .or()
- .expression("(?:[01])?,").optional() // pin15 mode
- .number("(d+)?,") // adc1
- .number("(d+)?,").optional() // adc2
- .number("(xx)?,") // digital input
- .number("(xx)?,") // digital output
- .number("[-+]dddd,") // timezone
- .expression("[01],") // daylight saving
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_VER = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTVER,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .expression("([^,]*),") // device type
- .number("(xxxx),") // firmware version
- .number("(xxxx),") // hardware version
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_LOCATION = new PatternBuilder()
- .number("(d{1,2})?,") // hdop
- .number("(d{1,3}.d)?,") // speed
- .number("(d{1,3})?,") // course
- .number("(-?d{1,5}.d)?,") // altitude
- .number("(-?d{1,3}.d{6})?,") // longitude
- .number("(-?d{1,2}.d{6})?,") // latitude
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(d+)?,") // mcc
- .number("(d+)?,") // mnc
- .groupBegin()
- .number("(d+),") // lac
- .number("(d+),") // cid
- .or()
- .number("(x+)?,") // lac
- .number("(x+)?,") // cid
- .groupEnd()
- .number("(?:d+|(d+.d))?,") // odometer
- .compile();
-
- private static final Pattern PATTERN_OBD = new PatternBuilder()
- .text("+RESP:GTOBD,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:[0-9A-Z]{17})?,") // vin
- .expression("[^,]{0,20},") // device name
- .expression("[01],") // report type
- .number("x{1,8},") // report mask
- .expression("(?:[0-9A-Z]{17})?,") // vin
- .number("[01],") // obd connect
- .number("(?:d{1,5})?,") // obd voltage
- .number("(?:x{8})?,") // support pids
- .number("(d{1,5})?,") // engine rpm
- .number("(d{1,3})?,") // speed
- .number("(-?d{1,3})?,") // coolant temp
- .number("(d+.?d*|Inf|NaN)?,") // fuel consumption
- .number("(d{1,5})?,") // dtcs cleared distance
- .number("(?:d{1,5})?,")
- .expression("([01])?,") // obd connect
- .number("(d{1,3})?,") // number of dtcs
- .number("(x*),") // dtcs
- .number("(d{1,3})?,") // throttle
- .number("(?:d{1,3})?,") // engine load
- .number("(d{1,3})?,") // fuel level
- .expression("(?:[0-9A],)?") // obd protocol
- .number("(d+),") // odometer
- .expression(PATTERN_LOCATION.pattern())
- .number("(d{1,7}.d)?,") // odometer
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_FRI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTFRI,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:([0-9A-Z]{17}),)?") // vin
- .expression("[^,]*,") // device name
- .number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
- .expression("((?:")
- .expression(PATTERN_LOCATION.pattern())
- .expression(")+)")
- .groupBegin()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
- .number("(d{1,7}.d)?,") // odometer
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
- .number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
- .number("(d+)?,") // rpm
- .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
- .number("(d+)?,") // fuel level
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_ERI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTERI,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("x{8},") // mask
- .number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
- .expression("((?:")
- .expression(PATTERN_LOCATION.pattern())
- .expression(")+)")
- .number("(d{1,7}.d)?,") // odometer
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
- .number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
- .expression("(.*)") // additional data
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_IGN = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("d+,") // ignition off duration
- .expression(PATTERN_LOCATION.pattern())
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(d{1,7}.d)?,") // odometer
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_IDA = new PatternBuilder()
- .text("+RESP:GTIDA,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,,") // device name
- .number("([^,]+),") // rfid
- .expression("[01],") // report type
- .number("1,") // count
- .expression(PATTERN_LOCATION.pattern())
- .number("(d+.d),") // odometer
- .text(",,,,")
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_WIF = new PatternBuilder()
- .text("+RESP:GTWIF,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("(d+),") // count
- .number("((?:x{12},-?d+,,,,)+),,,,") // wifi
- .number("(d{1,3}),") // battery
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_GSM = new PatternBuilder()
- .text("+RESP:GTGSM,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:STR|CTN|NMR|RTL),") // fix type
- .expression("(.*)") // cells
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GT...,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("d*,")
- .number("(d{1,2}),") // report type
- .number("d{1,2},") // count
- .expression(PATTERN_LOCATION.pattern())
- .groupBegin()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
- .number("(d{1,7}.d)?,") // odometer
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)") // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_BASIC = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF)").text(":")
- .expression("GT...,")
- .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version
- .number("(d{15}|x{14}),") // imei
- .any()
- .number("(d{1,2})?,") // hdop
- .number("(d{1,3}.d)?,") // speed
- .number("(d{1,3})?,") // course
- .number("(-?d{1,5}.d)?,") // altitude
- .number("(-?d{1,3}.d{6})?,") // longitude
- .number("(-?d{1,2}.d{6})?,") // latitude
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(d+),") // mcc
- .number("(d+),") // mnc
- .number("(x+),") // lac
- .number("(x+),").optional(4) // cell
- .any()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN_ACK, sentence);
- if (parser.matches()) {
- String protocolVersion = parser.next();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- if (type.equals("HBD")) {
- if (channel != null) {
- parser.skip(6);
- channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress);
- }
- } else {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, parser.nextDateTime());
- position.setValid(false);
- position.set(Position.KEY_RESULT, "Command " + type + " accepted");
- return position;
- }
- }
- return null;
- }
-
- private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
- if (parser.matches()) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession != null) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- return position;
- }
- }
- return null;
- }
-
- private void decodeDeviceTime(Position position, Parser parser) {
- if (parser.hasNext(6)) {
- if (ignoreFixTime) {
- position.setTime(parser.nextDateTime());
- } else {
- position.setDeviceTime(parser.nextDateTime());
- }
- }
- }
-
- private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_INF, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_STATUS, parser.next());
-
- parser.next(); // odometer or external power
-
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
-
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- position.set(Position.PREFIX_TEMP + 1, parser.next());
-
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
-
- position.set(Position.KEY_INPUT, parser.next());
- position.set(Position.KEY_OUTPUT, parser.next());
-
- getLastLocation(position, parser.nextDateTime());
-
- position.set(Position.KEY_INDEX, parser.nextHexInt(0));
-
- return position;
- }
-
- private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_VER, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set("deviceType", parser.next());
- position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0));
- position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0));
-
- getLastLocation(position, parser.nextDateTime());
-
- return position;
- }
-
- private void decodeLocation(Position position, Parser parser) {
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
- position.set(Position.KEY_HDOP, hdop);
-
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- if (parser.hasNext(8)) {
- position.setValid(true);
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
- position.setTime(parser.nextDateTime());
- } else {
- getLastLocation(position, null);
- }
-
- if (parser.hasNext(6)) {
- int mcc = parser.nextInt(0);
- int mnc = parser.nextInt(0);
- if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0))));
- }
- if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0))));
- }
- }
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- }
-
- private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_OBD, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_RPM, parser.nextInt());
- position.set(Position.KEY_OBD_SPEED, parser.nextInt());
- position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
- position.set("dtcsClearedDistance", parser.nextInt());
- position.set("odbConnect", parser.nextInt(0) == 1);
- position.set("dtcsNumber", parser.nextInt());
- position.set("dtcsCodes", parser.next());
- position.set(Position.KEY_THROTTLE, parser.nextInt());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
- position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000);
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private void decodeStatus(Position position, Parser parser) {
- if (parser.hasNext(3)) {
- int ignition = parser.nextHexInt(0);
- if (BitUtil.check(ignition, 4)) {
- position.set(Position.KEY_IGNITION, false);
- } else if (BitUtil.check(ignition, 5)) {
- position.set(Position.KEY_IGNITION, true);
- }
- position.set(Position.KEY_INPUT, parser.nextHexInt(0));
- position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
- }
- }
-
- private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_FRI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- LinkedList<Position> positions = new LinkedList<>();
-
- String vin = parser.next();
- int power = parser.nextInt(0);
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_VIN, vin);
-
- decodeLocation(position, itemParser);
-
- positions.add(position);
- }
-
- Position position = positions.getLast();
-
- decodeLocation(position, parser);
-
- // power value only on some devices
- if (power > 10) {
- position.set(Position.KEY_POWER, power);
- }
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, 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.KEY_RPM, parser.nextInt());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
-
- decodeDeviceTime(position, parser);
-
- return positions;
- }
-
- private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_ERI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- LinkedList<Position> positions = new LinkedList<>();
-
- int power = parser.nextInt(0);
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- decodeLocation(position, itemParser);
-
- positions.add(position);
- }
-
- Position position = positions.getLast();
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_POWER, power);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, 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);
-
- int index = 0;
- String[] data = parser.next().split(",");
- if (data.length > 1) {
- int deviceType = Integer.parseInt(data[index++]);
- if (deviceType == 2) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index++; // id
- index++; // type
- position.set(Position.PREFIX_TEMP + i, Short.parseShort(data[index++], 16) * 0.0625);
- }
- }
- }
-
- decodeDeviceTime(position, parser);
-
- return positions;
- }
-
- private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_IGN, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_HOURS, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_IDA, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_RFID, parser.next());
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_WIF, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- getLastLocation(position, null);
-
- Network network = new Network();
-
- parser.nextInt(0); // count
- Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
- while (matcher.find()) {
- String mac = matcher.group(1).replaceAll("(..)", "$1:");
- network.addWifiAccessPoint(WifiAccessPoint.from(
- mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2))));
- }
-
- position.setNetwork(network);
-
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
-
- return position;
- }
-
- private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_GSM, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- getLastLocation(position, null);
-
- Network network = new Network();
-
- String[] data = parser.next().split(",");
- for (int i = 0; i < 6; i++) {
- if (!data[i * 6].isEmpty()) {
- network.addCellTower(CellTower.from(
- Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]),
- Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16),
- Integer.parseInt(data[i * 6 + 4])));
- }
- }
-
- position.setNetwork(network);
-
- return position;
- }
-
- private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- int reportType = parser.nextInt(0);
- if (type.equals("NMR")) {
- position.set(Position.KEY_MOTION, reportType == 1);
- } else if (type.equals("SOS")) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
- }
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
- channel.write("+SACK:" + parser.next() + "$", remoteAddress);
- }
-
- return position;
- }
-
- private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN_BASIC, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
- position.set(Position.KEY_HDOP, hdop);
-
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- if (parser.hasNext(2)) {
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
- } else {
- getLastLocation(position, null);
- }
-
- if (parser.hasNext(6)) {
- position.setTime(parser.nextDateTime());
- }
-
- if (parser.hasNext(4)) {
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
- }
-
- decodeDeviceTime(position, parser);
-
- switch (type) {
- case "PNA":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
- break;
- case "PFA":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
- break;
- case "EPN":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
- break;
- case "EPF":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
- break;
- case "BPL":
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- break;
- case "STT":
- position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
- break;
- case "SWG":
- position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
- break;
- case "TMP":
- case "TEM":
- position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
- break;
- case "JDR":
- case "JDS":
- position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
- break;
- default:
- break;
- }
-
- return position;
+ textProtocolDecoder = new Gl200TextProtocolDecoder(protocol);
+ binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol);
}
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
+ ChannelBuffer buf = (ChannelBuffer) msg;
- int typeIndex = sentence.indexOf(":GT");
- if (typeIndex < 0) {
- return null;
- }
-
- Object result;
- String type = sentence.substring(typeIndex + 3, typeIndex + 6);
- if (sentence.startsWith("+ACK")) {
- result = decodeAck(channel, remoteAddress, sentence, type);
+ if (Gl200FrameDecoder.isBinary(buf)) {
+ return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
} else {
- switch (type) {
- case "INF":
- result = decodeInf(channel, remoteAddress, sentence);
- break;
- case "OBD":
- result = decodeObd(channel, remoteAddress, sentence);
- break;
- case "FRI":
- result = decodeFri(channel, remoteAddress, sentence);
- break;
- case "ERI":
- result = decodeEri(channel, remoteAddress, sentence);
- break;
- case "IGN":
- case "IGF":
- result = decodeIgn(channel, remoteAddress, sentence);
- break;
- case "IDA":
- result = decodeIda(channel, remoteAddress, sentence);
- break;
- case "WIF":
- result = decodeWif(channel, remoteAddress, sentence);
- break;
- case "GSM":
- result = decodeGsm(channel, remoteAddress, sentence);
- break;
- case "VER":
- result = decodeVer(channel, remoteAddress, sentence);
- break;
- default:
- result = decodeOther(channel, remoteAddress, sentence, type);
- break;
- }
-
- if (result == null) {
- result = decodeBasic(channel, remoteAddress, sentence, type);
- }
-
- if (result != null) {
- if (result instanceof Position) {
- ((Position) result).set(Position.KEY_TYPE, type);
- } else {
- for (Position p : (List<Position>) result) {
- p.set(Position.KEY_TYPE, type);
- }
- }
- }
+ return textProtocolDecoder.decode(channel, remoteAddress, msg);
}
-
- return result;
}
}
diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
new file mode 100644
index 000000000..2f03cbb8f
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -0,0 +1,917 @@
+/*
+ * Copyright 2012 - 2017 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+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 org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
+
+ private boolean ignoreFixTime;
+
+ public Gl200TextProtocolDecoder(Gl200Protocol protocol) {
+ super(protocol);
+
+ ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
+ }
+
+ private static final Pattern PATTERN_ACK = new PatternBuilder()
+ .text("+ACK:GT")
+ .expression("...,") // type
+ .number("([0-9A-Z]{2}xxxx),") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any().text(",")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_INF = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTINF,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17},)?") // vin
+ .expression("(?:[^,]+)?,") // device name
+ .number("(xx),") // state
+ .expression("(?:[0-9Ff]{20})?,") // iccid
+ .number("(d{1,2}),") // rssi
+ .number("d{1,2},")
+ .expression("[01],") // external power
+ .number("([d.]+)?,") // odometer or external power
+ .number("d*,") // backup battery or lightness
+ .number("(d+.d+),") // battery
+ .expression("([01]),") // charging
+ .number("(?:d),") // led
+ .number("(?:d)?,") // gps on need
+ .number("(?:d)?,") // gps antenna type
+ .number("(?:d)?,").optional() // gps antenna state
+ .number("d{14},") // last fix time
+ .groupBegin()
+ .number("(d+),") // battery percentage
+ .number("[d.]*,") // flash type / power
+ .number("(-?[d.]+)?,,,") // temperature
+ .or()
+ .expression("(?:[01])?,").optional() // pin15 mode
+ .number("(d+)?,") // adc1
+ .number("(d+)?,").optional() // adc2
+ .number("(xx)?,") // digital input
+ .number("(xx)?,") // digital output
+ .number("[-+]dddd,") // timezone
+ .expression("[01],") // daylight saving
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_VER = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTVER,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .expression("([^,]*),") // device type
+ .number("(xxxx),") // firmware version
+ .number("(xxxx),") // hardware version
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+)?,") // mcc
+ .number("(d+)?,") // mnc
+ .groupBegin()
+ .number("(d+),") // lac
+ .number("(d+),") // cid
+ .or()
+ .number("(x+)?,") // lac
+ .number("(x+)?,") // cid
+ .groupEnd()
+ .number("(?:d+|(d+.d))?,") // odometer
+ .compile();
+
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("+RESP:GTOBD,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .expression("[^,]{0,20},") // device name
+ .expression("[01],") // report type
+ .number("x{1,8},") // report mask
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .number("[01],") // obd connect
+ .number("(?:d{1,5})?,") // obd voltage
+ .number("(?:x{8})?,") // support pids
+ .number("(d{1,5})?,") // engine rpm
+ .number("(d{1,3})?,") // speed
+ .number("(-?d{1,3})?,") // coolant temp
+ .number("(d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d{1,5})?,") // dtcs cleared distance
+ .number("(?:d{1,5})?,")
+ .expression("([01])?,") // obd connect
+ .number("(d{1,3})?,") // number of dtcs
+ .number("(x*),") // dtcs
+ .number("(d{1,3})?,") // throttle
+ .number("(?:d{1,3})?,") // engine load
+ .number("(d{1,3})?,") // fuel level
+ .expression("(?:[0-9A],)?") // obd protocol
+ .number("(d+),") // odometer
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_FRI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTFRI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:([0-9A-Z]{17}),)?") // vin
+ .expression("[^,]*,") // device name
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,") // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .number("(d+)?,") // rpm
+ .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d+)?,") // fuel level
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_ERI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTERI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("x{8},") // mask
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,") // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .expression("(.*)") // additional data
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IGN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d+,") // ignition off duration
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IDA = new PatternBuilder()
+ .text("+RESP:GTIDA,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,,") // device name
+ .number("([^,]+),") // rfid
+ .expression("[01],") // report type
+ .number("1,") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d+.d),") // odometer
+ .text(",,,,")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_WIF = new PatternBuilder()
+ .text("+RESP:GTWIF,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(d+),") // count
+ .number("((?:x{12},-?d+,,,,)+),,,,") // wifi
+ .number("(d{1,3}),") // battery
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_GSM = new PatternBuilder()
+ .text("+RESP:GTGSM,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:STR|CTN|NMR|RTL),") // fix type
+ .expression("(.*)") // cells
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d*,")
+ .number("(d{1,2}),") // report type
+ .number("d{1,2},") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_BASIC = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF)").text(":")
+ .expression("GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+),").optional(4) // cell
+ .any()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_ACK, sentence);
+ if (parser.matches()) {
+ String protocolVersion = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ if (type.equals("HBD")) {
+ if (channel != null) {
+ parser.skip(6);
+ channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress);
+ }
+ } else {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, parser.nextDateTime());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, "Command " + type + " accepted");
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
+ if (parser.matches()) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private void decodeDeviceTime(Position position, Parser parser) {
+ if (parser.hasNext(6)) {
+ if (ignoreFixTime) {
+ position.setTime(parser.nextDateTime());
+ } else {
+ position.setDeviceTime(parser.nextDateTime());
+ }
+ }
+ }
+
+ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_INF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ switch (parser.nextHexInt()) {
+ case 0x16:
+ case 0x1A:
+ case 0x12:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x11:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x21:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x22:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x41:
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x42:
+ position.set(Position.KEY_MOTION, true);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ parser.next(); // odometer or external power
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble(0));
+ position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ position.set(Position.PREFIX_TEMP + 1, parser.next());
+
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+
+ position.set(Position.KEY_INPUT, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.next());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ position.set(Position.KEY_INDEX, parser.nextHexInt(0));
+
+ return position;
+ }
+
+ private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_VER, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set("deviceType", parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0));
+ position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0));
+
+ getLastLocation(position, parser.nextDateTime());
+
+ return position;
+ }
+
+ private void decodeLocation(Position position, Parser parser) {
+ int hdop = parser.nextInt(0);
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(8)) {
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble(0));
+ position.setTime(parser.nextDateTime());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ int mcc = parser.nextInt(0);
+ int mnc = parser.nextInt(0);
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0))));
+ }
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
+ }
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ }
+
+ private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_OBD, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
+ position.set("dtcsClearedDistance", parser.nextInt());
+ position.set("odbConnect", parser.nextInt(0) == 1);
+ position.set("dtcsNumber", parser.nextInt());
+ position.set("dtcsCodes", parser.next());
+ position.set(Position.KEY_THROTTLE, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000);
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private void decodeStatus(Position position, Parser parser) {
+ if (parser.hasNext(3)) {
+ int ignition = parser.nextHexInt(0);
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+ position.set(Position.KEY_INPUT, parser.nextHexInt(0));
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
+ }
+ }
+
+ private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_FRI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ String vin = parser.next();
+ int power = parser.nextInt(0);
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_VIN, vin);
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ decodeLocation(position, parser);
+
+ // power value only on some devices
+ if (power > 10) {
+ position.set(Position.KEY_POWER, power);
+ }
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_HOURS, 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.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_ERI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ int power = parser.nextInt(0);
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_HOURS, 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);
+
+ int index = 0;
+ String[] data = parser.next().split(",");
+ if (data.length > 1) {
+ int deviceType = Integer.parseInt(data[index++]);
+ if (deviceType == 2) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index++; // id
+ index++; // type
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index++], 16) * 0.0625);
+ }
+ }
+ }
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IGN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_HOURS, parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IDA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_WIF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ parser.nextInt(0); // count
+ Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
+ while (matcher.find()) {
+ String mac = matcher.group(1).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2))));
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+
+ return position;
+ }
+
+ private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_GSM, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ String[] data = parser.next().split(",");
+ for (int i = 0; i < 6; i++) {
+ if (!data[i * 6].isEmpty()) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]),
+ Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16),
+ Integer.parseInt(data[i * 6 + 4])));
+ }
+ }
+
+ position.setNetwork(network);
+
+ return position;
+ }
+
+ private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int reportType = parser.nextInt(0);
+ if (type.equals("NMR")) {
+ position.set(Position.KEY_MOTION, reportType == 1);
+ } else if (type.equals("SOS")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
+ channel.write("+SACK:" + parser.next() + "$", remoteAddress);
+ }
+
+ return position;
+ }
+
+ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_BASIC, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int hdop = parser.nextInt(0);
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(2)) {
+ position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble(0));
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ position.setTime(parser.nextDateTime());
+ }
+
+ if (parser.hasNext(4)) {
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
+
+ decodeDeviceTime(position, parser);
+
+ switch (type) {
+ case "PNA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
+ break;
+ case "PFA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+ break;
+ case "EPN":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case "EPF":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case "BPL":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case "STT":
+ position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
+ break;
+ case "SWG":
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
+ break;
+ case "TMP":
+ case "TEM":
+ position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
+ break;
+ case "JDR":
+ case "JDS":
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ break;
+ default:
+ break;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = ((ChannelBuffer) msg).toString(StandardCharsets.US_ASCII);
+
+ int typeIndex = sentence.indexOf(":GT");
+ if (typeIndex < 0) {
+ return null;
+ }
+
+ Object result;
+ String type = sentence.substring(typeIndex + 3, typeIndex + 6);
+ if (sentence.startsWith("+ACK")) {
+ result = decodeAck(channel, remoteAddress, sentence, type);
+ } else {
+ switch (type) {
+ case "INF":
+ result = decodeInf(channel, remoteAddress, sentence);
+ break;
+ case "OBD":
+ result = decodeObd(channel, remoteAddress, sentence);
+ break;
+ case "FRI":
+ result = decodeFri(channel, remoteAddress, sentence);
+ break;
+ case "ERI":
+ result = decodeEri(channel, remoteAddress, sentence);
+ break;
+ case "IGN":
+ case "IGF":
+ result = decodeIgn(channel, remoteAddress, sentence);
+ break;
+ case "IDA":
+ result = decodeIda(channel, remoteAddress, sentence);
+ break;
+ case "WIF":
+ result = decodeWif(channel, remoteAddress, sentence);
+ break;
+ case "GSM":
+ result = decodeGsm(channel, remoteAddress, sentence);
+ break;
+ case "VER":
+ result = decodeVer(channel, remoteAddress, sentence);
+ break;
+ default:
+ result = decodeOther(channel, remoteAddress, sentence, type);
+ break;
+ }
+
+ if (result == null) {
+ result = decodeBasic(channel, remoteAddress, sentence, type);
+ }
+
+ if (result != null) {
+ if (result instanceof Position) {
+ ((Position) result).set(Position.KEY_TYPE, type);
+ } else {
+ for (Position p : (List<Position>) result) {
+ p.set(Position.KEY_TYPE, type);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/org/traccar/protocol/GnxProtocolDecoder.java b/src/org/traccar/protocol/GnxProtocolDecoder.java
index 070d394e8..2274ec164 100644
--- a/src/org/traccar/protocol/GnxProtocolDecoder.java
+++ b/src/org/traccar/protocol/GnxProtocolDecoder.java
@@ -102,7 +102,7 @@ public class GnxProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
if (type.equals("MIF")) {
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
}
return position;
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
index f5ba3cff7..099047aa0 100644
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 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.
@@ -248,6 +248,8 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, alarm.substring(2));
} else if (alarm.startsWith("oil ")) {
position.set("oil", alarm.substring(4));
+ } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) {
+ position.set(Position.KEY_EVENT, alarm);
}
DateBuilder dateBuilder = new DateBuilder()
@@ -258,7 +260,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
String rfid = parser.next();
if (alarm.equals("rfid")) {
- position.set(Position.KEY_RFID, rfid);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
}
String utcHours = parser.next();
diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/org/traccar/protocol/Gt06FrameDecoder.java
index f35af6572..c8b5e56ae 100644
--- a/src/org/traccar/protocol/Gt06FrameDecoder.java
+++ b/src/org/traccar/protocol/Gt06FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2017 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,6 @@ public class Gt06FrameDecoder extends FrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
- // Check minimum length
if (buf.readableBytes() < 5) {
return null;
}
@@ -35,21 +34,22 @@ public class Gt06FrameDecoder extends FrameDecoder {
if (buf.getByte(buf.readerIndex()) == 0x78) {
length += 1 + buf.getUnsignedByte(buf.readerIndex() + 2);
-
- int type = buf.getUnsignedByte(buf.readerIndex() + 3);
- if (type == Gt06ProtocolDecoder.MSG_STATUS && length == 13) {
- length += 2; // workaround for #1727
- }
-
} else {
length += 2 + buf.getUnsignedShort(buf.readerIndex() + 2);
}
- // Check length and return buffer
- if (buf.readableBytes() >= length) {
+ if (buf.readableBytes() >= length && buf.getUnsignedShort(buf.readerIndex() + length - 2) == 0x0d0a) {
return buf.readBytes(length);
}
+ int endIndex = buf.readerIndex() - 1;
+ do {
+ endIndex = buf.indexOf(endIndex + 1, buf.writerIndex(), (byte) 0x0d);
+ if (endIndex > 0 && buf.writerIndex() > endIndex + 1 && buf.getByte(endIndex + 1) == 0x0a) {
+ return buf.readBytes(endIndex + 2 - buf.readerIndex());
+ }
+ } while (endIndex > 0);
+
return null;
}
diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
index 24bedcabf..fbd1adfc6 100644
--- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -31,6 +31,7 @@ import org.traccar.model.CellTower;
import org.traccar.model.Device;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -68,6 +69,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GPS_LBS_STATUS_1 = 0x16;
public static final int MSG_GPS_LBS_STATUS_2 = 0x26;
public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
+ public static final int MSG_LBS_MULTIPLE = 0x28;
+ public static final int MSG_LBS_WIFI = 0x2C;
public static final int MSG_LBS_PHONE = 0x17;
public static final int MSG_LBS_EXTEND = 0x18;
public static final int MSG_LBS_STATUS = 0x19;
@@ -152,9 +155,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return false;
}
- int length = buf.readUnsignedByte();
- position.set(Position.KEY_SATELLITES, BitUtil.to(length, 4));
- length = BitUtil.from(length, 4);
+ position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4));
double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
@@ -178,10 +179,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, BitUtil.check(flags, 15));
}
- if (length > 0) {
- buf.skipBytes(length - 12); // skip reserved
- }
-
return true;
}
@@ -199,7 +196,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedShort(), buf.readUnsignedMedium())));
if (length > 0) {
- buf.skipBytes(length - 8);
+ buf.skipBytes(length - (hasLength ? 9 : 8));
}
return true;
@@ -336,7 +333,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
buf.readUnsignedShort(); // type
- // Timezone offset
if (dataLength > 10) {
int extensionBits = buf.readUnsignedShort();
int hours = (extensionBits >> 4) / 100;
@@ -391,77 +387,104 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else {
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- if (type == MSG_LBS_EXTEND) {
+ return decodeBasicOther(channel, buf, deviceSession, type, dataLength);
- DateBuilder dateBuilder = new DateBuilder(timeZone)
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ }
- getLastLocation(position, dateBuilder.getDate());
+ return null;
+ }
- int mcc = buf.readUnsignedShort();
- int mnc = buf.readUnsignedByte();
+ private Object decodeBasicOther(Channel channel, ChannelBuffer buf,
+ DeviceSession deviceSession, int type, int dataLength) throws Exception {
- Network network = new Network();
- for (int i = 0; i < 7; i++) {
- network.addCellTower(CellTower.from(
- mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedMedium(), -buf.readUnsignedByte()));
- }
- position.setNetwork(network);
+ Position position = new Position();
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setProtocol(getProtocolName());
- } else if (type == MSG_STRING) {
+ if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI) {
- getLastLocation(position, null);
+ DateBuilder dateBuilder = new DateBuilder(timeZone)
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- int commandLength = buf.readUnsignedByte();
+ getLastLocation(position, dateBuilder.getDate());
- if (commandLength > 0) {
- buf.readUnsignedByte(); // server flag (reserved)
- position.set(Position.KEY_RESULT,
- buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII));
+ int mcc = buf.readUnsignedShort();
+ int mnc = buf.readUnsignedByte();
+ Network network = new Network();
+ for (int i = 0; i < 7; i++) {
+ int lac = buf.readUnsignedShort();
+ int cid = buf.readUnsignedMedium();
+ int rssi = -buf.readUnsignedByte();
+ if (lac > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
}
+ }
- } else if (isSupported(type)) {
+ buf.readUnsignedByte(); // time leads
- if (hasGps(type)) {
- decodeGps(position, buf, false);
- } else {
- getLastLocation(position, null);
+ if (type != MSG_LBS_MULTIPLE) {
+ int wifiCount = buf.readUnsignedByte();
+ for (int i = 0; i < wifiCount; i++) {
+ String mac = ChannelBuffers.hexDump(buf.readBytes(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
}
+ }
- if (hasLbs(type)) {
- decodeLbs(position, buf, hasStatus(type));
- }
+ position.setNetwork(network);
- if (hasStatus(type)) {
- decodeStatus(position, buf);
- }
+ } else if (type == MSG_STRING) {
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 4 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
+ getLastLocation(position, null);
+ int commandLength = buf.readUnsignedByte();
+
+ if (commandLength > 0) {
+ buf.readUnsignedByte(); // server flag (reserved)
+ position.set(Position.KEY_RESULT,
+ buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII));
+ }
+
+ } else if (isSupported(type)) {
+
+ if (hasGps(type)) {
+ decodeGps(position, buf, false);
} else {
+ getLastLocation(position, null);
+ }
- buf.skipBytes(dataLength);
- if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
- sendResponse(channel, false, type);
- }
- return null;
+ if (hasLbs(type)) {
+ decodeLbs(position, buf, hasStatus(type));
+ }
+ if (hasStatus(type)) {
+ decodeStatus(position, buf);
}
- sendResponse(channel, false, type);
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() >= 4 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
- return position;
+ if (type == MSG_GPS_LBS_2 && buf.readableBytes() >= 3 + 6) {
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason
+ position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0);
+ }
+
+ } else {
+
+ buf.skipBytes(dataLength);
+ if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
+ sendResponse(channel, false, type);
+ }
+ return null;
}
- return null;
+ sendResponse(channel, false, type);
+
+ return position;
}
private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception {
@@ -509,7 +532,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (subType == 0x05) {
int flags = buf.readUnsignedByte();
- position.set("door", BitUtil.check(flags, 0));
+ position.set(Position.KEY_DOOR, BitUtil.check(flags, 0));
position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2));
return position;
@@ -533,7 +556,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
if (photo.writableBytes() > 0) {
sendPhotoRequest(channel, pictureId);
} else {
- Device device = Context.getDeviceManager().getDeviceById(deviceSession.getDeviceId());
+ Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId());
Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg");
photos.remove(pictureId);
}
@@ -544,7 +567,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, position.getDeviceTime());
}
- decodeLbs(position, buf, true);
+ if (decodeLbs(position, buf, true)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
buf.skipBytes(buf.readUnsignedByte()); // additional cell towers
buf.skipBytes(buf.readUnsignedByte()); // wifi access point
diff --git a/src/org/traccar/protocol/H02Protocol.java b/src/org/traccar/protocol/H02Protocol.java
index df64402f8..66965e9db 100644
--- a/src/org/traccar/protocol/H02Protocol.java
+++ b/src/org/traccar/protocol/H02Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.
@@ -15,6 +15,7 @@
*/
package org.traccar.protocol;
+import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.string.StringEncoder;
@@ -50,5 +51,13 @@ public class H02Protocol extends BaseProtocol {
pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
}
});
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new H02ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
+ }
+ });
}
}
diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java
index aa3d47650..4414870d2 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/H02ProtocolDecoder.java
@@ -150,10 +150,22 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.text("*")
.expression("..,") // manufacturer
.number("(d+),") // imei
- .expression("[^,]+,")
- .any()
+ .groupBegin()
+ .text("VP1,")
+ .or()
+ .groupBegin()
+ .text("V4,")
+ .expression("(.*),") // response
+ .or()
+ .expression("V[^,]*,")
+ .groupEnd()
.number("(?:(dd)(dd)(dd))?,") // time (hhmmss)
- .expression("([AV])?,") // validity
+ .groupEnd()
+ .groupBegin()
+ .expression("([ABV])?,") // validity
+ .or()
+ .number("(d+),") // coding scheme
+ .groupEnd()
.groupBegin()
.number("-(d+)-(d+.d+),") // latitude
.or()
@@ -169,25 +181,28 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(?:(dd)(dd)(dd))?") // date (ddmmyy)
- .any()
- .number(",(x{8})") // status
+ .groupBegin()
+ .expression(",[^,]*,")
+ .expression("[^,]*,")
+ .expression("[^,]*") // sim info
+ .groupEnd("?")
+ .groupBegin()
+ .number(",(x{8})")
.groupBegin()
.number(",(d+),") // odometer
.number("(-?d+),") // temperature
.number("(d+.d+),") // fuel
.number("(-?d+),") // altitude
.number("(x+),") // lac
- .number("(x+)#") // cid
+ .number("(x+)") // cid
.or()
- .number(",(d+),")
- .number("(d+),")
- .number("(d+),")
- .number("(d+)#")
+ .text(",")
+ .expression("(.*)") // data
.or()
- .expression(",.*")
+ .groupEnd()
.or()
- .text("#")
.groupEnd()
+ .text("#")
.compile();
private static final Pattern PATTERN_NBR = new PatternBuilder()
@@ -222,6 +237,24 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_V3 = new PatternBuilder()
+ .text("*")
+ .expression("..,") // manufacturer
+ .number("(d+),") // imei
+ .text("V3,")
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(ddd)") // mcc
+ .number("(d+),") // mnc
+ .number("(d+),") // count
+ .expression("(.*),") // cell info
+ .number("(x{4}),") // battery
+ .number("d+,") // reboot info
+ .text("X,")
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(x{8})") // status
+ .text("#").optional()
+ .compile();
+
private Position decodeText(String sentence, Channel channel, SocketAddress remoteAddress) {
Parser parser = new Parser(PATTERN, sentence);
@@ -238,6 +271,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_RESULT, parser.next());
+ }
+
DateBuilder dateBuilder = new DateBuilder();
if (parser.hasNext(3)) {
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -246,6 +283,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
position.setValid(parser.next().equals("A"));
}
+ if (parser.hasNext()) {
+ parser.nextInt(); // coding scheme
+ position.setValid(true);
+ }
if (parser.hasNext(2)) {
position.setLatitude(-parser.nextCoordinate());
@@ -271,7 +312,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setTime(new Date());
}
- processStatus(position, parser.nextLong(16, 0));
+ if (parser.hasNext()) {
+ processStatus(position, parser.nextLong(16, 0));
+ }
if (parser.hasNext(6)) {
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
@@ -284,8 +327,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext(4)) {
- for (int i = 1; i <= 4; i++) {
- position.set(Position.PREFIX_IO + i, parser.nextInt(0));
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < values.length; i++) {
+ position.set(Position.PREFIX_IO + (i + 1), values[i].trim());
}
}
@@ -354,7 +398,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextInt());
position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- position.set("steps", parser.nextInt());
+ position.set(Position.KEY_STEPS, parser.nextInt());
position.set("turnovers", parser.nextInt());
dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -366,6 +410,48 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeV3(String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_V3, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+
+ int count = parser.nextInt();
+ Network network = new Network();
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < count; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, Integer.parseInt(values[i * 4]), Integer.parseInt(values[i * 4 + 1])));
+ }
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY, parser.nextHexInt());
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ processStatus(position, parser.nextLong(16, 0));
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -385,6 +471,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return decodeLbs(sentence, channel, remoteAddress);
case "LINK":
return decodeLink(sentence, channel, remoteAddress);
+ case "V3":
+ return decodeV3(sentence, channel, remoteAddress);
default:
return decodeText(sentence, channel, remoteAddress);
}
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
index 2bd02d3f1..f76fd8069 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 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.
@@ -21,6 +21,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -31,6 +32,8 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
import java.util.regex.Pattern;
public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
@@ -45,10 +48,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes / 60;
}
- private Position decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private List<Position> decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ List<Position> positions = new LinkedList<>();
buf.readByte(); // header
@@ -59,97 +61,106 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
if (deviceSession == null) {
return null;
}
- position.setDeviceId(deviceSession.getDeviceId());
if (longFormat) {
buf.readUnsignedByte(); // protocol
}
- int version = buf.readUnsignedByte() >> 4;
+ int version = BitUtil.from(buf.readUnsignedByte(), 4);
buf.readUnsignedShort(); // length
- DateBuilder dateBuilder = new DateBuilder()
- .setDay(BcdUtil.readInteger(buf, 2))
- .setMonth(BcdUtil.readInteger(buf, 2))
- .setYear(BcdUtil.readInteger(buf, 2))
- .setHour(BcdUtil.readInteger(buf, 2))
- .setMinute(BcdUtil.readInteger(buf, 2))
- .setSecond(BcdUtil.readInteger(buf, 2));
- position.setTime(dateBuilder.getDate());
-
- double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8));
- double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9));
-
- byte flags = buf.readByte();
- position.setValid((flags & 0x1) == 0x1);
- if ((flags & 0x2) == 0) {
- latitude = -latitude;
- }
- position.setLatitude(latitude);
- if ((flags & 0x4) == 0) {
- longitude = -longitude;
- }
- position.setLongitude(longitude);
+ while (buf.readableBytes() > 1) {
- position.setSpeed(BcdUtil.readInteger(buf, 2));
- position.setCourse(buf.readUnsignedByte() * 2.0);
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
- if (longFormat) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setYear(BcdUtil.readInteger(buf, 2))
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2));
+ position.setTime(dateBuilder.getDate());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8));
+ double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9));
- buf.readUnsignedInt(); // vehicle id combined
+ byte flags = buf.readByte();
+ position.setValid((flags & 0x1) == 0x1);
+ if ((flags & 0x2) == 0) {
+ latitude = -latitude;
+ }
+ position.setLatitude(latitude);
+ if ((flags & 0x4) == 0) {
+ longitude = -longitude;
+ }
+ position.setLongitude(longitude);
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ position.setSpeed(BcdUtil.readInteger(buf, 2));
+ position.setCourse(buf.readUnsignedByte() * 2.0);
- int battery = buf.readUnsignedByte();
- if (battery == 0xff) {
- position.set(Position.KEY_CHARGE, true);
- } else {
- position.set(Position.KEY_BATTERY_LEVEL, battery);
- }
+ if (longFormat) {
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort());
- cellTower.setSignalStrength((int) buf.readUnsignedByte());
- position.setNetwork(new Network(cellTower));
+ buf.readUnsignedInt(); // vehicle id combined
- position.set(Position.KEY_INDEX, buf.readUnsignedByte());
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
- } else if (version == 1) {
+ int battery = buf.readUnsignedByte();
+ if (battery == 0xff) {
+ position.set(Position.KEY_CHARGE, true);
+ } else {
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ }
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- position.set(Position.KEY_POWER, buf.readUnsignedByte());
+ CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort());
+ cellTower.setSignalStrength((int) buf.readUnsignedByte());
+ position.setNetwork(new Network(cellTower));
- buf.readByte(); // other flags and sensors
+ } else if (version == 1) {
- position.setAltitude(buf.readUnsignedShort());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedByte());
- int cid = buf.readUnsignedShort();
- int lac = buf.readUnsignedShort();
- int rssi = buf.readUnsignedByte();
+ buf.readByte(); // other flags and sensors
- if (cid != 0 && lac != 0) {
- CellTower cellTower = CellTower.fromCidLac(cid, lac);
- cellTower.setSignalStrength(rssi);
- position.setNetwork(new Network(cellTower));
- } else {
- position.set(Position.KEY_RSSI, rssi);
- }
+ position.setAltitude(buf.readUnsignedShort());
+
+ int cid = buf.readUnsignedShort();
+ int lac = buf.readUnsignedShort();
+ int rssi = buf.readUnsignedByte();
+
+ if (cid != 0 && lac != 0) {
+ CellTower cellTower = CellTower.fromCidLac(cid, lac);
+ cellTower.setSignalStrength(rssi);
+ position.setNetwork(new Network(cellTower));
+ } else {
+ position.set(Position.KEY_RSSI, rssi);
+ }
- } else if (version == 2) {
+ } else if (version == 2) {
- int fuel = buf.readUnsignedByte() << 8;
+ int fuel = buf.readUnsignedByte() << 8;
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
+ position.set(Position.KEY_STATUS, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
- fuel += buf.readUnsignedByte();
- position.set(Position.KEY_FUEL_LEVEL, fuel);
+ fuel += buf.readUnsignedByte();
+ position.set(Position.KEY_FUEL_LEVEL, fuel);
+
+ }
+
+ positions.add(position);
}
- return position;
+ buf.readUnsignedByte(); // index
+
+ return positions;
}
private static final Pattern PATTERN_W01 = new PatternBuilder()
diff --git a/src/org/traccar/protocol/Jt600ProtocolEncoder.java b/src/org/traccar/protocol/Jt600ProtocolEncoder.java
index 0bf389460..377f104a3 100644
--- a/src/org/traccar/protocol/Jt600ProtocolEncoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
@@ -29,7 +31,7 @@ public class Jt600ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ENGINE_RESUME:
return "(S07,1)";
case Command.TYPE_SET_TIMEZONE:
- int offset = command.getInteger(Command.KEY_TIMEZONE) / 60;
+ int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
return "(S09,1," + offset + ")";
case Command.TYPE_REBOOT_DEVICE:
return "(S17)";
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java
index 15a384cc0..3ef52acd1 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -267,7 +267,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.or().text(" ")
.groupEnd("?").text(",")
.number("(d+)?,") // rfid
- .number("d*,")
+ .expression("[^,]*,")
.number("(d+)?,") // battery
.expression("([^,]*);") // alert
.any()
@@ -280,13 +280,13 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.next().equals("S")) {
@@ -327,7 +327,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
String battery = parser.next();
if (battery != null) {
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
index 44e01d5e0..e41a42843 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -94,8 +94,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+),") // single fuel consumption
.number("(d+.d+),") // total fuel consumption
.number("(d+),") // error code count
- .number("(d+),") // harsh acceleration count
- .number("(d+)") // harsh break count
+ .number("(d+),") // hard acceleration count
+ .number("(d+)") // hard brake count
.compile();
private static final Pattern PATTERN_OBDA = new PatternBuilder()
@@ -106,8 +106,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // average speed
.number("(d+),") // history highest speed
.number("(d+),") // history highest rpm
- .number("(d+),") // total harsh acceleration
- .number("(d+)") // total harsh break n0
+ .number("(d+),") // total hard acceleration
+ .number("(d+)") // total hard brake
.compile();
public static final int MSG_HEARTBEAT = 0x0001;
@@ -194,10 +194,16 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_MOVEMENT;
case 0x13:
return Position.ALARM_GEOFENCE_ENTER;
+ case 0x14:
+ return Position.ALARM_ACCIDENT;
case 0x50:
return Position.ALARM_POWER_OFF;
case 0x53:
return Position.ALARM_GPS_ANTENNA_CUT;
+ case 0x72:
+ return Position.ALARM_BRAKING;
+ case 0x73:
+ return Position.ALARM_ACCELERATION;
default:
return null;
}
@@ -253,7 +259,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext()) {
- position.set(Position.KEY_RFID, parser.nextHexInt(0));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(parser.nextHexInt(0)));
}
return position;
@@ -295,8 +301,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set("singleFuelConsumption", parser.nextDouble());
position.set("totalFuelConsumption", parser.nextDouble());
position.set(Position.KEY_DTCS, parser.nextInt());
- position.set("harshAcelerationNo", parser.nextInt());
- position.set("harshBreakerNo", parser.nextInt());
+ position.set("hardAccelerationCount", parser.nextInt());
+ position.set("hardBrakingCount", parser.nextInt());
return position;
}
@@ -353,7 +359,13 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
if (command == MSG_ALARM) {
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ short alarmCode = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarmCode));
+ if (alarmCode >= 0x02 && alarmCode <= 0x05) {
+ position.set(Position.PREFIX_IN + alarmCode, 1);
+ } else if (alarmCode >= 0x32 && alarmCode <= 0x35) {
+ position.set(Position.PREFIX_IN + (alarmCode - 0x30), 0);
+ }
} else if (command == MSG_POSITION_LOGGED) {
buf.skipBytes(6);
}
@@ -370,7 +382,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
if (rfid != 0) {
String card = String.format("%010d", rfid);
position.set("card" + (i + 1), card);
- position.set(Position.KEY_RFID, card);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, card);
}
}
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
index 268bae392..2e0a1e84c 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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.model.Command;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
+import java.util.TimeZone;
public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
@@ -78,7 +79,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
content.writeShort(command.getInteger(Command.KEY_RADIUS));
return encodeContent(command.getDeviceId(), MSG_MOVEMENT_ALARM, content);
case Command.TYPE_SET_TIMEZONE:
- int offset = command.getInteger(Command.KEY_TIMEZONE) / 60;
+ int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
content.writeBytes(String.valueOf(offset).getBytes(StandardCharsets.US_ASCII));
return encodeContent(command.getDeviceId(), MSG_TIME_ZONE, content);
case Command.TYPE_REBOOT_DEVICE:
diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
index 38ecde519..efc9c24db 100644
--- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -20,6 +20,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -69,16 +70,15 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
.number("(x+)|") // battery
- .number("(x+),") // power
+ .number("(x+)?,") // power
.groupBegin()
.expression("([^,]+)?,") // event specific
.expression("[^,]*,") // reserved
- .number("d*,") // protocol
+ .number("(d+)?,") // protocol
.number("(x{4})?") // fuel
.number("(?:,(x{6}(?:|x{6})*))?") // temperature
- .or()
+ .groupEnd("?")
.any()
- .groupEnd()
.text("*")
.number("xx")
.text("\r\n").optional()
@@ -160,7 +160,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
}
- String deviceModel = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getModel();
+ String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
if (deviceModel == null) {
deviceModel = "";
}
@@ -201,7 +201,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
if (eventData != null && !eventData.isEmpty()) {
switch (event) {
case 37:
- position.set(Position.KEY_RFID, eventData);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, eventData);
break;
default:
position.set("eventData", eventData);
@@ -209,6 +209,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
}
+ int protocol = parser.nextInt(0);
+
if (parser.hasNext()) {
String fuel = parser.next();
position.set(Position.KEY_FUEL_LEVEL,
@@ -218,9 +220,14 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
for (String temp : parser.next().split("\\|")) {
int index = Integer.valueOf(temp.substring(0, 2), 16);
- double value = Byte.valueOf(temp.substring(2, 4), 16);
- value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16);
- position.set(Position.PREFIX_TEMP + index, value);
+ if (protocol >= 3) {
+ double value = Short.valueOf(temp.substring(2), 16);
+ position.set(Position.PREFIX_TEMP + index, value * 0.01);
+ } else {
+ double value = Byte.valueOf(temp.substring(2, 4), 16);
+ value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16);
+ position.set(Position.PREFIX_TEMP + index, value);
+ }
}
}
@@ -307,7 +314,6 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
- // Find type
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
index = buf.indexOf(index + 1, buf.writerIndex(), (byte) ',');
@@ -316,8 +322,12 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case "D03":
if (channel != null) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- String imei = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getUniqueId();
- channel.write("@@O46," + imei + ",D00,camera_picture.jpg,0*00\r\n");
+ String imei = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
+ String content = "D00,camera_picture.jpg,0";
+ int length = 1 + imei.length() + 1 + content.length() + 5;
+ String response = String.format("@@O%02d,%s,%s*", length, imei, content);
+ response += Checksum.sum(response) + "\r\n";
+ channel.write(response);
}
return null;
case "CCC":
diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 8bfb4fb36..05994b697 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -133,7 +133,11 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
if (sentence.startsWith("!1,")) {
- getDeviceSession(channel, remoteAddress, sentence.substring(3, sentence.length()));
+ int index = sentence.indexOf(',', 3);
+ if (index < 0) {
+ index = sentence.length();
+ }
+ getDeviceSession(channel, remoteAddress, sentence.substring(3, index));
return null;
}
diff --git a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
index e5c43e29a..486f406a5 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
+++ b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
@@ -27,7 +29,7 @@ public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements
if (key.equals(Command.KEY_ENABLE)) {
return (Boolean) value ? "1" : "0";
} else if (key.equals(Command.KEY_TIMEZONE)) {
- return String.format("%+03d", ((Number) value).longValue() / 3600);
+ return String.format("%+03d", TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
} else if (key.equals(Command.KEY_INDEX)) {
switch (((Number) value).intValue()) {
case 0:
diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java
index 49987ce57..6d82e4a4b 100644
--- a/src/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/org/traccar/protocol/MxtProtocolDecoder.java
@@ -159,7 +159,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(infoGroups, 7)) {
- position.set(Position.KEY_RFID, buf.readUnsignedInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt()));
}
buf.readerIndex(buf.writerIndex() - 3);
diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java
index 15f6f40b8..15a71c88b 100644
--- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ b/src/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2017 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,7 +110,7 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(Double.parseDouble(location[1]));
break;
case "speed":
- position.setSpeed(Double.parseDouble(value));
+ position.setSpeed(convertSpeed(Double.parseDouble(value), "kn"));
break;
case "bearing":
case "heading":
@@ -128,11 +128,24 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder {
case "batt":
position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
break;
+ case "driverUniqueId":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
+ break;
default:
try {
position.set(entry.getKey(), Double.parseDouble(value));
} catch (NumberFormatException e) {
- position.set(entry.getKey(), value);
+ switch (value) {
+ case "true":
+ position.set(entry.getKey(), true);
+ break;
+ case "false":
+ position.set(entry.getKey(), false);
+ break;
+ default:
+ position.set(entry.getKey(), value);
+ break;
+ }
}
break;
}
diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
index b1851f8ca..fef5d9b39 100644
--- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -67,7 +67,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
case "HDA":
return Position.ALARM_ACCELERATION;
case "HDB":
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case "FDA":
return Position.ALARM_FATIGUE_DRIVING;
case "SKA":
@@ -129,7 +129,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
}
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
int value = parser.nextHexInt(0);
diff --git a/src/org/traccar/protocol/Pt502ProtocolEncoder.java b/src/org/traccar/protocol/Pt502ProtocolEncoder.java
index 5f7665e50..4a876f6da 100644
--- a/src/org/traccar/protocol/Pt502ProtocolEncoder.java
+++ b/src/org/traccar/protocol/Pt502ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -15,11 +15,27 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
-public class Pt502ProtocolEncoder extends StringProtocolEncoder {
+public class Pt502ProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+
+ @Override
+ public String formatValue(String key, Object value) {
+ if (key.equals(Command.KEY_TIMEZONE)) {
+ return String.valueOf(TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected String formatCommand(Command command, String format, String... keys) {
+ return formatCommand(command, format, this, keys);
+ }
@Override
protected Object encodeCommand(Command command) {
diff --git a/src/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/org/traccar/protocol/RuptelaProtocolDecoder.java
index ac95ed27a..8752d30c0 100644
--- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -74,6 +74,43 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private long readValue(ChannelBuffer buf, int length, boolean signed) {
+ switch (length) {
+ case 1:
+ return signed ? buf.readByte() : buf.readUnsignedByte();
+ case 2:
+ return signed ? buf.readShort() : buf.readUnsignedShort();
+ case 4:
+ return signed ? buf.readInt() : buf.readUnsignedInt();
+ default:
+ return buf.readLong();
+ }
+ }
+
+ private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) {
+ switch (id) {
+ case 2:
+ case 3:
+ case 4:
+ position.set("di" + (id - 1), readValue(buf, length, false));
+ break;
+ case 5:
+ position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
+ break;
+ case 74:
+ position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
+ break;
+ case 78:
+ case 79:
+ case 80:
+ position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1);
+ break;
+ default:
+ position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ break;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -133,28 +170,28 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedByte());
+ decodeParameter(position, id, buf, 1);
}
// Read 2 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedShort());
+ decodeParameter(position, id, buf, 2);
}
// Read 4 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedInt());
+ decodeParameter(position, id, buf, 4);
}
// Read 8 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readLong());
+ decodeParameter(position, id, buf, 8);
}
positions.add(position);
diff --git a/src/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
index e90dde455..79f013fac 100644
--- a/src/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -69,6 +69,29 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
return value.charAt(0) == '+' ? result : -result;
}
+ private String decodeAlarm(int event) {
+ switch (event) {
+ case 6:
+ return Position.ALARM_OVERSPEED;
+ case 7:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 8:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 9:
+ return Position.ALARM_POWER_CUT;
+ case 11:
+ return Position.ALARM_LOW_BATTERY;
+ case 26:
+ return Position.ALARM_TOW;
+ case 36:
+ return Position.ALARM_SOS;
+ case 42:
+ return Position.ALARM_JAMMING;
+ default:
+ return null;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -109,6 +132,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
break;
case "#EID#":
event = Integer.parseInt(data[i]);
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
position.set(Position.KEY_EVENT, event);
break;
case "#PDT#":
@@ -196,7 +220,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
if (rfid.matches("0+")) {
rfid = data[data.length - 2];
}
- position.set(Position.KEY_RFID, rfid);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
}
return position;
diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
index c81e83aab..26817a5c8 100644
--- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
@@ -104,7 +104,7 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
position.set(Position.KEY_INPUT, parser.nextInt(0));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
position.set(Position.PREFIX_TEMP + 1, parser.nextInt(0));
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt(0));
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
index 42e81f60c..6dfc6f77f 100644
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -19,6 +19,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -32,18 +33,34 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
private int protocolType;
private boolean hbm;
+ private boolean includeAdc;
+ private boolean includeTemp;
public SuntechProtocolDecoder(SuntechProtocol protocol) {
super(protocol);
protocolType = Context.getConfig().getInteger(getProtocolName() + ".protocolType");
hbm = Context.getConfig().getBoolean(getProtocolName() + ".hbm");
+ includeAdc = Context.getConfig().getBoolean(getProtocolName() + ".includeAdc");
+ includeTemp = Context.getConfig().getBoolean(getProtocolName() + ".includeTemp");
}
public void setProtocolType(int protocolType) {
this.protocolType = protocolType;
}
+ public void setHbm(boolean hbm) {
+ this.hbm = hbm;
+ }
+
+ public void setIncludeAdc(boolean includeAdc) {
+ this.includeAdc = includeAdc;
+ }
+
+ public void setIncludeTemp(boolean includeTemp) {
+ this.includeTemp = includeTemp;
+ }
+
private Position decode9(
Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
int index = 1;
@@ -54,19 +71,19 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
Position position = new Position();
position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
if (type.equals("Emergency") || type.equals("Alert")) {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
if (!type.equals("Alert") || protocolType == 0) {
position.set(Position.KEY_VERSION_FW, values[index++]);
}
@@ -87,13 +104,60 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setValid(values[index++].equals("1"));
if (protocolType == 1) {
- position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index]));
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
}
return position;
}
- private Position decode23(
+ private String decodeEmergency(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 2:
+ return Position.ALARM_PARKING;
+ case 3:
+ return Position.ALARM_POWER_CUT;
+ case 5:
+ case 6:
+ return Position.ALARM_DOOR;
+ case 7:
+ return Position.ALARM_MOVEMENT;
+ case 8:
+ return Position.ALARM_SHOCK;
+ default:
+ return null;
+ }
+ }
+
+ private String decodeAlert(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_OVERSPEED;
+ case 5:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 6:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 14:
+ return Position.ALARM_LOW_BATTERY;
+ case 15:
+ return Position.ALARM_SHOCK;
+ case 16:
+ return Position.ALARM_ACCIDENT;
+ case 46:
+ return Position.ALARM_ACCELERATION;
+ case 47:
+ return Position.ALARM_BRAKING;
+ case 48:
+ return Position.ALARM_ACCIDENT;
+ case 50:
+ return Position.ALARM_JAMMING;
+ default:
+ return null;
+ }
+ }
+
+ private Position decode235(
Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException {
int index = 0;
@@ -103,20 +167,17 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- if (type.equals("EMG") || type.equals("ALT")) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
- if (protocol.equals("ST300")) {
+ if (protocol.equals("ST300") || protocol.equals("ST500")) {
index += 1; // model
}
@@ -126,7 +187,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
position.setTime(dateFormat.parse(values[index++] + values[index++]));
- index += 1; // cell
+ if (!protocol.equals("ST500")) {
+ index += 1; // cell
+ }
position.setLatitude(Double.parseDouble(values[index++]));
position.setLongitude(Double.parseDouble(values[index++]));
@@ -140,12 +203,32 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
- position.set(Position.PREFIX_IO + 1, values[index++]);
-
- index += 1; // mode
+ String io = values[index++];
+ if (io.length() == 6) {
+ position.set(Position.KEY_IGNITION, io.charAt(0) == '1');
+ position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1');
+ position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1');
+ position.set(Position.PREFIX_IN + 3, io.charAt(3) == '1');
+ position.set(Position.PREFIX_OUT + 1, io.charAt(4) == '1');
+ position.set(Position.PREFIX_OUT + 2, io.charAt(5) == '1');
+ }
- if (type.equals("STT")) {
- position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ switch (type) {
+ case "STT":
+ index += 1; // mode
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ break;
+ case "EMG":
+ position.set(Position.KEY_ALARM, decodeEmergency(Integer.parseInt(values[index++])));
+ break;
+ case "EVT":
+ position.set(Position.KEY_EVENT, Integer.parseInt(values[index++]));
+ break;
+ case "ALT":
+ position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
+ break;
+ default:
+ break;
}
if (hbm) {
@@ -155,7 +238,35 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
if (index < values.length) {
- position.set(Position.KEY_BATTERY, Double.parseDouble(values[index]));
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
+ }
+
+ if (index < values.length && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (includeAdc) {
+ position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++]));
+ position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++]));
+ position.set(Position.PREFIX_ADC + 3, Double.parseDouble(values[index++]));
+ }
+
+ if (values.length - index >= 2) {
+ String driverUniqueId = values[index++];
+ if (values[index++].equals("1") && !driverUniqueId.isEmpty()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
+ }
+ }
+
+ if (includeTemp) {
+ for (int i = 1; i <= 3; i++) {
+ String temperature = values[index++];
+ String value = temperature.substring(temperature.indexOf(':') + 1);
+ if (!value.isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, Double.parseDouble(value));
+ }
+ }
+
}
}
@@ -163,18 +274,105 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeUniversal(
+ Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ int index = 0;
+
+ String type = values[index++];
+
+ if (!type.equals("STT")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
+ int mask = Integer.parseInt(values[index++], 16);
+
+ if (BitUtil.check(mask, 1)) {
+ index += 1; // model
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+ }
+
+ if (BitUtil.check(mask, 3) && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+ }
+
+ if (BitUtil.check(mask, 6)) {
+ index += 1; // cell
+ }
+
+ if (BitUtil.check(mask, 7)) {
+ index += 1; // mcc
+ }
+
+ if (BitUtil.check(mask, 8)) {
+ index += 1; // mnc
+ }
+
+ if (BitUtil.check(mask, 9)) {
+ index += 1; // lac
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 11)) {
+ position.setLatitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 12)) {
+ position.setLongitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(values[index++].equals("1"));
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String[] values = ((String) msg).split(";");
- String protocol = values[0].substring(0, 5);
-
- if (protocol.equals("ST910")) {
+ if (values[0].length() < 5) {
+ return decodeUniversal(channel, remoteAddress, values);
+ } else if (values[0].equals("ST910")) {
return decode9(channel, remoteAddress, values);
} else {
- return decode23(channel, remoteAddress, protocol, values);
+ return decode235(channel, remoteAddress, values[0].substring(0, 5), values);
}
}
diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java
index dee7210b1..6b4ee6ebd 100644
--- a/src/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/org/traccar/protocol/T55ProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -98,8 +99,11 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
private Position decodeGprmc(
DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) {
- if (channel != null && !(channel instanceof DatagramChannel)) {
- channel.write("OK1\r\n");
+ if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) {
+ if (Context.getIdentityManager().lookupAttributeBoolean(
+ deviceSession.getDeviceId(), getProtocolName() + ".ack", false, true)) {
+ channel.write("OK1\r\n");
+ }
}
Parser parser = new Parser(PATTERN_GPRMC, sentence);
diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/org/traccar/protocol/TaipProtocolDecoder.java
index 7702a89fb..e7117a5c9 100644
--- a/src/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/org/traccar/protocol/TaipProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.DateUtil;
@@ -67,7 +68,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.number("(x{8})") // odometer
.number("[01]") // gps power
.groupEnd("?")
- .number("(d)") // fix mode
.any()
.compile();
@@ -104,15 +104,38 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position();
position.setProtocol(getProtocolName());
+ Integer event = null;
+
if (parser.hasNext(3)) {
- position.set(Position.KEY_EVENT, parser.nextInt(0));
+ event = parser.nextInt();
position.setTime(getTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)));
} else if (parser.hasNext()) {
position.setTime(getTime(parser.nextInt(0)));
}
if (parser.hasNext()) {
- position.set(Position.KEY_EVENT, parser.nextInt(0));
+ event = parser.nextInt();
+ }
+
+ if (event != null) {
+ switch (event) {
+ case 22:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 23:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 24:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ case 26:
+ case 28:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ position.set(Position.KEY_EVENT, event);
+ break;
+ }
}
if (parser.hasNext(6)) {
@@ -138,7 +161,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
}
- position.setValid(parser.nextInt(0) != 0);
+ position.setValid(true);
String[] attributes = null;
beginIndex = sentence.indexOf(';');
@@ -150,6 +173,12 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
attributes = sentence.substring(beginIndex, endIndex).split(";");
}
+ return decodeAttributes(channel, remoteAddress, position, attributes);
+ }
+
+ private Position decodeAttributes(
+ Channel channel, SocketAddress remoteAddress, Position position, String[] attributes) {
+
String uniqueId = null;
DeviceSession deviceSession = null;
String messageIndex = null;
@@ -161,7 +190,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
String key = attribute.substring(0, index).toLowerCase();
String value = attribute.substring(index + 1);
switch (key) {
-
case "id":
uniqueId = value;
deviceSession = getDeviceSession(channel, remoteAddress, value);
@@ -169,23 +197,30 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
}
break;
-
+ case "io":
+ position.set(Position.KEY_IGNITION, BitUtil.check(value.charAt(0) - '0', 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(value.charAt(0) - '0', 1));
+ position.set(Position.KEY_OUTPUT, value.charAt(1) - '0');
+ position.set(Position.KEY_INPUT, value.charAt(2) - '0');
+ break;
+ case "ix":
+ position.set(Position.PREFIX_IO + 1, value);
+ break;
+ case "ad":
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(value));
+ break;
case "sv":
position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
break;
-
case "bl":
- position.set(Position.KEY_BATTERY, Integer.parseInt(value));
+ position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.001);
break;
-
case "vo":
position.set(Position.KEY_ODOMETER, Long.parseLong(value));
break;
-
default:
position.set(key, value);
break;
-
}
} else if (attribute.startsWith("#")) {
messageIndex = attribute;
diff --git a/src/org/traccar/protocol/TelicProtocolDecoder.java b/src/org/traccar/protocol/TelicProtocolDecoder.java
index 62b756ab5..2c0d714c6 100644
--- a/src/org/traccar/protocol/TelicProtocolDecoder.java
+++ b/src/org/traccar/protocol/TelicProtocolDecoder.java
@@ -34,8 +34,8 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.number("dddd")
- .number("(d{6})") // device id
- .number("(d+),") // type
+ .number("(d{6}|d{15})") // device id
+ .number("(d{1,2}),") // type
.number("d{12},") // event time
.number("d+,")
.number("(dd)(dd)(dd)") // date (ddmmyy)
diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java
index 524e6d5b5..d0177da97 100644
--- a/src/org/traccar/protocol/TeltonikaProtocol.java
+++ b/src/org/traccar/protocol/TeltonikaProtocol.java
@@ -39,14 +39,14 @@ public class TeltonikaProtocol extends BaseProtocol {
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new TeltonikaFrameDecoder());
pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
+ pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
+ pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true));
}
});
}
diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 3f5b68f67..80f0045d5 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -18,8 +18,8 @@ package org.traccar.protocol;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
@@ -35,8 +35,17 @@ import java.util.List;
public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
- public TeltonikaProtocolDecoder(TeltonikaProtocol protocol) {
+ private boolean connectionless;
+ private boolean extended;
+
+ public void setExtended(boolean extended) {
+ this.extended = extended;
+ }
+
+ public TeltonikaProtocolDecoder(TeltonikaProtocol protocol, boolean connectionless) {
super(protocol);
+ this.connectionless = connectionless;
+ this.extended = Context.getConfig().getBoolean(getProtocolName() + ".extended");
}
private DeviceSession parseIdentification(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
@@ -67,7 +76,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_TYPE, buf.readUnsignedByte());
- position.set(Position.KEY_COMMAND, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_RESULT, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII));
}
@@ -84,7 +93,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) {
+ private void decodeOtherParameter(Position position, int id, ChannelBuffer buf, int length) {
switch (id) {
case 1:
case 2:
@@ -95,15 +104,24 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 9:
position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false));
break;
+ case 17:
+ position.set("axisX", readValue(buf, length, true));
+ break;
+ case 18:
+ position.set("axisY", readValue(buf, length, true));
+ break;
+ case 19:
+ position.set("axisZ", readValue(buf, length, true));
+ break;
+ case 21:
+ position.set(Position.KEY_RSSI, readValue(buf, length, false));
+ break;
case 66:
position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001);
break;
case 67:
position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
break;
- case 70:
- position.set(Position.KEY_DEVICE_TEMP, readValue(buf, length, true) * 0.1);
- break;
case 72:
position.set(Position.PREFIX_TEMP + 1, readValue(buf, length, true) * 0.1);
break;
@@ -114,17 +132,102 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
break;
case 78:
- position.set(Position.KEY_RFID, readValue(buf, length, false));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", readValue(buf, length, false)));
+ break;
+ case 80:
+ position.set("workMode", readValue(buf, length, false));
+ break;
+ case 179:
+ position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
+ break;
+ case 180:
+ position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1);
+ break;
+ case 181:
+ position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
break;
case 182:
position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
break;
+ case 239:
+ position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
+ break;
+ case 240:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ break;
+ case 241:
+ position.set(Position.KEY_OPERATOR, readValue(buf, length, false));
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
}
}
+ private void decodeGh3000Parameter(Position position, int id, ChannelBuffer buf, int length) {
+ switch (id) {
+ case 1:
+ position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false));
+ break;
+ case 2:
+ position.set("usbConnected", readValue(buf, length, false) == 1);
+ break;
+ case 5:
+ position.set("uptime", readValue(buf, length, false));
+ break;
+ case 20:
+ position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 21:
+ position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 22:
+ position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 67:
+ position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ break;
+ case 221:
+ position.set("button", readValue(buf, length, false));
+ break;
+ case 222:
+ if (readValue(buf, length, false) == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ break;
+ case 240:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ break;
+ case 244:
+ position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1);
+ break;
+ default:
+ position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ break;
+ }
+ }
+
+ private void decodeParameter(Position position, int id, ChannelBuffer buf, int length, int codec) {
+ if (codec == CODEC_GH3000) {
+ decodeGh3000Parameter(position, id, buf, length);
+ } else {
+ decodeOtherParameter(position, id, buf, length);
+ }
+ }
+
+ private void decodeNetwork(Position position) {
+ long cid = position.getLong(Position.PREFIX_IO + 205);
+ int lac = position.getInteger(Position.PREFIX_IO + 206);
+ if (cid != 0 && lac != 0) {
+ CellTower cellTower = CellTower.fromLacCid(lac, cid);
+ long operator = position.getInteger(Position.KEY_OPERATOR);
+ if (operator != 0) {
+ cellTower.setOperator(operator);
+ }
+ position.setNetwork(new Network(cellTower));
+ }
+ }
+
private void decodeLocation(Position position, ChannelBuffer buf, int codec) {
int globalMask = 0x0f;
@@ -171,14 +274,19 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
}
- position.setNetwork(new Network(cellTower));
+ if (BitUtil.check(locationMask, 7)) {
+ cellTower.setOperator(buf.readUnsignedInt());
+ }
- } else if (BitUtil.check(locationMask, 6)) {
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- }
+ position.setNetwork(new Network(cellTower));
- if (BitUtil.check(locationMask, 7)) {
- position.set(Position.KEY_OPERATOR, buf.readUnsignedInt());
+ } else {
+ if (BitUtil.check(locationMask, 6)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(locationMask, 7)) {
+ position.set(Position.KEY_OPERATOR, buf.readUnsignedInt());
+ }
}
} else {
@@ -215,7 +323,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 1)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 1);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 1, codec);
}
}
@@ -223,7 +331,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 2)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 2);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 2, codec);
}
}
@@ -231,7 +339,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 3)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 4);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 4, codec);
}
}
@@ -239,17 +347,27 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (codec == CODEC_FM4X00) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 8);
+ decodeOtherParameter(position, buf.readUnsignedByte(), buf, 8);
}
}
+ // Read 16 byte data
+ if (extended) {
+ int cnt = buf.readUnsignedByte();
+ for (int j = 0; j < cnt; j++) {
+ position.set(Position.PREFIX_IO + buf.readUnsignedByte(), ChannelBuffers.hexDump(buf.readBytes(16)));
+ }
+ }
+
+ decodeNetwork(position);
+
}
private List<Position> parseData(
Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int locationPacketId, String... imei) {
List<Position> positions = new LinkedList<>();
- if (!(channel instanceof DatagramChannel)) {
+ if (!connectionless) {
buf.readUnsignedInt(); // data length
}
@@ -278,7 +396,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (channel != null) {
- if (channel instanceof DatagramChannel) {
+ if (connectionless) {
ChannelBuffer response = ChannelBuffers.dynamicBuffer();
response.writeShort(5);
response.writeShort(0);
@@ -301,7 +419,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
- if (channel instanceof DatagramChannel) {
+ if (connectionless) {
return decodeUdp(channel, remoteAddress, buf);
} else {
return decodeTcp(channel, remoteAddress, buf);
diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
index 4c7da12e0..be3def453 100644
--- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -23,7 +23,6 @@ import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
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;
@@ -33,8 +32,11 @@ import java.util.regex.Pattern;
public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
+ private boolean decodeLow;
+
public Tk103ProtocolDecoder(Tk103Protocol protocol) {
super(protocol);
+ decodeLow = Context.getConfig().getBoolean(getProtocolName() + ".decodeLow");
}
private static final Pattern PATTERN = new PatternBuilder()
@@ -50,7 +52,14 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d)(?:d*,)?") // speed
.number("(dd)(dd)(dd),?") // time (hhmmss)
.number("(d+.?d{1,2}),?") // course
- .number("(?:([01]{8})|(x{8}))?,?") // state
+ .groupBegin()
+ .number("([01])") // charge
+ .number("([01])") // ignition
+ .number("(x)") // io
+ .number("(x)") // io
+ .number("(x)") // io
+ .number("(xxx),?") // fuel
+ .groupEnd("?")
.number("(?:L(x+))?") // odometer
.any()
.number("([+-]ddd.d)?") // temperature
@@ -200,17 +209,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
if (channel != null) {
String id = sentence.substring(0, 12);
String type = sentence.substring(12, 16);
- if (type.equals("BP00") || type.equals("BP05")) {
- String content = sentence.substring(16);
- if (content.length() >= 15) {
- getDeviceSession(channel, remoteAddress, content.substring(0, 15));
- }
- if (type.equals("BP00")) {
- channel.write("(" + id + "AP01HSO)");
- return null;
- } else if (type.equals("BP05")) {
- channel.write("(" + id + "AP05)");
- }
+ if (type.equals("BP00")) {
+ channel.write("(" + id + "AP01HSO)");
+ return null;
+ } else if (type.equals("BP05")) {
+ channel.write("(" + id + "AP05)");
}
}
@@ -249,32 +252,44 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- switch (Context.getConfig().getString(getProtocolName() + ".speed", "kmh")) {
- case "kn":
- position.setSpeed(parser.nextDouble(0));
- break;
- case "mph":
- position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0)));
- break;
- default:
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- break;
- }
+ position.setSpeed(convertSpeed(parser.nextDouble(0), "kmh"));
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
position.setCourse(parser.nextDouble(0));
- String status = parser.next();
- if (status != null) {
- position.set(Position.KEY_STATUS, status); // binary status
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 0);
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+
+ int mask1 = parser.nextHexInt();
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(mask1, 0) ? 1 : 0);
+ position.set("panic", BitUtil.check(mask1, 1) ? 1 : 0);
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(mask1, 2) ? 1 : 0);
+ if (decodeLow || BitUtil.check(mask1, 3)) {
+ position.set(Position.KEY_BLOCKED, BitUtil.check(mask1, 3) ? 1 : 0);
+ }
+
+ int mask2 = parser.nextHexInt();
+ for (int i = 0; i < 3; i++) {
+ if (decodeLow || BitUtil.check(mask2, i)) {
+ position.set("hs" + (3 - i), BitUtil.check(mask2, i) ? 1 : 0);
+ }
+ }
+ if (decodeLow || BitUtil.check(mask2, 3)) {
+ position.set(Position.KEY_DOOR, BitUtil.check(mask2, 3) ? 1 : 0);
+ }
+
+ int mask3 = parser.nextHexInt();
+ for (int i = 1; i <= 3; i++) {
+ if (decodeLow || BitUtil.check(mask3, i)) {
+ position.set("ls" + (3 - i + 1), BitUtil.check(mask3, i) ? 1 : 0);
+ }
+ }
- int value = Integer.parseInt(new StringBuilder(status).reverse().toString(), 2);
- position.set(Position.KEY_CHARGE, !BitUtil.check(value, 0));
- position.set(Position.KEY_IGNITION, BitUtil.check(value, 1));
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt());
}
- position.set(Position.KEY_STATUS, parser.next()); // hex status
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
diff --git a/src/org/traccar/protocol/TlvProtocol.java b/src/org/traccar/protocol/TlvProtocol.java
new file mode 100644
index 000000000..da8d88b8a
--- /dev/null
+++ b/src/org/traccar/protocol/TlvProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class TlvProtocol extends BaseProtocol {
+
+ public TlvProtocol() {
+ super("tlv");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder('\0'));
+ pipeline.addLast("objectDecoder", new TlvProtocolDecoder(TlvProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TlvProtocolDecoder.java b/src/org/traccar/protocol/TlvProtocolDecoder.java
new file mode 100644
index 000000000..41d65be09
--- /dev/null
+++ b/src/org/traccar/protocol/TlvProtocolDecoder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+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 TlvProtocolDecoder extends BaseProtocolDecoder {
+
+ public TlvProtocolDecoder(TlvProtocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, String type, String... arguments) {
+ if (channel != null) {
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeBytes(ChannelBuffers.copiedBuffer(type, StandardCharsets.US_ASCII));
+ for (String argument : arguments) {
+ response.writeByte(argument.length());
+ response.writeBytes(ChannelBuffers.copiedBuffer(argument, StandardCharsets.US_ASCII));
+ }
+ response.writeByte(0);
+ channel.write(response);
+ }
+ }
+
+ private String readArgument(ChannelBuffer buf) {
+ return buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ String type = buf.readBytes(2).toString(StandardCharsets.US_ASCII);
+
+ if (channel != null) {
+ switch (type) {
+ case "0A":
+ case "0C":
+ sendResponse(channel, type);
+ break;
+ case "0B":
+ sendResponse(channel, type, "1482202689", "10", "20", "15");
+ break;
+ case "0E":
+ case "0F":
+ sendResponse(channel, type, "30", "Unknown");
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (type.equals("0E")) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, readArgument(buf));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(new Date(Long.parseLong(readArgument(buf)) * 1000));
+
+ readArgument(buf); // location identifier
+
+ position.setLongitude(Double.parseDouble(readArgument(buf)));
+ position.setLatitude(Double.parseDouble(readArgument(buf)));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(readArgument(buf))));
+ position.setCourse(Double.parseDouble(readArgument(buf)));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(readArgument(buf)));
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java
index c10523117..b8458dd52 100644
--- a/src/org/traccar/protocol/TmgProtocolDecoder.java
+++ b/src/org/traccar/protocol/TmgProtocolDecoder.java
@@ -144,7 +144,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0));
position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0));
position.set(Position.KEY_VERSION_FW, parser.next());
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java
index a4ae4cb8f..3c2dee8ec 100644
--- a/src/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/org/traccar/protocol/TotemProtocolDecoder.java
@@ -171,6 +171,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_LOW_BATTERY;
case 0x11:
return Position.ALARM_OVERSPEED;
+ case 0x30:
+ return Position.ALARM_PARKING;
case 0x42:
return Position.ALARM_GEOFENCE_EXIT;
case 0x43:
diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/org/traccar/protocol/TramigoProtocolDecoder.java
index b39e3eab1..b1e28e17d 100644
--- a/src/org/traccar/protocol/TramigoProtocolDecoder.java
+++ b/src/org/traccar/protocol/TramigoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2017 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.
@@ -130,12 +130,13 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2))));
}
- pattern = Pattern.compile("(\\d{1,2}:\\d{2} \\w{3} \\d{1,2})");
+ pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})");
matcher = pattern.matcher(sentence);
if (!matcher.find()) {
return null;
}
- DateFormat dateFormat = new SimpleDateFormat("HH:mm MMM d yyyy", Locale.ENGLISH);
+ DateFormat dateFormat = new SimpleDateFormat(
+ matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH);
position.setTime(DateUtil.correctYear(
dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR))));
diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java
index 88ac76134..918748f7b 100644
--- a/src/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/org/traccar/protocol/TrvProtocolDecoder.java
@@ -53,8 +53,8 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)") // satellites
.number("(ddd)") // battery
.number("(d)") // acc
- .number("dd") // arm status
- .number("dd,") // working mode
+ .number("(dd)") // arm status
+ .number("(dd),") // working mode
.number("(d+),") // mcc
.number("(d+),") // mnc
.number("(d+),") // lac
@@ -71,9 +71,41 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(d)") // acc
.number("(dd)") // arm status
.number("(dd)") // working mode
+ .groupBegin()
+ .number("(ddd)") // interval
+ .number("d") // vibration alarm
+ .number("ddd") // vibration sensitivity
+ .number("d") // automatic arm
+ .number("dddd") // automatic arm time
+ .number("(d)") // blocked
+ .number("(d)") // power status
+ .number("(d)") // movement status
+ .groupEnd("?")
.any()
.compile();
+ private Boolean decodeOptionalValue(Parser parser, int activeValue) {
+ int value = parser.nextInt();
+ if (value != 0) {
+ return value == activeValue;
+ }
+ return null;
+ }
+
+ private void decodeCommon(Position position, Parser parser) {
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_BATTERY, parser.nextInt());
+ position.set(Position.KEY_IGNITION, decodeOptionalValue(parser, 1));
+ position.set(Position.KEY_ARMED, decodeOptionalValue(parser, 1));
+
+ int mode = parser.nextInt();
+ if (mode != 0) {
+ position.set("mode", mode);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -84,11 +116,14 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
String type = sentence.substring(id.length(), id.length() + 4);
if (channel != null) {
+ String responseHeader = id + (char) (type.charAt(0) + 1) + type.substring(1);
if (type.equals("AP00") && id.equals("IW")) {
String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
- channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "," + time + ",0#");
+ channel.write(responseHeader + "," + time + ",0#");
+ } else if (type.equals("AP14")) {
+ channel.write(responseHeader + ",0.000,0.000#");
} else {
- channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "#");
+ channel.write(responseHeader + "#");
}
}
@@ -115,13 +150,13 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- position.set(Position.KEY_RSSI, parser.nextInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
- position.set(Position.KEY_IGNITION, parser.nextInt(0) != 0);
+ decodeCommon(position, parser);
- position.set("arm", parser.nextInt(0));
- position.set("mode", parser.nextInt(0));
+ if (parser.hasNext(3)) {
+ position.set(Position.KEY_BLOCKED, decodeOptionalValue(parser, 2));
+ position.set(Position.KEY_CHARGE, decodeOptionalValue(parser, 1));
+ position.set(Position.KEY_MOTION, decodeOptionalValue(parser, 1));
+ }
return position;
@@ -137,31 +172,25 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
- .setDate(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setValid(parser.next().equals("A"));
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
- dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
- position.setCourse(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble());
- int rssi = parser.nextInt(0);
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
-
- int acc = parser.nextInt(0);
- if (acc != 0) {
- position.set(Position.KEY_IGNITION, acc == 1);
- }
+ decodeCommon(position, parser);
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), rssi)));
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())));
return position;
+
}
return null;
diff --git a/src/org/traccar/protocol/TytanProtocolDecoder.java b/src/org/traccar/protocol/TytanProtocolDecoder.java
index 030fbce78..0ae669784 100644
--- a/src/org/traccar/protocol/TytanProtocolDecoder.java
+++ b/src/org/traccar/protocol/TytanProtocolDecoder.java
@@ -111,7 +111,7 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 5);
break;
case 150:
- position.set("door", buf.readUnsignedByte());
+ position.set(Position.KEY_DOOR, buf.readUnsignedByte());
break;
default:
buf.skipBytes(length);
diff --git a/src/org/traccar/protocol/TzoneProtocolDecoder.java b/src/org/traccar/protocol/TzoneProtocolDecoder.java
index 69aa916df..079ad3126 100644
--- a/src/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -43,7 +43,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
case 0x11:
return Position.ALARM_OVERSPEED;
case 0x14:
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case 0x15:
return Position.ALARM_ACCELERATION;
case 0x30:
@@ -203,7 +203,16 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
if (blockLength >= 13) {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
position.set("terminalInfo", buf.readUnsignedByte());
- position.set(Position.PREFIX_IO + 1, buf.readUnsignedShort());
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 1));
+ status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 4));
+ if (BitUtil.check(status, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
position.set("gsmStatus", buf.readUnsignedByte());
position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
index 1b22eeb75..31a3d2cfe 100644
--- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -308,7 +308,8 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
break;
case DATA_RFID:
- position.set(Position.KEY_RFID, buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID,
+ buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII));
position.set("authorized", buf.readUnsignedByte() != 0);
break;
diff --git a/src/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/org/traccar/protocol/VisiontekProtocolDecoder.java
index 636a3d640..f32c9fbfe 100644
--- a/src/org/traccar/protocol/VisiontekProtocolDecoder.java
+++ b/src/org/traccar/protocol/VisiontekProtocolDecoder.java
@@ -130,7 +130,7 @@ public class VisiontekProtocolDecoder extends BaseProtocolDecoder {
position.setValid(parser.next().equals("A"));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/org/traccar/protocol/Vt200ProtocolDecoder.java
index b9af11b43..2ae24efbb 100644
--- a/src/org/traccar/protocol/Vt200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Vt200ProtocolDecoder.java
@@ -27,6 +27,8 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Date;
public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
@@ -40,6 +42,13 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes * 0.0001 / 60;
}
+ protected Date decodeDate(ChannelBuffer buf) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2))
+ .setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
+ return dateBuilder.getDate();
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -57,7 +66,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
int type = buf.readUnsignedShort();
buf.readUnsignedShort(); // length
- if (type == 0x2084) {
+ if (type == 0x2086 || type == 0x2084 || type == 0x2082) {
Position position = new Position();
position.setProtocol(getProtocolName());
@@ -66,12 +75,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // data type
buf.readUnsignedShort(); // trip id
- DateBuilder dateBuilder = new DateBuilder();
- dateBuilder.setDateReverse(
- BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
- dateBuilder.setTime(
- BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
- position.setTime(dateBuilder.getDate());
+ position.setTime(decodeDate(buf));
position.setLatitude(decodeCoordinate(BcdUtil.readInteger(buf, 8)));
position.setLongitude(decodeCoordinate(BcdUtil.readInteger(buf, 9)));
@@ -97,6 +101,48 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == 0x3088) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ buf.readUnsignedShort(); // trip id
+ buf.skipBytes(8); // imei
+ buf.skipBytes(8); // imsi
+
+ position.set("tripStart", decodeDate(buf).getTime());
+ position.set("tripEnd", decodeDate(buf).getTime());
+ position.set("drivingTime", buf.readUnsignedShort());
+
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+
+ position.set("maxSpeed", UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set("maxRpm", buf.readUnsignedShort());
+ position.set("maxTemp", buf.readUnsignedByte() - 40);
+ position.set("hardAccelerationCount", buf.readUnsignedByte());
+ position.set("hardBrakingCount", buf.readUnsignedByte());
+
+ for (String speedType : Arrays.asList("over", "high", "normal", "low")) {
+ position.set(speedType + "SpeedTime", buf.readUnsignedShort());
+ position.set(speedType + "SpeedDistance", buf.readUnsignedInt());
+ position.set(speedType + "SpeedFuel", buf.readUnsignedInt());
+ }
+
+ position.set("idleTime", buf.readUnsignedShort());
+ position.set("idleFuel", buf.readUnsignedInt());
+
+ position.set("hardCorneringCount", buf.readUnsignedByte());
+ position.set("overspeedCount", buf.readUnsignedByte());
+ position.set("overheatCount", buf.readUnsignedShort());
+ position.set("laneChangeCount", buf.readUnsignedByte());
+ position.set("emergencyRefueling", buf.readUnsignedByte());
+
+ return position;
+
}
return null;
diff --git a/src/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/org/traccar/protocol/VtfmsProtocolDecoder.java
index 852b9a749..5fb687e6d 100644
--- a/src/org/traccar/protocol/VtfmsProtocolDecoder.java
+++ b/src/org/traccar/protocol/VtfmsProtocolDecoder.java
@@ -84,7 +84,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
case 15:
return Position.ALARM_POWER_RESTORED;
case 32:
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case 33:
return Position.ALARM_ACCELERATION;
default:
diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/org/traccar/protocol/WatchProtocol.java
index 42a640b85..2be2dc9ae 100644
--- a/src/org/traccar/protocol/WatchProtocol.java
+++ b/src/org/traccar/protocol/WatchProtocol.java
@@ -29,6 +29,7 @@ public class WatchProtocol extends BaseProtocol {
public WatchProtocol() {
super("watch");
setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_SOS_NUMBER,
diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java
index 57f5d7e78..86dc9456d 100644
--- a/src/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/org/traccar/protocol/WatchProtocolDecoder.java
@@ -76,8 +76,6 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_ENTER;
} else if (BitUtil.check(status, 3)) {
return Position.ALARM_OVERSPEED;
- } else if (BitUtil.check(status, 4)) {
- return Position.ALARM_MOVEMENT;
} else if (BitUtil.check(status, 16)) {
return Position.ALARM_SOS;
} else if (BitUtil.check(status, 17)) {
@@ -207,9 +205,13 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextInt(0));
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
- position.set("steps", parser.nextInt(0));
+ position.set(Position.KEY_STEPS, parser.nextInt(0));
- position.set(Position.KEY_ALARM, decodeAlarm(parser.nextHexInt(0)));
+ int status = parser.nextHexInt(0);
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+ if (BitUtil.check(status, 4)) {
+ position.set(Position.KEY_MOTION, true);
+ }
decodeTail(position, parser.next());
diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java
index c5d8fad86..d2d3b52d1 100644
--- a/src/org/traccar/protocol/WatchProtocolEncoder.java
+++ b/src/org/traccar/protocol/WatchProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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,13 +26,14 @@ import java.text.DecimalFormatSymbols;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
public class WatchProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
@Override
public String formatValue(String key, Object value) {
if (key.equals(Command.KEY_TIMEZONE)) {
- double offset = ((Number) value).longValue() / 3600.0;
+ double offset = TimeZone.getTimeZone((String) value).getRawOffset() / 3600000.0;
DecimalFormat fmt = new DecimalFormat("+#.##;-#.##", DecimalFormatSymbols.getInstance(Locale.US));
return fmt.format(offset);
}
@@ -41,8 +42,9 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
}
+ @Override
protected String formatCommand(Command command, String format, String... keys) {
- String content = super.formatCommand(command, format, this, keys);
+ String content = formatCommand(command, format, this, keys);
return String.format("[CS*%s*%04x*%s]",
getUniqueId(command.getDeviceId()), content.length(), content);
}
@@ -97,6 +99,8 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
protected Object encodeCommand(Command command) {
switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, command.getString(Command.KEY_DATA));
case Command.TYPE_POSITION_SINGLE:
return formatCommand(command, "RG");
case Command.TYPE_SOS_NUMBER:
diff --git a/src/org/traccar/protocol/WialonProtocolDecoder.java b/src/org/traccar/protocol/WialonProtocolDecoder.java
index 82098413b..4eb3b9b8e 100644
--- a/src/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/org/traccar/protocol/WialonProtocolDecoder.java
@@ -109,7 +109,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
String[] values = parser.next().split(",");
diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/org/traccar/protocol/XexunProtocolDecoder.java
index 20804bbb4..bb4b4f48c 100644
--- a/src/org/traccar/protocol/XexunProtocolDecoder.java
+++ b/src/org/traccar/protocol/XexunProtocolDecoder.java
@@ -17,13 +17,11 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
-import org.traccar.helper.UnitsConverter;
import java.net.SocketAddress;
import java.util.regex.Pattern;
@@ -70,9 +68,11 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
if (value != null) {
switch (value.toLowerCase()) {
case "acc on":
+ case "accstart":
position.set(Position.KEY_IGNITION, true);
break;
case "acc off":
+ case "accstop":
position.set(Position.KEY_IGNITION, false);
break;
case "help me!":
@@ -121,14 +121,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- switch (Context.getConfig().getString(getProtocolName() + ".speed", "kn")) {
- case "kmh":
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- break;
- default:
- position.setSpeed(parser.nextDouble(0));
- break;
- }
+ position.setSpeed(convertSpeed(parser.nextDouble(0), "kn"));
position.setCourse(parser.nextDouble(0));
diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
index a42c6175f..15e8558be 100644
--- a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -25,6 +25,7 @@ import org.traccar.model.Position;
import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -77,8 +78,6 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
for (int i : l4) {
TAG_LENGTH_MAP.put(i, 4);
}
- TAG_LENGTH_MAP.put(0x65, 17);
- TAG_LENGTH_MAP.put(0x73, 16);
TAG_LENGTH_MAP.put(0x95, 24);
}
@@ -163,12 +162,24 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
case 0x13:
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0x14:
+ position.set(Position.KEY_RSSI, buf.readShort());
+ break;
case 0x16:
position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
break;
case 0x17:
position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1);
break;
+ case 0x57:
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ break;
+ case 0x65:
+ position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII));
+ break;
+ case 0x73:
+ position.set(Position.KEY_VERSION_FW, buf.readBytes(16).toString(StandardCharsets.US_ASCII).trim());
+ break;
default:
buf.skipBytes(getTagLength(tag));
break;