diff options
Diffstat (limited to 'src/main/java/org/traccar/protocol')
50 files changed, 1474 insertions, 270 deletions
diff --git a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java index f071e2d97..8ed1fc8e8 100644 --- a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2020 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. @@ -39,7 +39,7 @@ public class AtrackFrameDecoder extends BaseFrameDecoder { return buf.readRetainedSlice(KEEPALIVE_LENGTH); } - } else if (buf.getUnsignedShort(buf.readerIndex()) == 0x4050 && buf.getByte(buf.readerIndex() + 2) != ',') { + } else if (buf.getUnsignedByte(buf.readerIndex()) == 0x40 && buf.getByte(buf.readerIndex() + 2) != ',') { if (buf.readableBytes() > 6) { int length = buf.getUnsignedShort(buf.readerIndex() + 4) + 4 + 2; diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java index 428b69cd9..56c00c7c1 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2020 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. @@ -50,10 +50,12 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { private static final int MIN_DATA_LENGTH = 40; private boolean longDate; - private boolean decimalFuel; + private final boolean decimalFuel; private boolean custom; private String form; + private ByteBuf photo; + private final Map<Integer, String> alarmMap = new HashMap<>(); public AtrackProtocolDecoder(Protocol protocol) { @@ -510,20 +512,34 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { return position; } - private List<Position> decodeBinary(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + private Position decodePhoto(DeviceSession deviceSession, ByteBuf buf, long id) { - buf.skipBytes(2); // prefix - buf.readUnsignedShort(); // checksum - buf.readUnsignedShort(); // length - int index = buf.readUnsignedShort(); + long time = buf.readUnsignedInt(); + int index = buf.readUnsignedByte(); + int count = buf.readUnsignedByte(); - long id = buf.readLong(); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); - if (deviceSession == null) { - return null; + if (photo == null) { + photo = Unpooled.buffer(); + } + photo.writeBytes(buf.readSlice(buf.readUnsignedShort())); + + if (index == count - 1) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, new Date(time * 1000)); + + position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(String.valueOf(id), photo, "jpg")); + photo.release(); + photo = null; + + return position; } - sendResponse(channel, remoteAddress, id, index); + return null; + } + + private List<Position> decodeBinary(DeviceSession deviceSession, ByteBuf buf) { List<Position> positions = new LinkedList<>(); @@ -613,7 +629,26 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { } else if (buf.getByte(buf.readerIndex() + 2) == ',') { return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim()); } else { - return decodeBinary(channel, remoteAddress, buf); + + String prefix = buf.readCharSequence(2, StandardCharsets.US_ASCII).toString(); + buf.readUnsignedShort(); // checksum + buf.readUnsignedShort(); // length + int index = buf.readUnsignedShort(); + + long id = buf.readLong(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + sendResponse(channel, remoteAddress, id, index); + + if (prefix.equals("@R")) { + return decodePhoto(deviceSession, buf, id); + } else { + return decodeBinary(deviceSession, buf); + } + } } diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java index bc74b6576..09bd3572f 100644 --- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java @@ -73,7 +73,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { content.writeByte(packetNumber); content.writeZero(11); - ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content); + ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, 0, content); channel.writeAndFlush(new NetworkMessage(reply, remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java index d22106a80..115884e2c 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -97,7 +97,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { String sentence = (String) msg; String type = sentence.substring(20, 22); - if (type.equals("TX") && channel != null) { + if ((type.equals("TX") || type.equals("MQ")) && channel != null) { channel.writeAndFlush(new NetworkMessage(sentence + "#", remoteAddress)); } diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java index c3fe7121e..613710587 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2020 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. @@ -35,6 +35,8 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.regex.Pattern; public class EelinkProtocolDecoder extends BaseProtocolDecoder { @@ -76,6 +78,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { case 0x08: case 0x09: return Position.ALARM_GPS_ANTENNA_CUT; + case 0x25: + return Position.ALARM_REMOVING; case 0x81: return Position.ALARM_LOW_SPEED; case 0x82: @@ -388,9 +392,29 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { deviceSession = getDeviceSession(channel, remoteAddress); } + List<Position> positions = new LinkedList<>(); + + while (buf.isReadable()) { + Position position = decodePackage(channel, remoteAddress, buf, uniqueId, deviceSession); + if (position != null) { + positions.add(position); + } + } + + if (!positions.isEmpty()) { + return positions.size() > 1 ? positions : positions.iterator().next(); + } else { + return null; + } + } + + protected Position decodePackage( + Channel channel, SocketAddress remoteAddress, ByteBuf buf, + String uniqueId, DeviceSession deviceSession) throws Exception { + buf.skipBytes(2); // header int type = buf.readUnsignedByte(); - buf.readShort(); // length + buf = buf.readSlice(buf.readUnsignedShort()); int index = buf.readUnsignedShort(); if (type != MSG_GPS && type != MSG_DATA) { diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index 9bc7cb504..fe42a44d7 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -60,7 +60,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+),") // altitude .number("(d+),") // odometer .number("d+,") // runtime - .number("(x{4,8}),") // status + .number("(x+),") // status .number("(x+)?,") // input .number("(x+)?,") // output .number("(d+)|") // mcc diff --git a/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java new file mode 100644 index 000000000..812f78d02 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; +import org.traccar.helper.DataConverter; + +import java.nio.charset.StandardCharsets; + +public class FutureWayFrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + if (buf.readableBytes() < 10) { + return null; + } + + int length = Unpooled.wrappedBuffer(DataConverter.parseHex( + buf.getCharSequence(buf.readerIndex() + 2, 8, StandardCharsets.US_ASCII).toString())).readInt() + 17; + + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java new file mode 100644 index 000000000..73b53ee12 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 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.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class FutureWayProtocol extends BaseProtocol { + + public FutureWayProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new FutureWayFrameDecoder()); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new FutureWayProtocolDecoder(FutureWayProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java new file mode 100644 index 000000000..4b0f46e34 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.helper.DataConverter; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +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.util.regex.Pattern; + +public class FutureWayProtocolDecoder extends BaseProtocolDecoder { + + public FutureWayProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN_GPS = new PatternBuilder() + .text("GPS:") + .expression("([AV]),") // validity + .number("(dd)(dd)(dd)") // date (yymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(d+.d+)([NS]),") // latitude + .number("(d+.d+)([EW]),") // longitude + .number("(d+.d+),") // speed + .number("(d+.d+)") // course + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + ByteBuf header = Unpooled.wrappedBuffer(DataConverter.parseHex(sentence.substring(0, 16))); + sentence = sentence.substring(16, sentence.length() - 4); + + header.readUnsignedByte(); // header + header.readUnsignedInt(); // length + int type = header.readUnsignedByte(); + header.readUnsignedShort(); // index + + if (type == 0x20) { + + getDeviceSession(channel, remoteAddress, sentence.split(",")[1].substring(5)); + + } else if (type == 0xA0) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + Network network = new Network(); + + for (String line : sentence.split("\r\n")) { + + if (line.startsWith("GPS")) { + + Parser parser = new Parser(PATTERN_GPS, line); + if (!parser.matches()) { + return null; + } + + position.setValid(parser.next().equals("A")); + position.setTime(parser.nextDateTime()); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + } else if (line.startsWith("WIFI")) { + + for (String item : line.substring(line.indexOf(',') + 1).split("&")) { + String[] values = item.split("\\|"); + network.addWifiAccessPoint( + WifiAccessPoint.from(values[1].replace('-', ':'), Integer.parseInt(values[2]))); + } + + } else if (line.startsWith("LBS")) { + + String[] values = line.substring("LBS:".length()).split(","); + network.addCellTower(CellTower.from( + Integer.parseInt(values[0]), + Integer.parseInt(values[1]), + Integer.parseInt(values[3]), + Integer.parseInt(values[2]))); + + } + + } + + if (!network.getCellTowers().isEmpty() || !network.getWifiAccessPoints().isEmpty()) { + position.setNetwork(network); + } + + if (position.getFixTime() == null) { + getLastLocation(position, null); + } + + return position; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 283dbeb37..ec569b71b 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 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. @@ -45,7 +45,7 @@ import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - private boolean ignoreFixTime; + private final boolean ignoreFixTime; public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); @@ -190,6 +190,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .expression(PATTERN_LOCATION.pattern()) .expression(")+)") .groupBegin() + .number("d{1,2},,") + .number("(d{1,3}),") // battery + .number("[01],") // mode + .number("(?:[01])?,") // motion + .number("(?:-?d{1,2}.d)?,") // temperature + .or() .number("(d{1,7}.d)?,") // odometer .number("(d{5}:dd:dd)?,") // hour meter .number("(x+)?,") // adc 1 @@ -226,6 +232,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .expression("((?:") .expression(PATTERN_LOCATION.pattern()) .expression(")+)") + .groupBegin() .number("(d{1,7}.d)?,") // odometer .number("(d{5}:dd:dd)?,") // hour meter .number("(x+)?,") // adc 1 @@ -233,6 +240,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .number("(d{1,3})?,") // battery .number("(?:(xx)(xx)(xx))?,") // device status .expression("(.*)") // additional data + .or() + .number("d*,,") + .number("(d+),") // battery + .any() + .groupEnd() .number("(dddd)(dd)(dd)") // date (yyyymmdd) .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) .text(",") @@ -789,8 +801,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } else if (BitUtil.check(ignition, 5)) { position.set(Position.KEY_IGNITION, true); } - position.set(Position.KEY_INPUT, parser.nextHexInt()); - position.set(Position.KEY_OUTPUT, parser.nextHexInt()); + int input = parser.nextHexInt(); + int output = parser.nextHexInt(); + position.set(Position.KEY_INPUT, input); + position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2)); + position.set(Position.KEY_OUTPUT, output); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1)); } } @@ -835,6 +853,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + } + + if (parser.hasNext()) { position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); } position.set(Position.KEY_HOURS, parseHours(parser.next())); @@ -897,49 +919,58 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (power != null) { position.set(Position.KEY_POWER, power * 0.001); } - position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); - position.set(Position.KEY_HOURS, parseHours(parser.next())); - position.set(Position.PREFIX_ADC + 1, parser.next()); - position.set(Position.PREFIX_ADC + 2, parser.next()); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - decodeStatus(position, parser); + if (parser.hasNext(9)) { - int index = 0; - String[] data = parser.next().split(","); + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + position.set(Position.KEY_HOURS, parseHours(parser.next())); + position.set(Position.PREFIX_ADC + 1, parser.next()); + position.set(Position.PREFIX_ADC + 2, parser.next()); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - index += 1; // device type + decodeStatus(position, parser); - if (BitUtil.check(mask, 0)) { - index += 1; // digital fuel sensor data - } + int index = 0; + String[] data = parser.next().split(","); + + index += 1; // device type + + if (BitUtil.check(mask, 0)) { + index += 1; // digital fuel sensor data + } - if (BitUtil.check(mask, 1)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // id - index += 1; // type - if (!data[index++].isEmpty()) { - position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); + if (BitUtil.check(mask, 1)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // id + index += 1; // type + if (!data[index++].isEmpty()) { + position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); + } } } - } - if (BitUtil.check(mask, 2)) { - index += 1; // can data - } + if (BitUtil.check(mask, 2)) { + index += 1; // can data + } - if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // type - if (BitUtil.check(mask, 3)) { - position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); - } - if (BitUtil.check(mask, 4)) { - index += 1; // volume + if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // type + if (BitUtil.check(mask, 3)) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); + } + if (BitUtil.check(mask, 4)) { + index += 1; // volume + } } } + + } + + if (parser.hasNext()) { + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); } decodeDeviceTime(position, parser); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java index 7243be72a..e86b5dc30 100644 --- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 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. @@ -21,16 +21,22 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.model.Command; public class GlobalSatProtocol extends BaseProtocol { public GlobalSatProtocol() { + setSupportedDataCommands( + Command.TYPE_CUSTOM, + Command.TYPE_ALARM_DISMISS, + Command.TYPE_OUTPUT_CONTROL); addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); + pipeline.addLast(new GlobalSatProtocolEncoder(GlobalSatProtocol.this)); pipeline.addLast(new GlobalSatProtocolDecoder(GlobalSatProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java new file mode 100644 index 000000000..4f56274da --- /dev/null +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020 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 org.traccar.Protocol; +import org.traccar.StringProtocolEncoder; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class GlobalSatProtocolEncoder extends StringProtocolEncoder { + + public GlobalSatProtocolEncoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object encodeCommand(Command command) { + + String formattedCommand = null; + + switch (command.getType()) { + case Command.TYPE_CUSTOM: + formattedCommand = formatCommand( + command, "GSC,%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA); + break; + case Command.TYPE_ALARM_DISMISS: + formattedCommand = formatCommand( + command, "GSC,%s,Na", Command.KEY_UNIQUE_ID); + break; + case Command.TYPE_OUTPUT_CONTROL: + formattedCommand = formatCommand( + command, "GSC,%s,Lo(%s,%s)", Command.KEY_UNIQUE_ID, Command.KEY_INDEX, Command.KEY_DATA); + break; + default: + break; + } + + if (formattedCommand != null) { + return formattedCommand + Checksum.nmea(formattedCommand) + '!'; + } else { + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java index de23ea170..b742d0cac 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java @@ -60,9 +60,9 @@ import java.util.List; public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { - private DocumentBuilder documentBuilder; - private XPath xPath; - private XPathExpression messageExpression; + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; public GlobalstarProtocolDecoder(Protocol protocol) { super(protocol); @@ -161,17 +161,20 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { position.setLongitude(position.getLongitude() - 360); } - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + int speed = buf.readUnsignedByte(); + position.setSpeed(UnitsConverter.knotsFromKph(speed)); position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7)); - positions.add(position); + if (speed != 0xff) { + positions.add(position); + } } } sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue()); - return positions; + return !positions.isEmpty() ? positions : null; } } diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java index e00d83061..9b672cacc 100644 --- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -408,7 +408,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { } } - if (sentence.substring(21, 21 + 2).equals("vr")) { + if (sentence.startsWith("vr", 21)) { return decodePhoto(channel, remoteAddress, sentence); } else if (sentence.substring(21, 21 + 3).contains("OBD")) { return decodeObd(channel, remoteAddress, sentence); diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java index cc187225b..c158d3212 100644 --- a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java @@ -69,7 +69,8 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { private void send(Channel channel, SocketAddress remoteAddress, String message) { if (channel != null) { - channel.writeAndFlush(new NetworkMessage(message + Checksum.nmea(message) + "\r\n", remoteAddress)); + channel.writeAndFlush(new NetworkMessage( + message + Checksum.nmea(message.substring(1)) + "\r\n", remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 2c2ca7365..e003db51a 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -59,6 +59,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_LBS = 0x11; public static final int MSG_GPS_LBS_1 = 0x12; public static final int MSG_GPS_LBS_2 = 0x22; + public static final int MSG_GPS_LBS_3 = 0x37; public static final int MSG_STATUS = 0x13; public static final int MSG_SATELLITE = 0x14; public static final int MSG_STRING = 0x15; @@ -87,7 +88,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_COMMAND_2 = 0x82; public static final int MSG_TIME_REQUEST = 0x8A; public static final int MSG_INFO = 0x94; - public static final int MSG_RFID = 0x9B; + public static final int MSG_SERIAL = 0x9B; public static final int MSG_STRING_INFO = 0x21; public static final int MSG_GPS_2 = 0xA0; public static final int MSG_LBS_2 = 0xA1; @@ -114,6 +115,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS: case MSG_GPS_LBS_1: case MSG_GPS_LBS_2: + case MSG_GPS_LBS_3: case MSG_GPS_LBS_STATUS_1: case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: @@ -134,6 +136,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_LBS_STATUS: case MSG_GPS_LBS_1: case MSG_GPS_LBS_2: + case MSG_GPS_LBS_3: case MSG_GPS_LBS_STATUS_1: case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: @@ -736,57 +739,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (isSupported(type)) { - if (hasGps(type)) { - decodeGps(position, buf, false, deviceSession.getTimeZone()); - } else { - getLastLocation(position, null); - } - - if (hasLbs(type)) { - decodeLbs(position, buf, hasStatus(type)); - } - - if (hasStatus(type)) { - decodeStatus(position, buf); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); - buf.readUnsignedByte(); // alarm - buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) { - int mask = buf.readUnsignedShort(); - position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); - if (BitUtil.check(mask, 8 + 4)) { - int value = BitUtil.to(mask, 8 + 1); - if (BitUtil.check(mask, 8 + 1)) { - value = -value; - } - position.set(Position.PREFIX_TEMP + 1, value); - } else { - int value = BitUtil.to(mask, 8 + 2); - if (BitUtil.check(mask, 8 + 5)) { - position.set(Position.PREFIX_ADC + 1, value); - } else { - position.set(Position.PREFIX_ADC + 1, value * 0.1); - } - } - } - - if (type == MSG_GPS_LBS_2 && buf.readableBytes() == 3 + 6) { - position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); - position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason - position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); - } - - if (buf.readableBytes() == 4 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - } + decodeBasicUniversal(buf, deviceSession, type, position); } else if (type == MSG_ALARM) { boolean extendedAlarm = dataLength > 7; @@ -850,6 +803,80 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; } + private void decodeBasicUniversal(ByteBuf buf, DeviceSession deviceSession, int type, Position position) { + + if (hasGps(type)) { + decodeGps(position, buf, false, deviceSession.getTimeZone()); + } else { + getLastLocation(position, null); + } + + if (hasLbs(type)) { + decodeLbs(position, buf, hasStatus(type)); + } + + if (hasStatus(type)) { + decodeStatus(position, buf); + } + + if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); + buf.readUnsignedByte(); // alarm + buf.readUnsignedByte(); // swiped + position.set("driverLicense", data.trim()); + } + + if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) { + int mask = buf.readUnsignedShort(); + position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); + if (BitUtil.check(mask, 8 + 4)) { + int value = BitUtil.to(mask, 8 + 1); + if (BitUtil.check(mask, 8 + 1)) { + value = -value; + } + position.set(Position.PREFIX_TEMP + 1, value); + } else { + int value = BitUtil.to(mask, 8 + 2); + if (BitUtil.check(mask, 8 + 5)) { + position.set(Position.PREFIX_ADC + 1, value); + } else { + position.set(Position.PREFIX_ADC + 1, value * 0.1); + } + } + } + + if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3) && buf.readableBytes() >= 3 + 6) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason + position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); + } + + if (type == MSG_GPS_LBS_3) { + int module = buf.readUnsignedShort(); + int length = buf.readUnsignedByte(); + switch (module) { + case 0x0027: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 0x002E: + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + break; + case 0x003B: + position.setAccuracy(buf.readUnsignedShort() * 0.01); + break; + default: + buf.skipBytes(length); + break; + } + } + + if (buf.readableBytes() == 4 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + } + } + private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); @@ -1026,18 +1053,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (type == MSG_RFID) { - - getLastLocation(position, null); - - buf.readUnsignedByte(); // external device type code - buf.readUnsignedByte(); // card type - position.set( - Position.KEY_DRIVER_UNIQUE_ID, - buf.readCharSequence(buf.readableBytes() - 9, StandardCharsets.US_ASCII).toString()); - - return position; - } else if (type == MSG_GPS_MODULAR) { return decodeExtendedModular(buf, deviceSession); @@ -1205,6 +1220,27 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == MSG_SERIAL) { + + position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, null); + + buf.readUnsignedByte(); // external device type code + int length = buf.readableBytes() - 9; // line break + checksum + index + checksum + footer + if (length < 8) { + position.set( + Position.PREFIX_TEMP + 1, + Double.parseDouble(buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString())); + } else { + buf.readUnsignedByte(); // card type + position.set( + Position.KEY_DRIVER_UNIQUE_ID, + buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString()); + } + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java index 8d39abead..f1d146bda 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java @@ -190,6 +190,9 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.set( Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); break; + case 0x0011: + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05); + break; case 0x0020: String[] cells = buf.readCharSequence( length, StandardCharsets.US_ASCII).toString().split("\\+"); diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 841e2235d..b9b156f93 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -57,13 +57,17 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { public static final int RESULT_SUCCESS = 0; - public static ByteBuf formatMessage(int type, ByteBuf id, ByteBuf data) { + public static ByteBuf formatMessage(int type, ByteBuf id, boolean shortIndex, ByteBuf data) { ByteBuf buf = Unpooled.buffer(); buf.writeByte(0x7e); buf.writeShort(type); buf.writeShort(data.readableBytes()); buf.writeBytes(id); - buf.writeShort(0); // index + if (shortIndex) { + buf.writeByte(1); + } else { + buf.writeShort(1); + } buf.writeBytes(data); data.release(); buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1))); @@ -79,18 +83,18 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_GENERAL_RESPONSE, id, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE, id, false, response), remoteAddress)); } } private void sendGeneralResponse2( - Channel channel, SocketAddress remoteAddress, ByteBuf id, int index) { + Channel channel, SocketAddress remoteAddress, ByteBuf id, int type) { if (channel != null) { ByteBuf response = Unpooled.buffer(); - response.writeShort(index); + response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_GENERAL_RESPONSE_2, id, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE_2, id, true, response), remoteAddress)); } } @@ -161,7 +165,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { response.writeByte(RESULT_SUCCESS); response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII)); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress)); + formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); } } else if (type == MSG_TERMINAL_AUTH) { @@ -170,12 +174,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_LOCATION_REPORT) { + sendGeneralResponse(channel, remoteAddress, id, type, index); + return decodeLocation(deviceSession, buf); } else if (type == MSG_LOCATION_REPORT_2 || type == MSG_LOCATION_REPORT_BLIND) { if (BitUtil.check(attribute, 15)) { - sendGeneralResponse2(channel, remoteAddress, id, index); + sendGeneralResponse2(channel, remoteAddress, id, type); } return decodeLocation2(deviceSession, buf, type); @@ -296,11 +302,24 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0xD3: position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); break; + case 0xD4: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; + case 0xD5: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; + case 0xDA: + buf.readUnsignedShort(); // string cut count + int deviceStatus = buf.readUnsignedByte(); + position.set("string", BitUtil.check(deviceStatus, 0)); + position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2)); + position.set("cover", BitUtil.check(deviceStatus, 3)); + break; case 0xEB: while (buf.readerIndex() < endIndex) { - int tenetLength = buf.readUnsignedShort(); - int tenetType = buf.readUnsignedShort(); - switch (tenetType) { + int extendedLength = buf.readUnsignedShort(); + int extendedType = buf.readUnsignedShort(); + switch (extendedType) { case 0x0001: position.set("fuel1", buf.readUnsignedShort() * 0.1); buf.readUnsignedByte(); // unused @@ -309,8 +328,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set("fuel2", Double.parseDouble( buf.readCharSequence(6, StandardCharsets.US_ASCII).toString())); break; + case 0x00CE: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; default: - buf.skipBytes(tenetLength - 2); + buf.skipBytes(extendedLength - 2); break; } } diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java index 40d07230d..55c1e0c3b 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2020 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. @@ -50,22 +50,22 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder { data.writeByte(0x01); data.writeBytes(time); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data); } else { data.writeByte(0xf0); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); } case Command.TYPE_ENGINE_RESUME: if (alternative) { data.writeByte(0x00); data.writeBytes(time); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data); } else { data.writeByte(0xf1); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); } default: return null; diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java index 897606270..930d4f23b 100644 --- a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2020 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. @@ -44,7 +44,7 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { .number("(d+.?d*),") // course .number("(-?d+.?d*),") // altitude .number("(d+),") // satellites - .number("(d+),") // index + .number("(d+),") // event .number("(d+),") // input .number("(d+),?") // output .number("(d+.d+)?,?") // adc1 @@ -65,6 +65,30 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private String decodeAlarm(int value) { + switch (value) { + case 164: + return Position.ALARM_GEOFENCE_ENTER; + case 165: + return Position.ALARM_GEOFENCE_EXIT; + case 168: + case 169: + return Position.ALARM_LOW_POWER; + case 170: + return Position.ALARM_POWER_OFF; + case 176: + return Position.ALARM_POWER_RESTORED; + case 180: + return Position.ALARM_FALL_DOWN; + case 225: + return Position.ALARM_JAMMING; + case 995: + return Position.ALARM_SOS; + default: + return null; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -74,12 +98,12 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); position.setTime(parser.nextDateTime()); @@ -92,7 +116,11 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextDouble()); position.set(Position.KEY_SATELLITES, parser.nextInt()); - position.set(Position.KEY_INDEX, parser.nextLong()); + + int event = parser.nextInt(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + position.set(Position.KEY_INPUT, parser.nextInt()); position.set(Position.KEY_OUTPUT, parser.nextInt()); @@ -100,16 +128,18 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); // J1939 data - position.set(Position.KEY_OBD_SPEED, parser.nextInt()); - position.set(Position.KEY_RPM, parser.nextInt()); - position.set("coolant", parser.nextInt()); - position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt()); - position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); - position.set("chargerPressure", parser.nextInt()); - position.set("tpl", parser.nextInt()); - position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt()); - position.set(Position.KEY_OBD_ODOMETER, parser.nextInt()); + if (parser.hasNext(10)) { + position.set(Position.KEY_OBD_SPEED, parser.nextInt()); + position.set(Position.KEY_RPM, parser.nextInt()); + position.set("coolant", parser.nextInt()); + position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); + position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); + position.set("chargerPressure", parser.nextInt()); + position.set("tpl", parser.nextInt()); + position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt()); + position.set(Position.KEY_OBD_ODOMETER, parser.nextInt()); + } return position; } diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java index f66669a98..94c9a3038 100644 --- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java @@ -90,6 +90,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+),") // tilt x .or() .number("d+,") // index + .number("(d+.?d*),") // odometer + .number("x+,") // checksum + .or() + .number("d+,") // index .number("(d+.?d*),") // adc1 .number("(d+.?d*),") // adc2 .groupEnd("?") @@ -244,6 +248,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder { position.set("tiltX", parser.nextInt()); } + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } + if (parser.hasNext(2)) { position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java index 9868de435..5b5eb7d60 100644 --- a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java @@ -138,7 +138,7 @@ public class L100ProtocolDecoder extends BaseProtocolDecoder { String sentence = (String) msg; if (sentence.startsWith("L") || sentence.startsWith("H")) { - if (sentence.substring(2, 8).equals("ATLOBD")) { + if (sentence.startsWith("ATLOBD", 2)) { return decodeObdData(channel, remoteAddress, sentence); } else { return decodeObdLocation(channel, remoteAddress, sentence); diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java index 0c72568f3..4abb75025 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java @@ -152,7 +152,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { if (responseCode != null) { String response = "$AVCFG," + devicePassword + "," + responseCode; - response += Checksum.nmea(response) + "\r\n"; + response += Checksum.nmea(response.substring(1)) + "\r\n"; channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } @@ -163,7 +163,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { if (Character.isLowerCase(status.charAt(0))) { String response = "$EAVACK," + event + "," + checksum; - response += Checksum.nmea(response) + "\r\n"; + response += Checksum.nmea(response.substring(1)) + "\r\n"; channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java index 0c9f8ebb8..aaa5a70f7 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java @@ -29,7 +29,7 @@ public class LaipacProtocolEncoder extends StringProtocolEncoder { @Override protected String formatCommand(Command command, String format, String... keys) { String sentence = super.formatCommand(command, "$" + format, keys); - sentence += Checksum.nmea(sentence) + "\r\n"; + sentence += Checksum.nmea(sentence.substring(1)) + "\r\n"; return sentence; } diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java index 529496928..c43f1ea83 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -177,8 +177,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(new Network(CellTower.from( parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi))); - position.set(Position.KEY_OUTPUT, parser.nextHexInt()); position.set(Position.KEY_INPUT, parser.nextHexInt()); + position.set(Position.KEY_OUTPUT, parser.nextHexInt()); for (int i = 1; i <= 3; i++) { position.set(Position.PREFIX_ADC + i, parser.nextHexInt()); @@ -421,6 +421,22 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x1A: position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01); break; + case 0x91: + case 0x92: + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE()); + break; + case 0x98: + position.set(Position.KEY_FUEL_USED, buf.readUnsignedShortLE()); + break; + case 0x99: + position.set(Position.KEY_RPM, buf.readUnsignedShortLE()); + break; + case 0x9C: + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedShortLE()); + break; + case 0xC9: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE()); + break; default: buf.readUnsignedShortLE(); break; @@ -440,9 +456,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x04: position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01 break; + case 0x0C: + position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); + break; case 0x0D: position.set("runtime", buf.readUnsignedIntLE()); break; + case 0xA0: + position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001); + break; + case 0xA2: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE() * 0.01); + break; default: buf.readUnsignedIntLE(); break; diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java index c8d64fd81..9fd9666e4 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java @@ -15,7 +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; @@ -28,7 +27,6 @@ public class MictrackProtocol extends BaseProtocol { addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { - pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java index 0f815ca7b..5ea9f148c 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java @@ -66,6 +66,8 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder { private String decodeAlarm(int event) { switch (event) { + case 0: + return Position.ALARM_POWER_ON; case 5: return Position.ALARM_SOS; case 8: @@ -209,8 +211,12 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder { } private Object decodeLowAltitude( - Channel channel, SocketAddress remoteAddress, String sentence) throws Exception { - String deviceId = sentence.substring(0, sentence.indexOf("$")); + Channel channel, SocketAddress remoteAddress, String sentence) { + int separator = sentence.indexOf("$"); + if (separator < 0) { + return null; + } + String deviceId = sentence.substring(0, separator); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); if (deviceSession == null) { diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index b8ab134c5..5b04992ec 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 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. @@ -34,6 +34,10 @@ import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { @@ -100,18 +104,33 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { if (type == MSG_DATA) { + List<Position> positions = new LinkedList<>(); + Set<Integer> keys = new HashSet<>(); + boolean hasLocation = false; Position position = new Position(getProtocolName()); + DeviceSession deviceSession = null; + while (buf.isReadable()) { int endIndex = buf.readUnsignedByte() + buf.readerIndex(); int key = buf.readUnsignedByte(); + + if (keys.contains(key)) { + if (!hasLocation) { + getLastLocation(position, null); + } + positions.add(position); + keys.clear(); + hasLocation = false; + position = new Position(getProtocolName()); + } + keys.add(key); + switch (key) { case 0x01: - DeviceSession deviceSession = getDeviceSession( + deviceSession = getDeviceSession( channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString()); - if (deviceSession == null) { - return null; - } + position.setDeviceId(deviceSession.getDeviceId()); break; case 0x02: @@ -122,6 +141,7 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001); break; case 0x20: + hasLocation = true; position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); @@ -169,6 +189,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24)); position.set(Position.KEY_STATUS, status); break; + case 0x30: + buf.readUnsignedInt(); // timestamp + position.set(Position.KEY_STEPS, buf.readUnsignedInt()); + break; case 0x40: buf.readUnsignedIntLE(); // timestamp int heartRate = buf.readUnsignedByte(); @@ -182,11 +206,20 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { buf.readerIndex(endIndex); } - if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) { + if (!hasLocation) { getLastLocation(position, null); } + positions.add(position); + + if (deviceSession != null) { + for (Position p : positions) { + p.setDeviceId(deviceSession.getDeviceId()); + } + } else { + return null; + } - return position.getDeviceId() > 0 ? position : null; + return positions; } diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java new file mode 100644 index 000000000..7b554266f --- /dev/null +++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class MoovboxProtocol extends BaseProtocol { + + public MoovboxProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new MoovboxProtocolDecoder(MoovboxProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java new file mode 100644 index 000000000..3116d073c --- /dev/null +++ b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java @@ -0,0 +1,106 @@ +/* + * Copyright 2020 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 com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.model.Position; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.net.SocketAddress; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class MoovboxProtocolDecoder extends BaseHttpProtocolDecoder { + + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; + + public MoovboxProtocolDecoder(Protocol protocol) { + super(protocol); + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + builderFactory.setXIncludeAware(false); + builderFactory.setExpandEntityReferences(false); + documentBuilder = builderFactory.newDocumentBuilder(); + xPath = XPathFactory.newInstance().newXPath(); + messageExpression = xPath.compile("//gps/coordinates/coordinate"); + } catch (ParserConfigurationException | XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + + Document document = documentBuilder.parse(new ByteBufferBackedInputStream(request.content().nioBuffer())); + + String id = document.getDocumentElement().getAttribute("id"); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + NodeList nodes = (NodeList) messageExpression.evaluate(document, XPathConstants.NODESET); + List<Position> positions = new LinkedList<>(); + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(new Date(Long.parseLong(xPath.evaluate("time", node)) * 1000)); + position.setLatitude(Double.parseDouble(xPath.evaluate("longitude", node))); + position.setLongitude(Double.parseDouble(xPath.evaluate("latitude", node))); + position.setAltitude(Double.parseDouble(xPath.evaluate("altitude", node))); + position.setSpeed(Double.parseDouble(xPath.evaluate("speed", node))); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(xPath.evaluate("satellites", node))); + + positions.add(position); + } + + sendResponse(channel, HttpResponseStatus.OK); + return positions; + } + +} diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java new file mode 100644 index 000000000..c114d19fc --- /dev/null +++ b/src/main/java/org/traccar/protocol/NetProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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.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; + +public class NetProtocol extends BaseProtocol { + + public NetProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '!')); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new NetProtocolDecoder(NetProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java new file mode 100644 index 000000000..61cb29b6b --- /dev/null +++ b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020 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.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class NetProtocolDecoder extends BaseProtocolDecoder { + + public NetProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("@L") + .number("ddd") + .number("(d{15})") // imei + .number("xx") + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd)") // time (hhmmss) + .number("(x)") // flags + .number("(dd)(dd)(dddd)") // latitude + .number("(ddd)(dd)(dddd)") // longitude + .number("(x{8})") // status + .number("(x{4})") // speed + .number("(x{6})") // odometer + .number("(xxx)") // course + .number("(xxx)") // alarm + .any() + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + int flags = parser.nextHexInt(); + + position.setValid(BitUtil.check(flags, 3)); + int hemisphereLatitude = BitUtil.check(flags, 1) ? -1 : 1; + int hemisphereLongitude = BitUtil.check(flags, 0) ? -1 : 1; + + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLatitude); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLongitude); + + position.set(Position.KEY_STATUS, parser.nextHexLong()); + position.setSpeed(parser.nextHexInt() * 0.01); + position.set(Position.KEY_ODOMETER, parser.nextHexInt()); + position.setCourse(parser.nextHexInt()); + + parser.nextHexInt(); // alarm + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java index 58de0d38f..47c6e2ffd 100644 --- a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java @@ -31,6 +31,7 @@ import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; public class NiotProtocolDecoder extends BaseProtocolDecoder { @@ -95,12 +96,65 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder { .setSecond(BcdUtil.readInteger(buf, 2)); position.setTime(dateBuilder.getDate()); - position.setValid(true); position.setLatitude(readCoordinate(buf)); position.setLongitude(readCoordinate(buf)); - position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4))); + BcdUtil.readInteger(buf, 4); // reserved position.setCourse(BcdUtil.readInteger(buf, 4)); + int statusX = buf.readUnsignedByte(); + position.setValid(BitUtil.check(statusX, 7)); + switch (BitUtil.between(statusX, 3, 5)) { + case 0b10: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case 0b01: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + default: + break; + } + + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + + int statusA = buf.readUnsignedByte(); + position.set(Position.KEY_IGNITION, !BitUtil.check(statusA, 7)); + if (!BitUtil.check(statusA, 6)) { + position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); + } + + buf.readUnsignedByte(); // statusB + buf.readUnsignedByte(); // statusC + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); + buf.readUnsignedByte(); // speed limit + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + buf.readUnsignedByte(); // sensor speed + buf.readUnsignedByte(); // reserved + buf.readUnsignedByte(); // reserved + + while (buf.readableBytes() > 4) { + int extendedLength = buf.readUnsignedShort(); + int extendedType = buf.readUnsignedShort(); + switch (extendedType) { + case 0x0001: + position.set(Position.KEY_ICCID, + buf.readCharSequence(20, StandardCharsets.US_ASCII).toString()); + break; + case 0x0002: + int statusD = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, BitUtil.check(statusD, 5) ? Position.ALARM_REMOVING : null); + position.set(Position.KEY_ALARM, BitUtil.check(statusD, 4) ? Position.ALARM_TAMPERING : null); + buf.readUnsignedByte(); // run mode + buf.readUnsignedByte(); // reserved + break; + default: + buf.skipBytes(extendedLength - 2); + break; + } + + } + return position; } diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java index 323d97fa3..509d14ae4 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java @@ -110,7 +110,7 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder { if (root.containsKey("t")) { String trigger = root.getString("t"); position.set("t", trigger); - Integer reportType = -1; + int reportType = -1; if (root.containsKey("rty")) { reportType = root.getInt("rty"); } @@ -148,8 +148,8 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder { } if (root.containsKey("anum")) { - Integer numberOfAnalogueInputs = root.getInt("anum"); - for (Integer i = 0; i < numberOfAnalogueInputs; i++) { + int numberOfAnalogueInputs = root.getInt("anum"); + for (int i = 0; i < numberOfAnalogueInputs; i++) { String indexString = String.format("%02d", i); if (root.containsKey("adda-" + indexString)) { position.set(Position.PREFIX_ADC + (i + 1), root.getString("adda-" + indexString)); diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java new file mode 100644 index 000000000..a3e548716 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PolteProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class PolteProtocol extends BaseProtocol { + + public PolteProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new PolteProtocolDecoder(PolteProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java new file mode 100644 index 000000000..ce45abef6 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java @@ -0,0 +1,84 @@ +/* + * Copyright 2020 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.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.model.Position; + +import javax.json.Json; +import javax.json.JsonObject; +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public class PolteProtocolDecoder extends BaseHttpProtocolDecoder { + + public PolteProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + String content = request.content().toString(StandardCharsets.UTF_8); + JsonObject json = Json.createReader(new StringReader(content)).readObject(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("ueToken")); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + if (json.containsKey("location")) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + JsonObject location = json.getJsonObject("location"); + + position.setValid(true); + position.setTime(new Date(location.getInt("detected_at") * 1000L)); + position.setLatitude(location.getJsonNumber("latitude").doubleValue()); + position.setLongitude(location.getJsonNumber("longitude").doubleValue()); + position.setAltitude(location.getJsonNumber("altitude").doubleValue()); + + if (json.containsKey("report")) { + JsonObject report = json.getJsonObject("report"); + position.set(Position.KEY_EVENT, report.getInt("event")); + if (report.containsKey("battery")) { + JsonObject battery = report.getJsonObject("battery"); + position.set(Position.KEY_BATTERY_LEVEL, battery.getInt("level")); + position.set(Position.KEY_BATTERY, battery.getJsonNumber("voltage").doubleValue()); + } + } + + return position; + + } + + sendResponse(channel, HttpResponseStatus.OK); + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/PstFrameEncoder.java b/src/main/java/org/traccar/protocol/PstFrameEncoder.java new file mode 100644 index 000000000..5a9ede3e6 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PstFrameEncoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class PstFrameEncoder extends MessageToByteEncoder<ByteBuf> { + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { + + out.writeByte('('); + while (msg.isReadable()) { + int b = msg.readUnsignedByte(); + if (b == 0x27 || b == 0x28 || b == 0x29) { + out.writeByte(0x27); + out.writeByte(b ^ 0x40); + } else { + out.writeByte(b); + } + } + out.writeByte(')'); + } +} diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java index 0ed9affd8..d8c7008cb 100644 --- a/src/main/java/org/traccar/protocol/PstProtocol.java +++ b/src/main/java/org/traccar/protocol/PstProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 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,20 +18,27 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.model.Command; public class PstProtocol extends BaseProtocol { public PstProtocol() { + setSupportedDataCommands( + Command.TYPE_ENGINE_STOP, + Command.TYPE_ENGINE_RESUME); addServer(new TrackerServer(true, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); pipeline.addLast(new PstProtocolDecoder(PstProtocol.this)); } }); addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new PstFrameEncoder()); pipeline.addLast(new PstFrameDecoder()); + pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); pipeline.addLast(new PstProtocolDecoder(PstProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java index 62cc203d2..e3fe1af62 100644 --- a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java @@ -38,6 +38,7 @@ public class PstProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_ACK = 0x00; public static final int MSG_STATUS = 0x05; + public static final int MSG_COMMAND = 0x06; private Date readDate(ByteBuf buf) { long value = buf.readUnsignedInt(); @@ -61,21 +62,13 @@ public class PstProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, long id, int version, long index, int type) { if (channel != null) { - ByteBuf content = Unpooled.buffer(); - content.writeInt((int) id); - content.writeByte(version); - content.writeInt((int) index); - content.writeByte(MSG_ACK); - content.writeByte(type); - - int checksum = Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer()); - ByteBuf response = Unpooled.buffer(); - response.writeByte('('); - response.writeBytes(content); - content.release(); - response.writeShort(checksum); - response.writeByte(')'); + response.writeInt((int) id); + response.writeByte(version); + response.writeInt((int) index); + response.writeByte(MSG_ACK); + response.writeByte(type); + response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer())); channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); diff --git a/src/main/java/org/traccar/protocol/PstProtocolEncoder.java b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java new file mode 100644 index 000000000..f3d193324 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class PstProtocolEncoder extends BaseProtocolEncoder { + + public PstProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(long deviceId, int type, int data1, int data2) { + + ByteBuf buf = Unpooled.buffer(); + + buf.writeInt((int) Long.parseLong(getUniqueId(deviceId))); + buf.writeByte(0x06); // version + + buf.writeInt(1); // index + buf.writeByte(PstProtocolDecoder.MSG_COMMAND); + buf.writeShort(type); + buf.writeShort(data1); + buf.writeShort(data2); + + buf.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer())); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + switch (command.getType()) { + case Command.TYPE_ENGINE_STOP: + return encodeContent(command.getDeviceId(), 0x0002, 0xffff, 0xffff); + case Command.TYPE_ENGINE_RESUME: + return encodeContent(command.getDeviceId(), 0x0001, 0xffff, 0xffff); + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java index 0afec67ad..ff92b51f1 100644 --- a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2012 Luis Parada (luis.parada@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -55,8 +55,8 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { .expression("([EW]),") .number("(d+.d+)?,") // speed .number("(d+.d+)?,") // course - .number("(dd)(dd)(dd),,,") // date (ddmmyy) - .expression("./") + .number("(dd)(dd)(dd),,,?") // date (ddmmyy) + .expression(".?/") .expression("([01])+,") // input .expression("([01])+/") // output .expression("([^/]+)?/") // adc @@ -108,7 +108,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); DateBuilder dateBuilder = new DateBuilder() - .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setValid(parser.next().equals("A")); position.setLatitude(parser.nextCoordinate()); @@ -116,7 +116,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(parser.nextDouble(0)); position.setCourse(parser.nextDouble(0)); - dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); position.set(Position.KEY_INPUT, parser.next()); diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java index b56a081c7..d87f77b84 100644 --- a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 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. @@ -70,6 +70,7 @@ public class RadarProtocolDecoder extends BaseProtocolDecoder { for (int index = 0; index < count; index++) { Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); position.set(Position.KEY_EVENT, buf.readUnsignedShort()); diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java index da36c2048..34417d95f 100644 --- a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java @@ -42,9 +42,9 @@ import java.util.List; public class SpotProtocolDecoder extends BaseHttpProtocolDecoder { - private DocumentBuilder documentBuilder; - private XPath xPath; - private XPathExpression messageExpression; + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; public SpotProtocolDecoder(Protocol protocol) { super(protocol); diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java index 2d1613e03..7ba41ad56 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java @@ -20,11 +20,13 @@ import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.Protocol; +import org.traccar.helper.DataConverter; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.protobuf.StarLinkMessage; import java.net.SocketAddress; import java.text.DateFormat; @@ -47,8 +49,8 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { .number("xx") // checksum .compile(); - private String[] dataTags; - private DateFormat dateFormat; + private String format; + private String dateFormat; public StarLinkProtocolDecoder(Protocol protocol) { super(protocol); @@ -60,13 +62,24 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { setDateFormat(Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss")); } + public String[] getFormat(long deviceId) { + return Context.getIdentityManager().lookupAttributeString( + deviceId, getProtocolName() + ".format", format, false, false).split(","); + } + public void setFormat(String format) { - dataTags = format.split(","); + this.format = format; + } + + public DateFormat getDateFormat(long deviceId) { + DateFormat dateFormat = new SimpleDateFormat(Context.getIdentityManager().lookupAttributeString( + deviceId, getProtocolName() + ".dateFormat", this.dateFormat, false, false)); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat; } public void setDateFormat(String dateFormat) { - this.dateFormat = new SimpleDateFormat(dateFormat); - this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + this.dateFormat = dateFormat; } private double parseCoordinate(String value) { @@ -128,6 +141,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { Integer lac = null, cid = null; int event = 0; + String[] dataTags = getFormat(deviceSession.getDeviceId()); + DateFormat dateFormat = getDateFormat(deviceSession.getDeviceId()); + for (int i = 0; i < Math.min(data.length, dataTags.length); i++) { if (data[i].isEmpty()) { continue; @@ -186,6 +202,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { cid = Integer.parseInt(data[i]); } break; + case "#CSS#": + position.set(Position.KEY_RSSI, Integer.parseInt(data[i])); + break; case "#VIN#": position.set(Position.KEY_POWER, Double.parseDouble(data[i])); break; @@ -201,6 +220,32 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { case "#ENG#": position.set("engine", data[i].equals("1")); break; + case "#SATU#": + position.set(Position.KEY_SATELLITES, Integer.parseInt(data[i])); + break; + case "#TS1#": + position.set("sensor1State", Integer.parseInt(data[i])); + break; + case "#TS2#": + position.set("sensor2State", Integer.parseInt(data[i])); + break; + case "#TD1#": + case "#TD2#": + StarLinkMessage.mEventReport_TDx message = + StarLinkMessage.mEventReport_TDx.parseFrom(DataConverter.parseBase64(data[i])); + position.set( + "sensor" + message.getSensorNumber() + "Id", + message.getSensorID()); + position.set( + "sensor" + message.getSensorNumber() + "Temp", + message.getTemperature() * 0.1); + position.set( + "sensor" + message.getSensorNumber() + "Humidity", + message.getTemperature() * 0.1); + position.set( + "sensor" + message.getSensorNumber() + "Voltage", + message.getVoltage() * 0.001); + break; default: break; } diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 451b9ba32..76e3e6ecc 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -479,20 +479,21 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setTime(dateFormat.parse(values[index++] + values[index++])); } + CellTower cellTower = new CellTower(); if (BitUtil.check(mask, 6)) { - index += 1; // cell + cellTower.setCellId(Long.parseLong(values[index++], 16)); } - if (BitUtil.check(mask, 7)) { - index += 1; // mcc + cellTower.setMobileCountryCode(Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 8)) { - index += 1; // mnc + cellTower.setMobileNetworkCode(Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 9)) { - index += 1; // lac + cellTower.setLocationAreaCode(Integer.parseInt(values[index++], 16)); + } + if (cellTower.getCellId() != null) { + position.setNetwork(new Network(cellTower)); } if (BitUtil.check(mask, 10)) { @@ -531,16 +532,26 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 19)) { - position.set("alertId", values[index++]); - } - - if (BitUtil.check(mask, 20)) { - position.set("alertModifier", values[index++]); - } - - if (BitUtil.check(mask, 21)) { - position.set("alertData", values[index++]); + if (type.equals("ALT")) { + if (BitUtil.check(mask, 19)) { + position.set("alertId", values[index++]); + } + if (BitUtil.check(mask, 20)) { + position.set("alertModifier", values[index++]); + } + if (BitUtil.check(mask, 21)) { + position.set("alertData", values[index++]); + } + } else { + if (BitUtil.check(mask, 19)) { + position.set("mode", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 20)) { + position.set("reason", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 21)) { + position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); + } } if (BitUtil.check(mask, 22)) { @@ -676,6 +687,24 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeTravelReport(Channel channel, SocketAddress remoteAddress, String[] values) { + int index = 1; + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_DRIVER_UNIQUE_ID, values[values.length - 1]); + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -693,6 +722,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (prefix.length() < 5) { return decodeUniversal(channel, remoteAddress, values); + } else if (prefix.endsWith("HTE")) { + return decodeTravelReport(channel, remoteAddress, values); } else if (prefix.startsWith("ST9")) { return decode9(channel, remoteAddress, values); } else if (prefix.startsWith("ST4")) { diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java index b75addfae..230d29216 100644 --- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 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. @@ -24,6 +24,7 @@ import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; @@ -109,6 +110,20 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_QZE = new PatternBuilder() + .text("QZE,") + .number("(d{15}),") // imei + .number("(d+),") // event + .number("(dd)(dd)(dddd),") // date (mmddyyyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+),") // latitude + .number("(-?d+.d+),") // longitude + .number("(d+),") // speed + .number("(d+),") // course + .expression("([AV]),") // validity + .expression("([01])") // ignition + .compile(); + private Position position = null; private Position decodeGprmc( @@ -256,6 +271,35 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeQze(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_QZE, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_EVENT, parser.nextInt()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + position.setCourse(parser.nextInt()); + position.setValid(parser.next().equals("A")); + + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -308,6 +352,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return decodeTrccr(deviceSession, sentence); } else if (sentence.startsWith("$GPIOP")) { return decodeGpiop(deviceSession, sentence); + } else if (sentence.startsWith("QZE")) { + return decodeQze(channel, remoteAddress, sentence); } return null; diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java index 3331ebb71..5822d6b8f 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java @@ -230,10 +230,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); int battery = BcdUtil.readInteger(buf, 2); - if (battery == 0) { - battery = 100; - } - position.set(Position.KEY_BATTERY, battery); + position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100); } @@ -284,7 +281,8 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { } position.set(Position.KEY_G_SENSOR, "[" + accelerationX + "," + accelerationY + "," + accelerationZ + "]"); - position.set(Position.KEY_BATTERY_LEVEL, BcdUtil.readInteger(buf, 2)); + int battery = BcdUtil.readInteger(buf, 2); + position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100); position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte()); position.set("lightSensor", BcdUtil.readInteger(buf, 2) * 0.1); position.set(Position.KEY_BATTERY, BcdUtil.readInteger(buf, 2) * 0.1); diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index 40b769869..0ea02b157 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -43,9 +43,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { private static final int IMAGE_PACKET_MAX = 2048; - private boolean connectionless; + private final boolean connectionless; private boolean extended; - private Map<Long, ByteBuf> photos = new HashMap<>(); + private final Map<Long, ByteBuf> photos = new HashMap<>(); public void setExtended(boolean extended) { this.extended = extended; @@ -208,7 +208,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false)); break; case 16: - case 87: position.set(Position.KEY_ODOMETER, readValue(buf, length, false)); break; case 17: @@ -223,9 +222,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 21: position.set(Position.KEY_RSSI, readValue(buf, length, false)); break; - case 24: - readValue(buf, length, false); // speed - break; case 25: case 26: case 27: @@ -255,34 +251,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 80: position.set("workMode", readValue(buf, length, false)); break; - case 81: - position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false)); - break; - case 82: - position.set(Position.KEY_THROTTLE, readValue(buf, length, false)); - break; - case 83: - position.set(Position.KEY_FUEL_USED, readValue(buf, length, false) * 0.1); - break; - case 84: - position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 0.1); - break; - case 85: - position.set(Position.KEY_RPM, readValue(buf, length, false)); - break; case 90: position.set(Position.KEY_DOOR, readValue(buf, length, false)); break; - case 110: - position.set(Position.KEY_FUEL_CONSUMPTION, readValue(buf, length, true) * 0.1); - break; - case 113: - if (length == 1) { - position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, true)); - } else { - position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); - } - break; case 115: position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1); break; @@ -311,9 +282,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 199: position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false)); break; - case 235: - position.set("oilLevel", readValue(buf, length, false)); - break; case 236: if (readValue(buf, length, false) == 1) { position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); @@ -569,6 +537,29 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { int length = buf.readUnsignedShort(); if (id == 256) { position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII)); + } else if (id == 385) { + ByteBuf data = buf.readSlice(length); + data.readUnsignedByte(); // data part + int index = 1; + while (data.isReadable()) { + int flags = data.readUnsignedByte(); + if (BitUtil.from(flags, 4) > 0) { + position.set("beacon" + index + "Uuid", ByteBufUtil.hexDump(data.readSlice(16))); + position.set("beacon" + index + "Major", data.readUnsignedShort()); + position.set("beacon" + index + "Minor", data.readUnsignedShort()); + } else { + position.set("beacon" + index + "Namespace", ByteBufUtil.hexDump(data.readSlice(10))); + position.set("beacon" + index + "Instance", ByteBufUtil.hexDump(data.readSlice(6))); + } + position.set("beacon" + index + "Rssi", (int) data.readByte()); + if (BitUtil.check(flags, 1)) { + position.set("beacon" + index + "Battery", data.readUnsignedShort() * 0.01); + } + if (BitUtil.check(flags, 2)) { + position.set("beacon" + index + "Temp", data.readUnsignedShort()); + } + index += 1; + } } else { position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length))); } diff --git a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java index 0342404a6..40267022b 100644 --- a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java @@ -62,7 +62,7 @@ public class V680ProtocolDecoder extends BaseProtocolDecoder { if (sentence.length() == 16) { - getDeviceSession(channel, remoteAddress, sentence.substring(1, sentence.length())); + getDeviceSession(channel, remoteAddress, sentence.substring(1)); } else { diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java index c132f194b..31a1bbb11 100644 --- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -91,7 +91,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { return length; } - private Map<Short, byte[]> formats = new HashMap<>(); + private final Map<Short, byte[]> formats = new HashMap<>(); public void setConfig(String configString) { Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)"); |