aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar')
-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
14 files changed, 644 insertions, 34 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();