aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java2
-rw-r--r--src/main/java/org/traccar/notification/NotificatorManager.java4
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTelegram.java77
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocol.java7
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/PebbellProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/PebbellProtocolDecoder.java153
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocolDecoder.java191
-rw-r--r--src/main/java/org/traccar/protocol/SuntechFrameDecoder.java47
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java5
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java4
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java13
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/PebbellProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java6
22 files changed, 755 insertions, 40 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index a92dba7fa..5a5850a86 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -134,8 +134,8 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
FilterHandler.class,
GeocoderHandler.class,
MotionHandler.class,
- EngineHoursHandler.class,
CopyAttributesHandler.class,
+ EngineHoursHandler.class,
ComputedAttributesHandler.class,
WebDataHandler.class,
DefaultDataHandler.class);
diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java
index a4080a38d..191748379 100644
--- a/src/main/java/org/traccar/notification/NotificatorManager.java
+++ b/src/main/java/org/traccar/notification/NotificatorManager.java
@@ -31,6 +31,7 @@ import org.traccar.notificators.NotificatorNull;
import org.traccar.notificators.Notificator;
import org.traccar.notificators.NotificatorSms;
import org.traccar.notificators.NotificatorWeb;
+import org.traccar.notificators.NotificatorTelegram;
public final class NotificatorManager {
@@ -57,6 +58,9 @@ public final class NotificatorManager {
case "firebase":
defaultNotificator = NotificatorFirebase.class.getCanonicalName();
break;
+ case "telegram":
+ defaultNotificator = NotificatorTelegram.class.getCanonicalName();
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
new file mode 100644
index 000000000..89c15ba7c
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
@@ -0,0 +1,77 @@
+/*
+ * 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.notificators;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.notification.NotificationFormatter;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+
+public class NotificatorTelegram extends Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTelegram.class);
+
+ private String url;
+ private String chatId;
+
+ public static class Message {
+ @JsonProperty("chat_id")
+ private String chatId;
+ @JsonProperty("text")
+ private String text;
+ @JsonProperty("parse_mode")
+ private String parseMode = "html";
+ }
+
+ public NotificatorTelegram() {
+ url = String.format(
+ "https://api.telegram.org/bot%s/sendMessage",
+ Context.getConfig().getString("notificator.telegram.key"));
+ chatId = Context.getConfig().getString("notificator.Telegram.chatid");
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) {
+
+ Message message = new Message();
+ message.chatId = chatId;
+ message.text = NotificationFormatter.formatShortMessage(userId, event, position);
+
+ Context.getClient().target(url).request()
+ .async().post(Entity.json(message), new InvocationCallback<Object>() {
+ @Override
+ public void completed(Object o) {
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ LOGGER.warn("Telegram API error", throwable);
+ }
+ });
+ }
+
+ @Override
+ public void sendAsync(long userId, Event event, Position position) {
+ sendSync(userId, event, position);
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java b/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java
new file mode 100644
index 000000000..967b17a64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.nio.charset.StandardCharsets;
+
+public class FifotrackFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
+ if (index != -1) {
+ int length = index - buf.readerIndex() + 3 + Integer.parseInt(
+ buf.toString(buf.readerIndex() + 2, index - buf.readerIndex() - 2, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() >= length + 2) {
+ ByteBuf frame = buf.readRetainedSlice(length);
+ buf.skipBytes(2); // delimiter
+ return frame;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
index 371e01e55..12cd00b4e 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.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.
@@ -15,8 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
@@ -28,9 +26,8 @@ public class FifotrackProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new FifotrackFrameDecoder());
pipeline.addLast(new StringEncoder());
- pipeline.addLast(new StringDecoder());
pipeline.addLast(new FifotrackProtocolDecoder(FifotrackProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 35696ee12..a9cf025af 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -19,11 +19,11 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
-import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -32,6 +32,7 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
@@ -87,12 +88,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.text("$$")
.number("d+,") // length
.number("(d+),") // imei
- .expression("([^*]+),") // photo id
+ .number("x+,") // index
+ .expression("[^,]+,") // type
+ .expression("([^,]+),") // photo id
.number("(d+),") // offset
.number("(d+),") // size
- .number("(x+)") // data
- .text("*")
- .number("xx")
.compile();
private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
@@ -165,11 +165,14 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
- int typeIndex = sentence.indexOf(',', sentence.indexOf(',', sentence.indexOf(',') + 1) + 1) + 1;
- String type = sentence.substring(typeIndex, typeIndex + 3);
+ ByteBuf buf = (ByteBuf) msg;
+ int typeIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',') + 1;
+ typeIndex = buf.indexOf(typeIndex, buf.writerIndex(), (byte) ',') + 1;
+ typeIndex = buf.indexOf(typeIndex, buf.writerIndex(), (byte) ',') + 1;
+ String type = buf.toString(typeIndex, 3, StandardCharsets.US_ASCII);
if (type.equals("D05")) {
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
Parser parser = new Parser(PATTERN_PHOTO, sentence);
if (parser.matches()) {
String imei = parser.next();
@@ -179,16 +182,35 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
requestPhoto(channel, remoteAddress, imei, photoId);
}
} else if (type.equals("D06")) {
+ if (photo == null) {
+ return null;
+ }
+ int dataIndex = buf.indexOf(typeIndex + 4, buf.writerIndex(), (byte) ',') + 1;
+ dataIndex = buf.indexOf(dataIndex, buf.writerIndex(), (byte) ',') + 1;
+ dataIndex = buf.indexOf(dataIndex, buf.writerIndex(), (byte) ',') + 1;
+ String sentence = buf.toString(buf.readerIndex(), dataIndex, StandardCharsets.US_ASCII);
Parser parser = new Parser(PATTERN_PHOTO_DATA, sentence);
if (parser.matches()) {
String imei = parser.next();
String photoId = parser.next();
parser.nextInt(); // offset
parser.nextInt(); // size
- photo.writeBytes(DataConverter.parseHex(parser.next()));
- requestPhoto(channel, remoteAddress, imei, photoId);
+ buf.readerIndex(dataIndex);
+ photo.writeBytes(buf.readBytes(buf.readableBytes() - 3)); // ignore checksum
+ if (photo.isWritable()) {
+ requestPhoto(channel, remoteAddress, imei, photoId);
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(getDeviceSession(channel, remoteAddress, imei).getDeviceId());
+ getLastLocation(position, null);
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ photo.release();
+ photo = null;
+ return position;
+ }
}
} else {
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
return decodeLocation(channel, remoteAddress, sentence);
}
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index c4443a00b..22bbe4441 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -88,19 +88,18 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
}
private Integer decodeBattery(int value) {
- switch (value) {
- case 6:
- return 100;
- case 5:
- return 80;
- case 4:
- return 60;
- case 3:
- return 20;
- case 2:
- return 10;
- default:
- return null;
+ if (value == 0) {
+ return null;
+ } else if (value <= 3) {
+ return (value - 1) * 10;
+ } else if (value <= 6) {
+ return (value - 1) * 20;
+ } else if (value <= 100) {
+ return value;
+ } else if (value >= 0xF1 && value <= 0xF6) {
+ return value - 0xF0;
+ } else {
+ return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/PebbellProtocol.java b/src/main/java/org/traccar/protocol/PebbellProtocol.java
new file mode 100644
index 000000000..762c30585
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PebbellProtocol.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 PebbellProtocol extends BaseProtocol {
+
+ public PebbellProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true));
+ pipeline.addLast(new PebbellProtocolDecoder(PebbellProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PebbellProtocolDecoder.java b/src/main/java/org/traccar/protocol/PebbellProtocolDecoder.java
new file mode 100644
index 000000000..7271c60d8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PebbellProtocolDecoder.java
@@ -0,0 +1,153 @@
+/*
+ * 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.ByteBufUtil;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class PebbellProtocolDecoder extends BaseProtocolDecoder {
+
+ public PebbellProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_DATA = 0x01;
+
+ private String decodeAlarm(int code) {
+ if (BitUtil.check(code, 0)) {
+ return Position.ALARM_LOW_BATTERY;
+ }
+ if (BitUtil.check(code, 1)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(code, 2)) {
+ return Position.ALARM_FALL_DOWN;
+ }
+ if (BitUtil.check(code, 8)) {
+ return Position.ALARM_POWER_OFF;
+ }
+ if (BitUtil.check(code, 9)) {
+ return Position.ALARM_POWER_ON;
+ }
+ if (BitUtil.check(code, 12)) {
+ return Position.ALARM_SOS;
+ }
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedByte(); // properties
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedShortLE(); // index
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_DATA) {
+
+ Position position = new Position(getProtocolName());
+
+ while (buf.isReadable()) {
+ int endIndex = buf.readUnsignedByte() + buf.readerIndex();
+ int key = buf.readUnsignedByte();
+ switch (key) {
+ case 0x01:
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case 0x02:
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readIntLE()));
+ break;
+ case 0x20:
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+ position.setAltitude(buf.readShortLE());
+ position.setValid(buf.readUnsignedShortLE() > 0);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x21:
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedByte();
+ if (position.getNetwork() == null) {
+ position.setNetwork(new Network());
+ }
+ while (buf.readerIndex() < endIndex) {
+ int rssi = buf.readByte();
+ position.getNetwork().addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShortLE(), buf.readUnsignedShortLE(), rssi));
+ }
+ break;
+ case 0x22:
+ if (position.getNetwork() == null) {
+ position.setNetwork(new Network());
+ }
+ while (buf.readerIndex() < endIndex) {
+ int rssi = buf.readByte();
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ position.getNetwork().addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), rssi));
+ }
+ break;
+ case 0x40:
+ buf.readUnsignedIntLE(); // timestamp
+ int heartRate = buf.readUnsignedByte();
+ if (heartRate > 1) {
+ position.set(Position.KEY_HEART_RATE, heartRate);
+ }
+ break;
+ default:
+ break;
+ }
+ buf.readerIndex(endIndex);
+ }
+
+ if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) {
+ getLastLocation(position, null);
+ }
+
+ return position.getDeviceId() > 0 ? position : null;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java
new file mode 100644
index 000000000..9783778f0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RadarProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+public class RadarProtocol extends BaseProtocol {
+
+ public RadarProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 12, 2, -14, 0));
+ pipeline.addLast(new RadarProtocolDecoder(RadarProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
new file mode 100644
index 000000000..765171e9c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
@@ -0,0 +1,191 @@
+/*
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.BitSet;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class RadarProtocolDecoder extends BaseProtocolDecoder {
+
+ public RadarProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_TRACKING = 0x4C;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // product id
+ buf.readUnsignedByte(); // product version
+
+ String serialNumber = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serialNumber);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // index
+ buf.readUnsignedInt(); // timestamp
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+
+ if (type == MSG_TRACKING) {
+
+ buf.readUnsignedShort(); // memory count
+ buf.readUnsignedShort(); // memory index
+ int count = buf.readUnsignedShort();
+ buf.readUnsignedShort(); // first index
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int index = 0; index < count; index++) {
+
+ Position position = new Position(getProtocolName());
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedShort());
+
+ int maskLength = buf.readUnsignedByte();
+ BitSet mask = BitSet.valueOf(buf.nioBuffer(buf.readerIndex(), maskLength));
+ buf.skipBytes(maskLength);
+
+ buf.readUnsignedShort(); // length
+
+ if (mask.get(0)) {
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
+ }
+ if (mask.get(1)) {
+ position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
+ }
+ if (mask.get(2)) {
+ position.setLatitude(buf.readInt() / 360000.0);
+ }
+ if (mask.get(3)) {
+ position.setLongitude(buf.readInt() / 360000.0);
+ }
+ if (mask.get(4)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ }
+ if (mask.get(5)) {
+ position.setCourse(buf.readUnsignedShort() * 0.1);
+ }
+ if (mask.get(6)) {
+ position.setAltitude(buf.readShort());
+ }
+ if (mask.get(7)) {
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 0));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(flags, 4));
+ }
+ if (mask.get(8)) {
+ long flags = buf.readUnsignedInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(flags, 1));
+ position.set(Position.KEY_MOTION, BitUtil.check(flags, 2));
+ for (int i = 0; i < 3; i++) {
+ position.set(Position.PREFIX_IN + i, BitUtil.check(flags, 4 + i));
+ }
+ }
+ if (mask.get(9)) {
+ int flags = buf.readUnsignedShort();
+ position.set(Position.KEY_BLOCKED, BitUtil.check(flags, 0));
+ position.set(Position.PREFIX_IN + 0, BitUtil.check(flags, 4));
+ }
+ for (int i = 10; i <= 14; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedShort(); // reserved
+ }
+ }
+ if (mask.get(15)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ }
+ if (mask.get(16)) {
+ buf.readUnsignedInt(); // reserved
+ }
+ for (int i = 17; i <= 27; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ for (int i = 28; i <= 37; i += 2) {
+ if (mask.get(i)) {
+ buf.skipBytes(12); // reserved
+ }
+ if (mask.get(i + 1)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ if (mask.get(38)) {
+ buf.skipBytes(6); // driver id
+ }
+ if (mask.get(39)) {
+ buf.readUnsignedShort(); // hardware problems
+ }
+ if (mask.get(40)) {
+ buf.readShort(); // acceleration x
+ }
+ if (mask.get(41)) {
+ buf.readShort(); // acceleration y
+ }
+ if (mask.get(42)) {
+ buf.readShort(); // acceleration z
+ }
+ if (mask.get(43)) {
+ buf.skipBytes(10); // operator
+ }
+ if (mask.get(44)) {
+ buf.readUnsignedShort(); // power
+ }
+ for (int i = 45; i <= 49; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ if (mask.get(50)) {
+ buf.readShort(); // tilt
+ }
+
+ if (position.getDeviceTime() != null && position.getFixTime() != null) {
+ positions.add(position);
+ }
+
+ }
+
+ // ACK 0x9C
+
+ return positions.isEmpty() ? null : positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
new file mode 100644
index 000000000..de8de17f0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+public class SuntechFrameDecoder extends BaseFrameDecoder {
+
+ private ByteBuf readFrame(ByteBuf buf, int delimiterIndex) {
+ ByteBuf frame = buf.readRetainedSlice(delimiterIndex - buf.readerIndex());
+ buf.skipBytes(1); // delimiter
+ return frame;
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
+ while (delimiterIndex > 0) {
+ if (delimiterIndex + 1 < buf.writerIndex() && buf.getByte(delimiterIndex + 1) == '\n') {
+ delimiterIndex = buf.indexOf(delimiterIndex + 1, buf.writerIndex(), (byte) '\r');
+ } else {
+ return readFrame(buf, delimiterIndex);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 29ae114e7..48d6e81c1 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -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;
import org.traccar.model.Command;
@@ -37,7 +36,7 @@ public class SuntechProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
+ pipeline.addLast(new SuntechFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new SuntechProtocolEncoder());
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index f67ff88db..2a06d35e5 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 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.
@@ -38,7 +38,7 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_HEADER = new PatternBuilder()
.number("#(d+)#") // imei
.any()
- .expression("([^#]+)#") // status
+ .expression("#([^#]+)#") // status
.number("d+") // number of records
.compile();
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
index 4d48bb763..bfff4eef3 100644
--- a/src/test/java/org/traccar/ProtocolTest.java
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -101,7 +102,13 @@ public class ProtocolTest extends BaseTest {
}
protected void verifyAttribute(BaseProtocolDecoder decoder, Object object, String key, Object expected) throws Exception {
- Position position = (Position) decoder.decode(null, null, object);
+ Object decodedObject = decoder.decode(null, null, object);
+ Position position;
+ if (decodedObject instanceof Collection) {
+ position = (Position) ((Collection) decodedObject).iterator().next();
+ } else {
+ position = (Position) decodedObject;
+ }
switch (key) {
case "speed":
assertEquals(expected, position.getSpeed());
diff --git a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java
new file mode 100644
index 000000000..6b39b6c17
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FifotrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FifotrackFrameDecoder decoder = new FifotrackFrameDecoder();
+
+ verifyFrame(
+ binary("24243132362c3836393436373034393239303738372c324138432c4130312c2c3139303431333135333235342c412c2d31352e3132373836382c33392e3236323530362c302c3136322c3431352c38303937323234332c302c303030302c30302c302c3634337c337c353141467c424632462c3141337c3446367c3833327c302c312c2a3534"),
+ decoder.decode(null, null, binary("24243132362c3836393436373034393239303738372c324138432c4130312c2c3139303431333135333235342c412c2d31352e3132373836382c33392e3236323530362c302c3136322c3431352c38303937323234332c302c303030302c30302c302c3634337c337c353141467c424632462c3141337c3446367c3833327c302c312c2a35340d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
index 1dcfc89c4..11492fb6f 100644
--- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -10,19 +10,22 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest {
FifotrackProtocolDecoder decoder = new FifotrackProtocolDecoder(null);
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$$79,868345037864709,382,D05,190220085833,22.643210,114.018176,1,1,1,13152,23FFD339*25"));
- verifyPosition(decoder, text(
+ verifyNull(decoder, binary(
+ "2424313036332c3836383334353033373836343730392c312c4430362c32343434424438362c302c313032342cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00b0148a705c8cd00479e6917ef7b5003c9ec29b90bf5a00457c366a4620806801921f41da999cf02801ebf4a4e73cf14002b153f2d4a5cb0c8506802261cf4a50b8a007053d718a4c1cf340099c526ecd007fffd07f6a55c86140126e19e69acdcd0037a9a4a002909a004eb4e030334001a4ce280141cd2138a004ed4982074e6800ed49de801698793401ffd18cf4a65002af5a4ce1a8026cf14d278a008f760d20ebf350031cf6149183bb8a009c03de901f9a801c0e78a31400b9c518a004c5140094b8a00fffd28b1462800c518a00414b400b8a00e68016814001a2800a5eb40062908cd002628a0028a00fffd3998e4734b1b7c981400c3d79a7829b7ef73e98a0069f6a4c50034a926a551b47340037a1e4d424734012c43820529001e72680060bfc34a1f6f02800618e6a3c9cd003c336304d0091d680187ad211401fffd47f34a48079a0091946327d2a173e9400a290d002f6a4c7ad00205cf4a7f3b680131c52639a00304521140098a42c68010138a00e28014034d391401fffd58c9a69e6801a3341a004dc69439140085b3da909cd001b69369cf14013019148cb40028229dcd0014b4005142a3739"));
+
+ verifyPosition(decoder, buffer(
"$$105,866104023179743,AB,A00,,161007085534,A,54.738791,25.271918,0,350,151,0,17929,0000,0,,246|1|65|96DB,936|0*0B"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$$103,866104023179743,5,A00,,161006192841,A,54.738791,25.271918,0,342,200,0,4265,0000,0,,246|1|65|96DB,9C4|0*75"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$$103,866104023179743,4,A00,,161006192810,V,54.738791,25.271918,0,158,122,0,4235,0000,0,,246|1|65|96DB,9C5|0*69"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$$135,866104023192332,29,A01,,160606093046,A,22.546430,114.079730,0,186,181,0,415322,0000,02,2,460|0|27B3|EA7,A2F|3B9|3|0,940C7E,31.76|30.98*46"));
}
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
index 4a5eadf52..c69dffce7 100644
--- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
+ verifyPosition(decoder, binary(
+ "24702802061601234020031910125482600612695044000000ffffbbff000000000000000001760d04e2c9934d"));
+
verifyNotNull(decoder, buffer(
"*hq,356327081001239,VP1,V,470,002,92,3565,0Y92,19433,30Y92,1340,29#"));
diff --git a/src/test/java/org/traccar/protocol/PebbellProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PebbellProtocolDecoderTest.java
new file mode 100644
index 000000000..a6555dd76
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PebbellProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PebbellProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PebbellProtocolDecoder decoder = new PebbellProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "ab1845005d39370301100133353836383830303030303338303209245b92b55c84004b610502001000002221ca00050b4a005cc30f4a0056c80f4a003ba90e4a0055c8074a005dc3034a0057c8"));
+
+ verifyAttributes(decoder, binary(
+ "ab185c001db78b03011001333538363838303030303033383032092448bd8a5c82003b130502010000003922ca923bad10f794bd30b5c2cb0595b2944a0c49a4f9b6a4b1e9991e79ba0026bb78c08fb4581faae7ee3fb0e091f5778e96b074a78ed46528"));
+
+ verifyNotNull(decoder, binary(
+ "ab18ba0339dd89030110013335383638383030303030333830320924b9298a5c940083392221ca0005154a005dc3144a003ba90d4a00876c0d4a00406c0c4a00856c0a4a0056c80924a92a8a5c94003b452221ca0005154a005dc3154a003ba9124a0056c80e4a00876c0d4a00856c064a0057c80924d52b8a5c94007b4e2221ca0005124a005dc31a4a003ba90d4a0056c80d4a005cc30c4a00856c0c4a00406c1931a4298a5c8c050000d02a8a5c7e040000fc2b8a5c422200000924012d8a5c94009b592221ca0005134a005dc3174a003ba9164a0056c8114a00856c104a00406c0f4a0057c809242d2e8a5c9400bb5f2221ca00051a4a005dc3164a003ba9124a0056c8104a00406c0d4a00856c0c4a005cc30924592f8a5c9400bb642221ca00051a4a005dc3154a003ba9114a00406c104a0056c80c4a00856c0c4a005cc3092485308a5c9400bb642221ca00051a4a005dc3154a003ba9114a0056c8104a00406c0c4a005cc30b4a0057c80924b1318a5c9400bb642221ca00051a4a005dc3154a003ba9124a0056c80c4a00856c0b4a005cc30a4a0057c80924dd328a5c9400bb642221ca00051a4a005dc3154a003ba9114a0056c8104a00406c0c4a00856c0b4a005cc30931542e8a5c34440000092409348a5c9400bb642221ca00051a4a005dc3134a003ba9124a0056c80c4a005cc30c4a00856c0a4a0057c8092436358a5c9400bb642221ca00051b4a005dc3164a003ba9124a0056c8114a00406c0c4a00856c0c4a005cc3092462368a5c9400bb642221ca00051b4a005dc3154a003ba9134a0056c80f4a00406c0e4a005cc30c4a00856c09248e378a5c9400bb642221ca00051b4a005dc3174a003ba9134a0056c8104a00406c0e4a005cc30c4a00856c0924ba388a5c9400bb642221ca00051b4a005dc3164a003ba9134a0056c8114a00406c0d4a00856c0c4a0057c80924e6398a5c9400bb642221ca00051b4a005dc3134a0056c8104a00406c0e4a00856c0c4a005cc30a4a0057c80924123b8a5c9400b3642221ca0005194a005dc3184a003ba9134a0056c8104a00406c0d4a00856c0d4a005cc309243e3c8a5c9400bb642221ca00051a4a005dc3164a003ba9114a0056c8104a00406c0e4a00856c0c4a005cc309246a3d8a5c940063642221ca00051a4a005dc3174a003ba9114a00406c0f4a0056c80d4a00856c0c4a0057c80924963e8a5c840083642221ca0005144a005dc31d4a0056c81a4a00d73a174a003ba9164a00856c134a005cc30924c43f8a5c840083632221ca0005164a005cc31b4a0056c8174a003ba9164a006903134a005dc3124a006803"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java
new file mode 100644
index 000000000..3289474d1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RadarProtocolDecoderTest extends ProtocolTest {
+
+ @Ignore
+ @Test
+ public void testDecode() throws Exception {
+
+ RadarProtocolDecoder decoder = new RadarProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "361800011459015cb497554c01c101ff003500050038000207ff831c04c01f1c00555cb464895cb46487ff7f04eafeffdbd80000079402ead0000000110000000000120d2aff150000000000000002000a00050002436c61726f0000000000008b00000003764500037653005207ff831c04c01f1c00555cb4648a5cb46489ff7f04eafeffdbd80000079402ead0000000010000000000120e00ff150000000000000002000800060002436c61726f0000000000008d00000003764600037654000207ff831c04c01f1c00555cb464d85cb464d7ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000002000700070003436c61726f0000000000008d000000037694000376a2005207ff831c04c01f1c00555cb464d95cb464d9ff7f04eafeffdbd80000079402eac0000000010000000000120e00ff150000000000000002000700070003436c61726f0000000000008d000000037695000376a3000207ff831c04c01f1c00555cb465065cb46504ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000000000500060005436c61726f0000000000008d0000000376c2000376d07ed7"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
new file mode 100644
index 000000000..ccd9139f4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SuntechFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SuntechFrameDecoder decoder = new SuntechFrameDecoder();
+
+ verifyFrame(
+ binary("5354363030414c563b303038373238333237"),
+ decoder.decode(null, null, binary("5354363030414c563b3030383732383332370d")));
+
+ verifyFrame(
+ binary("53543630305545583b3030383732383332373b32303b3536383b32303139303432343b30383a33333a31313b30626631323833623b3333343b32303b326631393b31383b2b32302e3531343134353b2d3130302e3734333731303b3030302e3030343b3030302e30303b31303b313b31303337373836313b31322e39363b3030303030303b34343b745f303d31333b4e5f303d303839442e303b745f313d31423b4e5f313d304537342e303b515f443d30420d0a3b42343b3032383439383b342e313b31"),
+ decoder.decode(null, null, binary("53543630305545583b3030383732383332373b32303b3536383b32303139303432343b30383a33333a31313b30626631323833623b3333343b32303b326631393b31383b2b32302e3531343134353b2d3130302e3734333731303b3030302e3030343b3030302e30303b31303b313b31303337373836313b31322e39363b3030303030303b34343b745f303d31333b4e5f303d303839442e303b745f313d31423b4e5f313d304537342e303b515f443d30420d0a3b42343b3032383439383b342e313b310d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
index 83caae208..185c3c368 100644
--- a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class Tlt2hProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,11 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest {
Tlt2hProtocolDecoder decoder = new Tlt2hProtocolDecoder(null);
+ verifyAttribute(decoder, text(
+ "#869260042149724#MP90_4G#0000#AUTOLOW#1\r\n" +
+ "#02201be0000$GPRMC,001645.00,A,5333.2920,N,11334.3857,W,0.03,,250419,,,A*5E\r\n"),
+ Position.KEY_IGNITION, false);
+
verifyPositions(decoder, text(
"#867962040161955#MT600#0000#0#0#137#41#0#AUTO#1\r\n" +
"#00019023402$GPRMC,084702.00,A,3228.6772,S,11545.9684,E,,159.80,251018,,,A*56\r\n"));