diff options
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##")); |