diff options
23 files changed, 757 insertions, 40 deletions
diff --git a/setup/default.xml b/setup/default.xml index 18c45b879..f9907e9e2 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -263,5 +263,7 @@ <entry key='satsol.port'>5184</entry> <entry key='globalstar.port'>5185</entry> <entry key='sanul.port'>5186</entry> + <entry key='pebbell.port'>5187</entry> + <entry key='radar.port'>5188</entry> </properties> 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")); |