aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--setup/default.xml1
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java6
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java4
-rw-r--r--src/main/java/org/traccar/MainModule.java10
-rw-r--r--src/main/java/org/traccar/config/Keys.java13
-rw-r--r--src/main/java/org/traccar/handler/StandardLoggingHandler.java8
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java63
-rw-r--r--src/main/java/org/traccar/model/Position.java2
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/ItsFrameDecoder.java67
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocol.java3
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java79
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java53
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocol.java1
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java14
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java163
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java16
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolDecoder.java100
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java18
-rw-r--r--src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java15
-rw-r--r--src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java3
36 files changed, 754 insertions, 155 deletions
diff --git a/setup/default.xml b/setup/default.xml
index 65474c774..18c45b879 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -262,5 +262,6 @@
<entry key='neos.port'>5183</entry>
<entry key='satsol.port'>5184</entry>
<entry key='globalstar.port'>5185</entry>
+ <entry key='sanul.port'>5186</entry>
</properties>
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index b3d37f689..a92dba7fa 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Keys;
import org.traccar.handler.DefaultDataHandler;
+import org.traccar.handler.TimeHandler;
import org.traccar.handler.events.AlertEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
import org.traccar.handler.events.DriverEventHandler;
@@ -56,11 +57,13 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
private static final Logger LOGGER = LoggerFactory.getLogger(BasePipelineFactory.class);
private final TrackerServer server;
+ private final String protocol;
private boolean eventsEnabled;
private int timeout;
public BasePipelineFactory(TrackerServer server, String protocol) {
this.server = server;
+ this.protocol = protocol;
eventsEnabled = Context.getConfig().getBoolean(Keys.EVENT_ENABLE);
timeout = Context.getConfig().getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
if (timeout == 0) {
@@ -103,7 +106,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
}
pipeline.addLast(new OpenChannelHandler(server));
pipeline.addLast(new NetworkMessageHandler());
- pipeline.addLast(new StandardLoggingHandler());
+ pipeline.addLast(new StandardLoggingHandler(protocol));
addProtocolHandlers(handler -> {
if (!(handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder)) {
@@ -118,6 +121,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
addHandlers(
pipeline,
+ TimeHandler.class,
GeolocationHandler.class,
HemisphereHandler.class,
DistanceHandler.class,
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
index a8b53ff60..2309b1e70 100644
--- a/src/main/java/org/traccar/MainEventHandler.java
+++ b/src/main/java/org/traccar/MainEventHandler.java
@@ -45,10 +45,10 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
public MainEventHandler() {
String connectionlessProtocolList = Context.getConfig().getString("status.ignoreOffline");
if (connectionlessProtocolList != null) {
- connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split(",")));
+ connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]")));
}
logAttributes.addAll(Arrays.asList(
- Context.getConfig().getString("logger.attributes", DEFAULT_LOGGER_ATTRIBUTES).split(",")));
+ Context.getConfig().getString("logger.attributes", DEFAULT_LOGGER_ATTRIBUTES).split("[, ]")));
}
@Override
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 6fe8bad1c..3acd19b6a 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -59,6 +59,7 @@ import org.traccar.handler.GeolocationHandler;
import org.traccar.handler.HemisphereHandler;
import org.traccar.handler.MotionHandler;
import org.traccar.handler.RemoteAddressHandler;
+import org.traccar.handler.TimeHandler;
import org.traccar.handler.events.AlertEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
import org.traccar.handler.events.DriverEventHandler;
@@ -300,6 +301,15 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
+ public static TimeHandler provideTimeHandler(Config config) {
+ if (config.hasKey(Keys.TIME_OVERRIDE)) {
+ return new TimeHandler(config);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
public static DefaultDataHandler provideDefaultDataHandler(@Nullable DataManager dataManager) {
if (dataManager != null) {
return new DefaultDataHandler(dataManager);
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 48cf3e558..a000329e2 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -184,6 +184,19 @@ public final class Keys {
"filter.skipAttributes.enable", Boolean.class);
/**
+ * Override device time. Possible values are 'deviceTime' and 'serverTime'
+ */
+ public static final ConfigKey TIME_OVERRIDE = new ConfigKey(
+ "time.override", String.class);
+
+ /**
+ * List of protocols for overriding time. If not specified override is applied globally. List consist of protocol
+ * names that can be separated by comma or single space character.
+ */
+ public static final ConfigKey TIME_PROTOCOLS = new ConfigKey(
+ "time.protocols", String.class);
+
+ /**
* Replaces coordinates with last known if change is less than a 'coordinates.error' meters. Helps to avoid
* coordinates jumps during parking period.
*/
diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
index 88010458f..13c5f8281 100644
--- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java
+++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
@@ -31,6 +31,12 @@ public class StandardLoggingHandler extends ChannelDuplexHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class);
+ private final String protocol;
+
+ public StandardLoggingHandler(String protocol) {
+ this.protocol = protocol;
+ }
+
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log(ctx, false, msg);
@@ -58,7 +64,7 @@ public class StandardLoggingHandler extends ChannelDuplexHandler {
StringBuilder message = new StringBuilder();
message.append("[").append(ctx.channel().id().asShortText()).append(": ");
- message.append(((InetSocketAddress) ctx.channel().localAddress()).getPort());
+ message.append(protocol);
if (downstream) {
message.append(" > ");
} else {
diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java
new file mode 100644
index 000000000..d8039518c
--- /dev/null
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 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.handler;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@ChannelHandler.Sharable
+public class TimeHandler extends ChannelInboundHandlerAdapter {
+
+ private final boolean useServerTime;
+ private final Set<String> protocols;
+
+ public TimeHandler(Config config) {
+ useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
+ String protocolList = Context.getConfig().getString(Keys.TIME_PROTOCOLS);
+ if (protocolList != null) {
+ protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]")));
+ } else {
+ protocols = null;
+ }
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+
+ if (msg instanceof Position && (protocols == null
+ || protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) {
+
+ Position position = (Position) msg;
+ if (useServerTime) {
+ position.setDeviceTime(position.getServerTime());
+ }
+ position.setFixTime(position.getDeviceTime());
+
+ }
+ ctx.fireChannelRead(msg);
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 4b327cbd2..6032dc588 100644
--- a/src/main/java/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -157,7 +157,7 @@ public class Position extends Message {
this.protocol = protocol;
}
- private Date serverTime;
+ private Date serverTime = new Date();
public Date getServerTime() {
return serverTime;
diff --git a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
index 3635c29e5..853fa8f81 100644
--- a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
@@ -47,7 +47,10 @@ public class BoxProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // distance
.number("(d+),") // event
.number("(d+)") // status
- .any()
+ .groupBegin()
+ .text(";")
+ .expression("(.+)")
+ .groupEnd("?")
.compile();
@Override
@@ -99,6 +102,14 @@ public class BoxProtocolDecoder extends BaseProtocolDecoder {
position.setValid(!BitUtil.check(status, 2));
position.set(Position.KEY_STATUS, status);
+ if (parser.hasNext()) {
+ String[] data = parser.next().split(";");
+ for (String item : data) {
+ int valueIndex = item.indexOf(',');
+ position.set(item.substring(0, valueIndex).toLowerCase(), item.substring(valueIndex + 1));
+ }
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index beaa34125..35696ee12 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -97,9 +97,9 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
if (channel != null) {
- String content = "D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
- int length = 1 + imei.length() + 1 + content.length() + 5;
- String response = String.format("@@%02d,%s,%s*", length, imei, content);
+ String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
+ int length = 1 + imei.length() + 1 + content.length();
+ String response = String.format("##%02d,%s,%s*", length, imei, content);
response += Checksum.sum(response) + "\r\n";
channel.writeAndFlush(new NetworkMessage(response, socketAddress));
}
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index aeb57a116..46ef4fff8 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -648,16 +648,16 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1]));
}
if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) {
- position.set("tachographInfo", Integer.parseInt(values[index - 1]));
+ position.set("tachographInfo", Integer.parseInt(values[index - 1], 16));
}
if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) {
- position.set("indicators", Integer.parseInt(values[index - 1]));
+ position.set("indicators", Integer.parseInt(values[index - 1], 16));
}
if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) {
- position.set("lights", Integer.parseInt(values[index - 1]));
+ position.set("lights", Integer.parseInt(values[index - 1], 16));
}
if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) {
- position.set("doors", Integer.parseInt(values[index - 1]));
+ position.set("doors", Integer.parseInt(values[index - 1], 16));
}
if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) {
position.set("vehicleOverspeed", Double.parseDouble(values[index - 1]));
diff --git a/src/main/java/org/traccar/protocol/ItsFrameDecoder.java b/src/main/java/org/traccar/protocol/ItsFrameDecoder.java
new file mode 100644
index 000000000..cebfdca5f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ItsFrameDecoder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+public class ItsFrameDecoder extends BaseFrameDecoder {
+
+ private static final int MINIMUM_LENGTH = 20;
+
+ private ByteBuf readFrame(ByteBuf buf, int delimiterIndex, int skip) {
+ int headerIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) '$');
+ if (headerIndex > 0 && headerIndex < delimiterIndex) {
+ return buf.readRetainedSlice(headerIndex - buf.readerIndex());
+ } else {
+ ByteBuf frame = buf.readRetainedSlice(delimiterIndex - buf.readerIndex());
+ buf.skipBytes(skip);
+ return frame;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ while (buf.isReadable() && buf.getByte(buf.readerIndex()) != '$') {
+ buf.skipBytes(1);
+ }
+
+ int delimiterIndex = BufferUtil.indexOf("\r\n", buf);
+ if (delimiterIndex > MINIMUM_LENGTH) {
+ return readFrame(buf, delimiterIndex, 2);
+ } else {
+ delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
+ if (delimiterIndex > MINIMUM_LENGTH) {
+ if (buf.writerIndex() > delimiterIndex + 1 && buf.getByte(delimiterIndex + 1) == '*') {
+ delimiterIndex += 1;
+ }
+ if (buf.getByte(delimiterIndex - 2) == ',') {
+ return readFrame(buf, delimiterIndex - 1, 2); // skip binary checksum
+ } else {
+ return readFrame(buf, delimiterIndex, 1);
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java
index f53600dc9..e074f9277 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocol.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocol.java
@@ -18,7 +18,6 @@ package org.traccar.protocol;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -28,7 +27,7 @@ public class ItsProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '*'));
+ pipeline.addLast(new ItsFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new ItsProtocolDecoder(ItsProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index 482f34e65..313575f27 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -23,6 +23,8 @@ import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -41,11 +43,11 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.groupBegin()
.expression("[^,]+,") // vendor
.expression("[^,]+,") // firmware version
- .expression("[^,]+,") // type
- .number("d+,")
- .expression("[LH],") // history
+ .expression("(..),") // status
+ .number("(d+),") // event
+ .expression("([LH]),") // history
.or()
- .expression("[^,]+,") // type
+ .expression("([^,]+),") // type
.groupEnd()
.number("(d{15}),") // imei
.groupBegin()
@@ -72,12 +74,17 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("([01]),") // charging
.number("(d+.?d*),") // power
.number("(d+.?d*),") // battery
- .number("[01],") // emergency
+ .number("([01]),") // emergency
.expression("[CO]?,") // tamper
- .number("(?:x+,){5}") // main cell
- .number("(?:-?x+,){12}") // other cells
+ .number("((?:x+,){5}") // main cell
+ .number("(?:-?x+,){12})") // other cells
.number("([01]{4}),") // inputs
.number("([01]{2}),") // outputs
+ .groupBegin()
+ .number("d+,") // index
+ .number("(d+.d+),") // adc1
+ .number("(d+.d+),") // adc2
+ .groupEnd("?")
.groupEnd("?")
.or()
.number("(-?d+.d+),") // altitude
@@ -101,6 +108,12 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_CORNERING;
case "OS":
return Position.ALARM_OVERSPEED;
+ case "TA":
+ return Position.ALARM_TAMPERING;
+ case "BD":
+ return Position.ALARM_POWER_CUT;
+ case "BR":
+ return Position.ALARM_POWER_RESTORED;
default:
return null;
}
@@ -121,6 +134,11 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ String status = parser.next();
+ Integer event = parser.nextInt();
+ boolean history = "H".equals(parser.next());
+ String type = parser.next();
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
@@ -129,8 +147,28 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ if (type != null && type.equals("EMR")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ if (event != null) {
+ position.set(Position.KEY_EVENT, event);
+ }
+ if (history) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
if (parser.hasNext()) {
- position.set(Position.KEY_ALARM, decodeAlarm(parser.next()));
+ status = parser.next();
+ }
+ if (status != null) {
+ if (status.equals("IN")) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (status.equals("IF")) {
+ position.set(Position.KEY_IGNITION, false);
+ } else {
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+ }
}
if (parser.hasNext()) {
@@ -149,17 +187,40 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, parser.nextInt());
}
- if (parser.hasNext(7)) {
+ if (parser.hasNext(8)) {
position.setAltitude(parser.nextDouble());
position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
position.set(Position.KEY_POWER, parser.nextDouble());
position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ position.set("emergency", parser.nextInt() > 0);
+
+ String[] cells = parser.next().split(",");
+ int mcc = Integer.parseInt(cells[1]);
+ int mnc = Integer.parseInt(cells[2]);
+ int lac = Integer.parseInt(cells[3], 16);
+ int cid = Integer.parseInt(cells[4], 16);
+ Network network = new Network(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(cells[0])));
+ for (int i = 0; i < 4; i++) {
+ lac = Integer.parseInt(cells[5 + 3 * i + 1], 16);
+ cid = Integer.parseInt(cells[5 + 3 * i + 2], 16);
+ if (lac > 0 && cid > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ }
+ }
+ position.setNetwork(network);
+
position.set(Position.KEY_INPUT, parser.nextBinInt());
position.set(Position.KEY_OUTPUT, parser.nextBinInt());
}
if (parser.hasNext(2)) {
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ }
+
+ if (parser.hasNext(2)) {
position.setAltitude(parser.nextDouble());
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
}
diff --git a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
index e145205f7..62b41a2ea 100644
--- a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
@@ -22,6 +22,7 @@ import org.traccar.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
+import org.traccar.helper.ObdDecoder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -102,20 +103,66 @@ public class NyitechProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
if (type == MSG_COMPREHENSIVE_LIVE || type == MSG_COMPREHENSIVE_HISTORY) {
+
buf.skipBytes(6); // time
- buf.skipBytes(3); // data
+ boolean includeLocation = buf.readUnsignedByte() > 0;
+ boolean includeObd = buf.readUnsignedByte() > 0;
+ buf.readUnsignedByte(); // include sensor
+
+ if (includeLocation) {
+ decodeLocation(position, buf);
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (includeObd) {
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ int pid = buf.readUnsignedShortLE();
+ int length = buf.readUnsignedByte();
+ switch (length) {
+ case 1:
+ position.add(ObdDecoder.decodeData(pid, buf.readByte(), true));
+ break;
+ case 2:
+ position.add(ObdDecoder.decodeData(pid, buf.readShortLE(), true));
+ break;
+ case 4:
+ position.add(ObdDecoder.decodeData(pid, buf.readIntLE(), true));
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+ }
+
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.01);
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+
+
} else if (type == MSG_ALARM) {
+
buf.readUnsignedShortLE(); // random number
buf.readUnsignedByte(); // tag
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
buf.readUnsignedShortLE(); // threshold
buf.readUnsignedShortLE(); // value
buf.skipBytes(6); // time
+
+ decodeLocation(position, buf);
+
} else if (type == MSG_FIXED) {
+
buf.skipBytes(6); // time
- }
- decodeLocation(position, buf);
+ decodeLocation(position, buf);
+
+ } else {
+
+ decodeLocation(position, buf);
+
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
index 1ac62570a..a574293cd 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
@@ -26,6 +26,7 @@ public class RuptelaProtocol extends BaseProtocol {
public RuptelaProtocol() {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
+ Command.TYPE_REQUEST_PHOTO,
Command.TYPE_CONFIGURATION,
Command.TYPE_GET_VERSION,
Command.TYPE_FIRMWARE_UPDATE,
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index a9ab5f5fe..2d476427d 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2019 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.
@@ -48,6 +48,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_SMS_VIA_GPRS = 8;
public static final int MSG_DTCS = 9;
public static final int MSG_SET_IO = 17;
+ public static final int MSG_FILES = 37;
public static final int MSG_EXTENDED_RECORDS = 68;
private Position decodeCommandResponse(DeviceSession deviceSession, int type, ByteBuf buf) {
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index 4242584c9..96d0da5a7 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 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.
@@ -46,6 +46,12 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
case Command.TYPE_CUSTOM:
content.writeBytes(command.getString(Command.KEY_DATA).getBytes(StandardCharsets.US_ASCII));
return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ case Command.TYPE_REQUEST_PHOTO:
+ content.writeByte(1); // sub-command
+ content.writeByte(0); // source
+ content.writeInt(0); // start timestamp
+ content.writeInt(Integer.MAX_VALUE); // end timestamp
+ return encodeContent(RuptelaProtocolDecoder.MSG_FILES, content);
case Command.TYPE_CONFIGURATION:
content.writeBytes((command.getString(Command.KEY_DATA) + "\r\n").getBytes(StandardCharsets.US_ASCII));
return encodeContent(RuptelaProtocolDecoder.MSG_DEVICE_CONFIGURATION, content);
diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java
new file mode 100644
index 000000000..3104e9366
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SanulProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class SanulProtocol extends BaseProtocol {
+
+ public SanulProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 3, 2, 0, 0, true));
+ pipeline.addLast(new SanulProtocolDecoder(SanulProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
new file mode 100644
index 000000000..036d1ee51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class SanulProtocolDecoder extends BaseProtocolDecoder {
+
+ public static final int MSG_LOGIN = 0;
+ public static final int MSG_LOCATION = 1;
+ public static final int MSG_RESPONSE = 5;
+
+ public SanulProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, int type) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xaa); // header
+ response.writeShortLE(0x85da); // reserved
+ response.writeShortLE(15); // length
+ response.writeByte(1); // edition
+ response.writeShortLE(MSG_RESPONSE);
+ response.writeShortLE(type);
+ response.writeIntLE(0); // command id
+ response.writeByte(0); // status
+ response.writeByte(0); // result length
+ response.writeIntLE(0x20000); // result data ?
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedShortLE(); // reserved
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedByte(); // edition
+
+ int type = buf.readUnsignedShortLE();
+
+ buf.readUnsignedIntLE(); // command id
+
+ sendResponse(channel, type);
+
+ if (type == MSG_LOGIN) {
+
+ getDeviceSession(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
+
+ } else if (type == MSG_LOCATION) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index a80e84728..bd485ca70 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -254,7 +254,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
String type = values[index++].substring(5);
- if (!type.equals("STT") && !type.equals("EMG") && !type.equals("EVT") && !type.equals("ALT")) {
+ if (!type.equals("STT") && !type.equals("EMG") && !type.equals("EVT")
+ && !type.equals("ALT") && !type.equals("UEX")) {
return null;
}
@@ -322,6 +323,17 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
case "ALT":
position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
break;
+ case "UEX":
+ int remaining = Integer.parseInt(values[index++]);
+ while (remaining > 0) {
+ String value = values[index++];
+ String[] pair = value.split("=");
+ if (pair.length >= 2) {
+ position.set(pair[0].toLowerCase(), pair[1].trim());
+ }
+ remaining -= value.length() + 1;
+ }
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index fc7b88b08..96fb7f96a 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -32,6 +32,7 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
public class T800xProtocolDecoder extends BaseProtocolDecoder {
@@ -101,7 +102,6 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // length
int index = buf.readUnsignedShort();
ByteBuf imei = buf.readSlice(8);
- int alarm = 0;
DeviceSession deviceSession = getDeviceSession(
channel, remoteAddress, ByteBufUtil.hexDump(imei).substring(1));
@@ -111,96 +111,121 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_GPS || type == MSG_ALARM) {
+ return decodePosition(channel, deviceSession, buf, header, type, index, imei);
+
+ } else if (type == MSG_COMMAND) {
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_INDEX, index);
+ getLastLocation(position, null);
+
+ buf.readUnsignedByte(); // protocol number
- buf.readUnsignedShort(); // acc on interval
- buf.readUnsignedShort(); // acc off interval
- buf.readUnsignedByte(); // angle compensation
- buf.readUnsignedShort(); // distance compensation
+ position.set(Position.KEY_RESULT, buf.toString(StandardCharsets.UTF_16LE));
- position.set(Position.KEY_RSSI, BitUtil.to(buf.readUnsignedShort(), 7));
+ sendResponse(channel, header, type, index, imei, 0);
- int status = buf.readUnsignedByte();
- position.set(Position.KEY_SATELLITES, BitUtil.to(status, 5));
+ return position;
- buf.readUnsignedByte(); // gsensor manager status
- buf.readUnsignedByte(); // other flags
- buf.readUnsignedByte(); // heartbeat
- buf.readUnsignedByte(); // relay status
- buf.readUnsignedShort(); // drag alarm setting
+ }
- int io = buf.readUnsignedShort();
- position.set(Position.KEY_IGNITION, BitUtil.check(io, 14));
- position.set("ac", BitUtil.check(io, 13));
+ sendResponse(channel, header, type, index, imei, 0);
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ return null;
+ }
- alarm = buf.readUnsignedByte();
- position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+ private Position decodePosition(
+ Channel channel, DeviceSession deviceSession,
+ ByteBuf buf, short header, int type, int index, ByteBuf imei) {
- buf.readUnsignedByte(); // reserved
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_INDEX, index);
- int battery = BcdUtil.readInteger(buf, 2);
- if (battery == 0) {
- battery = 100;
- }
- position.set(Position.KEY_BATTERY, battery);
-
- DateBuilder dateBuilder = new DateBuilder()
- .setYear(BcdUtil.readInteger(buf, 2))
- .setMonth(BcdUtil.readInteger(buf, 2))
- .setDay(BcdUtil.readInteger(buf, 2))
- .setHour(BcdUtil.readInteger(buf, 2))
- .setMinute(BcdUtil.readInteger(buf, 2))
- .setSecond(BcdUtil.readInteger(buf, 2));
-
- if (BitUtil.check(status, 6)) {
-
- position.setValid(!BitUtil.check(status, 7));
- position.setTime(dateBuilder.getDate());
- position.setAltitude(buf.readFloatLE());
- position.setLongitude(buf.readFloatLE());
- position.setLatitude(buf.readFloatLE());
- position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4) * 0.1));
- position.setCourse(buf.readUnsignedShort());
-
- } else {
-
- getLastLocation(position, dateBuilder.getDate());
-
- int mcc = buf.readUnsignedShortLE();
- int mnc = buf.readUnsignedShortLE();
-
- if (mcc != 0xffff && mnc != 0xffff) {
- Network network = new Network();
- for (int i = 0; i < 3; i++) {
- network.addCellTower(CellTower.from(
- mcc, mnc, buf.readUnsignedShortLE(), buf.readUnsignedShortLE()));
- }
- position.setNetwork(network);
- }
+ buf.readUnsignedShort(); // acc on interval
+ buf.readUnsignedShort(); // acc off interval
+ buf.readUnsignedByte(); // angle compensation
+ buf.readUnsignedShort(); // distance compensation
- }
+ position.set(Position.KEY_RSSI, BitUtil.to(buf.readUnsignedShort(), 7));
- if (buf.readableBytes() >= 2) {
- position.set(Position.KEY_POWER, BcdUtil.readInteger(buf, 4) * 0.01);
- }
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.to(status, 5));
- sendResponse(channel, header, type, index, imei, alarm);
+ buf.readUnsignedByte(); // gsensor manager status
+ buf.readUnsignedByte(); // other flags
+ buf.readUnsignedByte(); // heartbeat
+ buf.readUnsignedByte(); // relay status
+ buf.readUnsignedShort(); // drag alarm setting
- return position;
+ int io = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(io, 14));
+ position.set("ac", BitUtil.check(io, 13));
+ for (int i = 0; i <= 2; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(io, 7 + i));
+ }
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+
+ int alarm = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+
+ buf.readUnsignedByte(); // reserved
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+
+ int battery = BcdUtil.readInteger(buf, 2);
+ if (battery == 0) {
+ battery = 100;
+ }
+ position.set(Position.KEY_BATTERY, battery);
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setYear(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2));
+
+ if (BitUtil.check(status, 6)) {
+
+ position.setValid(!BitUtil.check(status, 7));
+ position.setTime(dateBuilder.getDate());
+ position.setAltitude(buf.readFloatLE());
+ position.setLongitude(buf.readFloatLE());
+ position.setLatitude(buf.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4) * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+
+ } else {
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedShortLE();
+
+ if (mcc != 0xffff && mnc != 0xffff) {
+ Network network = new Network();
+ for (int i = 0; i < 3; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShortLE(), buf.readUnsignedShortLE()));
+ }
+ position.setNetwork(network);
+ }
}
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.KEY_POWER, BcdUtil.readInteger(buf, 4) * 0.01);
+ }
+
sendResponse(channel, header, type, index, imei, alarm);
- return null;
+ return position;
}
}
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java b/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java
index 1d0f3dabe..34f30b147 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java
@@ -33,8 +33,8 @@ public class T800xProtocolEncoder extends BaseProtocolEncoder {
ByteBuf buf = Unpooled.buffer();
- buf.writeByte('#');
- buf.writeByte('#');
+ buf.writeByte('%');
+ buf.writeByte('%');
buf.writeByte(T800xProtocolDecoder.MSG_COMMAND);
buf.writeShort(7 + 8 + 1 + content.length());
buf.writeShort(1); // serial number
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 974d2c106..485df833a 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -145,8 +145,22 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
} else {
position.set(Position.KEY_TYPE, type);
- position.set(Position.KEY_RESULT, buf.readSlice(buf.readInt()).toString(StandardCharsets.US_ASCII));
+ int length = buf.readInt();
+ boolean readable = true;
+ for (int i = 0; i < length; i++) {
+ byte b = buf.getByte(buf.readerIndex() + i);
+ if (b < 32 && b != '\r' && b != '\n') {
+ readable = false;
+ break;
+ }
+ }
+
+ if (readable) {
+ position.set(Position.KEY_RESULT, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ } else {
+ position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(buf.readSlice(length)));
+ }
}
}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
index de7073b67..42ff3177e 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2019 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.
@@ -59,9 +59,10 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
.groupEnd("?")
.compile();
- private void sendResponse(Channel channel, SocketAddress remoteAddress, String prefix, Integer number) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String type, Integer number) {
if (channel != null) {
- StringBuilder response = new StringBuilder(prefix);
+ StringBuilder response = new StringBuilder("#A");
+ response.append(type.substring(1));
if (number != null) {
response.append(number);
}
@@ -134,60 +135,69 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
+ String type = sentence.substring(0, sentence.indexOf('#', 1) + 1);
- if (sentence.startsWith("#L#")) {
+ switch (type) {
- String[] values = sentence.substring(3).split(";");
+ case "#L#":
+ String[] values = sentence.substring(3).split(";");
- String imei = values[0].indexOf('.') >= 0 ? values[1] : values[0];
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession != null) {
- sendResponse(channel, remoteAddress, "#AL#", 1);
- }
-
- } else if (sentence.startsWith("#P#")) {
-
- sendResponse(channel, remoteAddress, "#AP#", null); // heartbeat
+ String imei = values[0].indexOf('.') >= 0 ? values[1] : values[0];
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession != null) {
+ sendResponse(channel, remoteAddress, type, 1);
+ }
+ break;
- } else if (sentence.startsWith("#SD#") || sentence.startsWith("#D#")) {
+ case "#P#":
+ sendResponse(channel, remoteAddress, type, null); // heartbeat
+ break;
- Position position = decodePosition(
- channel, remoteAddress, sentence.substring(sentence.indexOf('#', 1) + 1));
+ case "#D#":
+ case "#SD#":
+ Position position = decodePosition(
+ channel, remoteAddress, sentence.substring(sentence.indexOf('#', 1) + 1));
- if (position != null) {
- sendResponse(channel, remoteAddress, "#AD#", 1);
- return position;
- }
+ if (position != null) {
+ sendResponse(channel, remoteAddress, type, 1);
+ return position;
+ }
+ break;
- } else if (sentence.startsWith("#B#")) {
+ case "#B#":
+ String[] messages = sentence.substring(sentence.indexOf('#', 1) + 1).split("\\|");
+ List<Position> positions = new LinkedList<>();
- String[] messages = sentence.substring(sentence.indexOf('#', 1) + 1).split("\\|");
- List<Position> positions = new LinkedList<>();
+ for (String message : messages) {
+ position = decodePosition(channel, remoteAddress, message);
+ if (position != null) {
+ position.set(Position.KEY_ARCHIVE, true);
+ positions.add(position);
+ }
+ }
- for (String message : messages) {
- Position position = decodePosition(channel, remoteAddress, message);
- if (position != null) {
- position.set(Position.KEY_ARCHIVE, true);
- positions.add(position);
+ sendResponse(channel, remoteAddress, type, messages.length);
+ if (!positions.isEmpty()) {
+ return positions;
}
- }
+ break;
+
+ case "#M#":
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, sentence.substring(sentence.indexOf('#', 1) + 1));
+ sendResponse(channel, remoteAddress, type, 1);
+ return position;
+ }
+ break;
- sendResponse(channel, remoteAddress, "#AB#", messages.length);
- if (!positions.isEmpty()) {
- return positions;
- }
+ default:
+ break;
- } else if (sentence.startsWith("#M#")) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession != null) {
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, new Date());
- position.setValid(false);
- position.set(Position.KEY_RESULT, sentence.substring(sentence.indexOf('#', 1) + 1));
- sendResponse(channel, remoteAddress, "#AM#", 1);
- return position;
- }
}
return null;
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
index 6d215e672..08809307f 100644
--- a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
@@ -16,9 +16,11 @@
package org.traccar.protocol;
import io.netty.channel.Channel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -27,6 +29,7 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XirgoProtocolDecoder extends BaseProtocolDecoder {
@@ -174,6 +177,15 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
+
+ if (channel instanceof NioDatagramChannel) {
+ Matcher matcher = Pattern.compile("\\$\\$\\d+,(\\d+),.*,(\\d+)##").matcher(sentence);
+ if (matcher.matches()) {
+ String response = "!UDP_ACK," + matcher.group(1) + "," + matcher.group(2);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
if (form != null) {
return decodeCustom(channel, remoteAddress, sentence);
} else {
@@ -188,10 +200,14 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder {
String[] keys = form.split(",");
String[] values = sentence.replace("$$", "").replace("##", "").split(",");
+ if (values.length < keys.length) {
+ return null;
+ }
+
Position position = new Position(getProtocolName());
DateBuilder dateBuilder = new DateBuilder();
- for (int i = 0; i < Math.min(keys.length, values.length); i++) {
+ for (int i = 0; i < keys.length; i++) {
switch (keys[i]) {
case "UID":
case "IM":
diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
index d661a10f2..f7197a05d 100644
--- a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
@@ -18,6 +18,12 @@ public class BoxProtocolDecoderTest extends ProtocolTest {
"H,BT,N878123,080415081234,D63E6DD9,6D6E6DC2,8944100300825505377"));
verifyPosition(decoder, text(
+ "L,190416090826,G,21.46701,39.18655,0,280,86.62,53,21;A,0;D,0;T0,34.2;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,190416090926,G,21.46701,39.18655,0,280,86.62,7,20;A,0;D,0;T0,34.2;I,0;END,106,222,190416080509"));
+
+ verifyPosition(decoder, text(
"L,190227043304,G,25.68773,48.59788,71,53,261.42,1,23;A,0.03;D,0.06;I,0"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index 2fe860573..df7e5d53c 100644
--- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -26,6 +26,9 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTFRI,1A0900,860599000306845,G3-313,0,0,4,1,2.1,0,426.7,8.611466,47.681639,20181214134603,0228,0001,077F,4812,25.2,1,5.7,34,437.3,8.611600,47.681846,20181214134619,0228,0001,077F,4812,25.2,1,4.4,62,438.2,8.611893,47.681983,20181214134633,0228,0001,077F,4812,25.2,1,4.8,78,436.6,8.612236,47.682040,20181214134648,0228,0001,077F,4812,25.2,83,20181214134702,0654$"));
verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,4B0201,867995030004314,,00,1,C07FFFFF,,2,H982769,30263.00,1266,69,82,H0,P69.20,,0,2037.40,243.93,45.56,74.33,13115,,C00,,0,,,0,68.3,99,42.5,-69.099503,18.445614,20190417233605,0370,0002,5A3D,EB42,00,20190417183607,BD76$"));
+
+ verifyPosition(decoder, buffer(
"+RESP:GTCAN,270703,867162025056839,gv300w,0,1,E07FFFFF,,2,H9307659,368713.50,1291,90,91,,P82.40,,61,10.10,6.76,3.34,524.08,,,0000,,00,,,007FFFFF,,,,,,,,,,,,,,,,,,,,,0000,2,0,,,0,88.6,104,117.6,-116.886007,32.543697,20181031202959,0334,0020,5234,7FCC3D0,00,20181031203002,9F50$"));
verifyPositions(decoder, buffer(
diff --git a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java
new file mode 100644
index 000000000..f5c98b1aa
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ItsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ItsFrameDecoder decoder = new ItsFrameDecoder();
+
+ verifyFrame(
+ binary("242c2c3836383732383033373731373434312c312e3444335f4149533134305f312e302c56455253494f4e312e302c32382e3633333731372c4e2c37372e3232323730322c45"),
+ decoder.decode(null, null, binary("242c2c3836383732383033373731373434312c312e3444335f4149533134305f312e302c56455253494f4e312e302c32382e3633333731372c4e2c37372e3232323730322c45242c43502c41544c2c312e3444335f4149533134305f312e302c49462c30382c4c2c3836383732383033373731373434312c2c312c31373034323031392c3133313830392c32382e3633333731352c4e2c37372e3232323730322c452c302e302c342e30302c302c3231312e302c312e312c362e362c496465612c302c312c31322e362c332e392c302c4f2c31322c3430342c31312c3430612c3564332c326464642c3430612c30392c3438622c3430612c30362c3564342c3430612c30352c6165392c3430612c30352c303131312c30302c3236303831342c662a")));
+
+ verifyFrame(
+ binary("244865616465722c69547269616e676c65312c4b41303147313233342c3836343439353033343433343631302c315f333654303242303136344d4149535f362c4149533134302c302e3030303030302c4e2c302e3030303030302c452a3545"),
+ decoder.decode(null, null, binary("244865616465722c69547269616e676c65312c4b41303147313233342c3836343439353033343433343631302c315f333654303242303136344d4149535f362c4149533134302c302e3030303030302c4e2c302e3030303030302c452a35450d0a")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c50432c31322c4c2c3836383732383033373731373434312c2c312c31363034323031392c3037333432372c32382e3633333533352c4e2c37372e3232323733332c452c302e302c3333392e30302c302c302e302c312e302c302e372c216465612c302c312c31332e372c332e392c302c4f2c32312c3430342c20342c38382c616433352c616437622c38382c32322c636661612c38382c31362c363666392c38382c31342c336632632c64372c31332c303131312c30302c3236303731312c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c50432c31322c4c2c3836383732383033373731373434312c2c312c31363034323031392c3037333432372c32382e3633333533352c4e2c37372e3232323733332c452c302e302c3333392e30302c302c302e302c312e302c302e372c216465612c302c312c31332e372c332e392c302c4f2c32312c3430342c20342c38382c616433352c616437622c38382c32322c636661612c38382c31362c363666392c38382c31342c336632632c64372c31332c303131312c30302c3236303731312c3a2a2c4f2c532c2b393138373433393530333938")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322cbc2a")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c2a2a")));
+
+ verifyFrame(
+ binary("244e524d2c524f41445250412c312e394149532c4e522c30312c4c2c3836393836373033363031353636392c2c312c30383034323031392c3230303530362c32362e39303634353636372c4e2c37352e37383936323333332c452c302e302c3238302e37342c31342c3437312e322c302e38302c302e36302c566f6461666f6e65202d2052616a61737468616e2c302c312c31322e392c342e322c302c4f2c32302c3430342c36302c303746352c333939462c31302c303746352c324434372c31302c303746352c333941312c31302c303746352c324136452c30382c303746352c333536382c303030302c30302c3030333838342c3936443446373031"),
+ decoder.decode(null, null, binary("244e524d2c524f41445250412c312e394149532c4e522c30312c4c2c3836393836373033363031353636392c2c312c30383034323031392c3230303530362c32362e39303634353636372c4e2c37352e37383936323333332c452c302e302c3238302e37342c31342c3437312e322c302e38302c302e36302c566f6461666f6e65202d2052616a61737468616e2c302c312c31322e392c342e322c302c4f2c32302c3430342c36302c303746352c333939462c31302c303746352c324434372c31302c303746352c333941312c31302c303746352c324136452c30382c303746352c333536382c303030302c30302c3030333838342c39364434463730312a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
index 7523e29a0..e5a1eacfa 100644
--- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class ItsProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,20 @@ public class ItsProtocolDecoderTest extends ProtocolTest {
ItsProtocolDecoder decoder = new ItsProtocolDecoder(null);
+ verifyAttribute(decoder, text(
+ "$TEL123,Teltonika,03.18.16,NR,1,L,352093085223096,KA09X6945,1,24122018,055749,12.303873,N,76.690697,E,0.0,349.00,10,795.0,0.50,0.40,Airtel,0,1,14.6,4.1,0,,28,404,45,625A,116E,29,28DF,03A3,28,9A5C,0923,26,116F,625A,25,2A51,03A3,0010,00,000042,0.1,0.1,0,()*7B"),
+ Position.PREFIX_ADC + 2, 0.1);
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS,BR,6,L,861693034634154,KA01I2000,1,09112017,160702,12.976593,N,77.549782,E,25.1,344,15,911.0,1.04,0.68,Airtel,1,1,11.8,3.8,1,C,24,404,45,61b4,9ad9,31,9adb,61b4,35,ffff,0000,33,ffff,0000,31,ffff,0000,0001,00,000014,0.0,0.1,4,()*1E"));
+
+ verifyAttribute(decoder, text(
+ "$EPB,EMR,869867036066035,NM,03042019,192008,V,000.00000000,N,000.00000000,E,0000000000.0,0000.0,00.000,G,,0,404,22,ECFB,36EF*226F7BD1"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyPosition(decoder, text(
+ "$,CP,ATL,1.4D3_AIS140_1.0,EA,10,H,868728037717441,,1,31032019,140054,28.533699,N,77.269020,E,0.0,188.00,14,76.0,1.3,0.0,Idea,0,1,12.7,3.9,1,O,22,404,11,69,979c,fc1,69,18,fbf,69,15,e36e,69,14,ba2f,3ff,13,0111,00,249404,"));
+
verifyPosition(decoder, text(
"$NRM,ROADRPA,1.8AIS,EA,01,L,869867036341099,,1,11032019,231048,19.25166667,N,73.04615167,E,0.0,230.21,17,12.8,0.80,0.80,airtel,0,1,13.5,4.2,0,O,22,404,90,0CC9,EBC8,19,0CC9,8992,16,0CC9,AB49,15,0CC9,AB44,14,0CC9,F03C,0000,00,002080,C9FBBF99"));
diff --git a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java
new file mode 100644
index 000000000..882a63fe6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SanulProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SanulProtocolDecoder decoder = new SanulProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "aa007020000100000000000033353333353830313831353431313700000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index f740faa4e..cb5df63e6 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -47,12 +47,6 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
decoder.setHbm(true);
- verifyPosition(decoder, text(
- "ST300STT;007238270;40;313;20190220;12:05:04;c99e48;+04.644623;-074.076922;010.390;202.77;20;1;997100;14.10;100000;2;8384;003634;4.1;1"));
-
- verifyPosition(decoder, text(
- "ST300STT;109002029;08;1080;20190220;13:00:55;85405;+04.645710;-074.078525;007.760;005.19;10;1;6520802;13.86;100100;4;1716;0000039863;4.1;1;0.00;0000;0000;0;0"));
-
verifyAttribute(decoder, text(
"ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"),
Position.KEY_HOURS, 3188 * 60000L);
@@ -65,6 +59,18 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
verifyPosition(decoder, text(
+ "ST600UEX;008728327;20;520;20190218;10:56:51;0bf1a893;334;20;2f19;18;+20.514195;-100.743597;000.015;000.00;9;1;3720808;12.89;000000;44;t_0=0D;N_0=0551.0;t_1=14;N_1=039F.0;Q_D=0B\r\n;9E;010440;4.1;1"));
+
+ verifyPosition(decoder, text(
+ "ST600UEX;100850000;01;010;20081017;07:41:56;0000004f;450;20;0023;24;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;25;Welcome to Suntech World!;12;0;4.5;1"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;007238270;40;313;20190220;12:05:04;c99e48;+04.644623;-074.076922;010.390;202.77;20;1;997100;14.10;100000;2;8384;003634;4.1;1"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;109002029;08;1080;20190220;13:00:55;85405;+04.645710;-074.078525;007.760;005.19;10;1;6520802;13.86;100100;4;1716;0000039863;4.1;1;0.00;0000;0000;0;0"));
+
+ verifyPosition(decoder, text(
"SA200STT;608945;129;20190215;15:04:53;3dce071558;+22.006721;-098.771016;001.198;000.00;11;1;2632589;12.21;010000;1;3211"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
index fe77d91b7..bfe06b5cd 100644
--- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class T800xProtocolDecoderTest extends ProtocolTest {
@@ -13,6 +14,10 @@ public class T800xProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"252501001504050880061689888888111111250350"));
+ verifyAttribute(decoder, binary(
+ "2525810128000108664250328959160149004d00450049003a003800360036003400320035003000330032003800390035003900310036002c005300450054002000560045005200530049004f004e0020004f004b002c00560065007200730069006f006e003a00420061007300690063003a00560031002e0030002e0030002c004100500050003a00560034002e0032002e0033002c004200550049004c0044003a0032003000310039002d00300033002d00330030002c00300038003a00300035002c0050004c0054003a0032003500300033004100560045002c00480057003a00560032002e0031002c004d004f00440045004c003a002c004d004f00440045004d003a0042003900470036004d0041005200300032004100300037004d00310047002300"),
+ Position.KEY_RESULT, "IMEI:866425032895916,SET VERSION OK,Version:Basic:V1.0.0,APP:V4.2.3,BUILD:2019-03-30,08:05,PLT:2503AVE,HW:V2.1,MODEL:,MODEM:B9G6MAR02A07M1G#");
+
verifyPosition(decoder, binary(
"2525020044a66d0862522030401350001403841409c40064edc000051100960000071701370000003ea7ee0019032010581300000000aad3e1bda6f24d42000000001281"));
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
index af3700225..e41b91281 100644
--- a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
@@ -16,7 +16,7 @@ public class T800xProtocolEncoderTest extends ProtocolTest {
command.setType(Command.TYPE_CUSTOM);
command.set(Command.KEY_DATA, "RELAY,0000,On#");
- verifyCommand(encoder, command, binary("232381001e000101234567890123450152454c41592c303030302c4f6e23"));
+ verifyCommand(encoder, command, binary("252581001e000101234567890123450152454c41592c303030302c4f6e23"));
}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
index f94cd9460..b515fbdc6 100644
--- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -15,6 +15,9 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"000F313233343536373839303132333435"));
+ verifyPositions(decoder, false, binary(
+ "00000000000000100C010600000008010300010015D5C5010000D988"));
+
verifyPositions(decoder, binary(
"000000000000004c08010000016818d500580009c28d9f1cb3757a00be00c60f0053000f06f0011503c80001011d00fc0007423799180053cdf80dce426f430f88190bb8560bb802f100005aa110002887e000010000ee8d"));
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
index f795742fd..d85364d22 100644
--- a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class XirgoProtocolDecoderTest extends ProtocolTest {
decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,HD,SV,HP,BV,CQ,GS,SI,IG,OT");
+ verifyNull(decoder, text(
+ "$$184800619,6115,Y1z1.1179AA2.3.7c79d34,,,000##"));
+
verifyPosition(decoder, text(
"$$183900034,4002,03/30/2019,02:15:22,46.848577,-114.022213,978,0.0,172.3,16,1.2,13.291,20,3,2,2,1##"));