aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-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
25 files changed, 648 insertions, 148 deletions
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":