diff options
author | Ivan Muratov <binakot@gmail.com> | 2017-10-24 14:52:48 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-24 14:52:48 +0300 |
commit | db02157dbb29539dda4b51a5e8b317293cfc536c (patch) | |
tree | b974f082172406e16a92cb9da8136ef856f571a5 /src/org/traccar/protocol | |
parent | 09d3cf2b5416327700ad22b652cf4a0dca09aaf2 (diff) | |
parent | 96e15853b9c28bd31295ca2c014e226e4a50aaa1 (diff) | |
download | trackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.gz trackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.bz2 trackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.zip |
Merge branch 'master' into master
Diffstat (limited to 'src/org/traccar/protocol')
70 files changed, 3298 insertions, 1336 deletions
diff --git a/src/org/traccar/protocol/AdmProtocol.java b/src/org/traccar/protocol/AdmProtocol.java index 442121f0a..4d2cbe7b3 100644 --- a/src/org/traccar/protocol/AdmProtocol.java +++ b/src/org/traccar/protocol/AdmProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 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,8 +18,10 @@ package org.traccar.protocol; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; +import org.jboss.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.TrackerServer; +import org.traccar.model.Command; import java.nio.ByteOrder; import java.util.List; @@ -28,6 +30,9 @@ public class AdmProtocol extends BaseProtocol { public AdmProtocol() { super("adm"); + setSupportedDataCommands( + Command.TYPE_GET_DEVICE_STATUS, + Command.TYPE_CUSTOM); } @Override @@ -36,6 +41,8 @@ public class AdmProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, -3, 0)); + pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("objectEncoder", new AdmProtocolEncoder()); pipeline.addLast("objectDecoder", new AdmProtocolDecoder(AdmProtocol.this)); } }; diff --git a/src/org/traccar/protocol/AdmProtocolDecoder.java b/src/org/traccar/protocol/AdmProtocolDecoder.java index f4a21cad0..f93c55e18 100644 --- a/src/org/traccar/protocol/AdmProtocolDecoder.java +++ b/src/org/traccar/protocol/AdmProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2017 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. @@ -33,76 +33,75 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder { super(protocol); } + public static final int CMD_RESPONSE_SIZE = 0x84; public static final int MSG_IMEI = 0x03; public static final int MSG_PHOTO = 0x0A; public static final int MSG_ADM5 = 0x01; - @Override - protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - - ChannelBuffer buf = (ChannelBuffer) msg; - - buf.readUnsignedShort(); // device id - buf.readUnsignedByte(); // length - - int type = buf.readUnsignedByte(); - - DeviceSession deviceSession; - if (type == MSG_IMEI) { - deviceSession = getDeviceSession( - channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII)); - } else { - deviceSession = getDeviceSession(channel, remoteAddress); - } + private Position decodeData(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int type) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); if (deviceSession == null) { return null; } if (BitUtil.to(type, 2) == 0) { - Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte()); - buf.readUnsignedShort(); // index + position.set(Position.KEY_INDEX, buf.readUnsignedShort()); - position.set(Position.KEY_STATUS, buf.readUnsignedShort()); - - position.setValid(true); + int status = buf.readUnsignedShort(); + position.set(Position.KEY_STATUS, status); + position.setValid(!BitUtil.check(status, 5)); position.setLatitude(buf.readFloat()); position.setLongitude(buf.readFloat()); position.setCourse(buf.readUnsignedShort() * 0.1); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); - position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte()); - + position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte() * 0.1); position.setAltitude(buf.readUnsignedShort()); - position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); position.set(Position.KEY_SATELLITES, buf.readUnsignedByte() & 0x0f); position.setTime(new Date(buf.readUnsignedInt() * 1000)); - position.set(Position.KEY_POWER, buf.readUnsignedShort()); - position.set(Position.KEY_BATTERY, buf.readUnsignedShort()); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); if (BitUtil.check(type, 2)) { - buf.skipBytes(4); + buf.readUnsignedByte(); // vib + buf.readUnsignedByte(); // vib_count + + int out = buf.readUnsignedByte(); + for (int i = 0; i <= 3; i++) { + position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(out, i) ? 1 : 0); + } + + buf.readUnsignedByte(); // in_alarm } if (BitUtil.check(type, 3)) { - buf.skipBytes(12); + for (int i = 1; i <= 6; i++) { + position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.001); + } } if (BitUtil.check(type, 4)) { - buf.skipBytes(8); + for (int i = 1; i <= 2; i++) { + position.set(Position.PREFIX_COUNT + i, buf.readUnsignedInt()); + } } if (BitUtil.check(type, 5)) { - buf.skipBytes(9); + for (int i = 1; i <= 3; i++) { + buf.readUnsignedShort(); // fuel level + } + for (int i = 1; i <= 3; i++) { + position.set(Position.PREFIX_TEMP + i, buf.readUnsignedByte()); + } } if (BitUtil.check(type, 6)) { @@ -119,4 +118,46 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder { return null; } + private Position parseCommandResponse(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + int responseTextLength = buf.bytesBefore((byte) 0); + if (responseTextLength < 0) { + responseTextLength = CMD_RESPONSE_SIZE - 3; + } + position.set(Position.KEY_RESULT, buf.readBytes(responseTextLength).toString(StandardCharsets.UTF_8)); + + return position; + } + + @Override + protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.readUnsignedShort(); // device id + + int size = buf.readUnsignedByte(); + if (size != CMD_RESPONSE_SIZE) { + int type = buf.readUnsignedByte(); + if (type == MSG_IMEI) { + getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.UTF_8)); + } else { + return decodeData(channel, remoteAddress, buf, type); + } + } else { + return parseCommandResponse(channel, remoteAddress, buf); + } + + return null; + } + } diff --git a/src/org/traccar/protocol/AdmProtocolEncoder.java b/src/org/traccar/protocol/AdmProtocolEncoder.java new file mode 100644 index 000000000..8cbd8618d --- /dev/null +++ b/src/org/traccar/protocol/AdmProtocolEncoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Anatoliy Golubev (darth.naihil@gmail.com) + * + * 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.StringProtocolEncoder; +import org.traccar.helper.Log; +import org.traccar.model.Command; + +public class AdmProtocolEncoder extends StringProtocolEncoder { + + @Override + protected Object encodeCommand(Command command) { + + switch (command.getType()) { + case Command.TYPE_GET_DEVICE_STATUS: + return formatCommand(command, "STATUS\r\n"); + + case Command.TYPE_CUSTOM: + return formatCommand(command, "{%s}\r\n", Command.KEY_DATA); + + default: + Log.warning(new UnsupportedOperationException(command.getType())); + break; + } + + return null; + } +} diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java index eb8d77011..154451b5b 100644 --- a/src/org/traccar/protocol/AplicomProtocolDecoder.java +++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java @@ -263,7 +263,8 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { } if ((selector & 0x0200) != 0) { - position.set(Position.KEY_RFID, (((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, + String.valueOf(((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt()); } if ((selector & 0x0400) != 0) { @@ -351,15 +352,28 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_VIN, buf.readBytes(18).toString(StandardCharsets.US_ASCII).trim()); } + if ((selector & 0x2000) != 0) { + buf.readUnsignedByte(); // card 1 type + buf.readUnsignedByte(); // card 1 country code + String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim(); + if (!card.isEmpty()) { + position.set("card1", card); + } + } + + if ((selector & 0x4000) != 0) { + buf.readUnsignedByte(); // card 2 type + buf.readUnsignedByte(); // card 2 country code + String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim(); + if (!card.isEmpty()) { + position.set("card2", card); + } + } + if ((selector & 0x10000) != 0) { int count = buf.readUnsignedByte(); for (int i = 1; i <= count; i++) { - ChannelBuffer driver = buf.readBytes(22); - int endIndex = driver.indexOf(0, driver.writerIndex(), (byte) 0); - if (endIndex < 0) { - endIndex = driver.writerIndex(); - } - position.set("driver" + i, driver.toString(0, endIndex, StandardCharsets.US_ASCII).trim()); + position.set("driver" + i, buf.readBytes(22).toString(StandardCharsets.US_ASCII).trim()); position.set("driverTime" + i, buf.readUnsignedInt()); } } @@ -456,59 +470,59 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // length while (buf.readableBytes() > 0) { - position.set("towedPosition", buf.readUnsignedByte()); + buf.readUnsignedByte(); // towed position int type = buf.readUnsignedByte(); int length = buf.readUnsignedByte(); + int end = buf.readerIndex() + length; - if (type == 0x01) { - position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length))); - } else if (type == 0x02) { - position.set("wheelSpeed", buf.readUnsignedShort() / 256.0); - position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0); - position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5); - position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0); - } else if (type == 0x03) { - position.set("axleLoadSum", buf.readUnsignedShort() * 2); - } else if (type == 0x04) { - position.set("tyrePressure", buf.readUnsignedByte() * 10); - position.set("pneumaticPressure", buf.readUnsignedByte() * 5); - } else if (type == 0x05) { - position.set("brakeLining", buf.readUnsignedByte() * 0.4); - position.set("brakeTemperature", buf.readUnsignedByte() * 10); - } else if (type == 0x06) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5); - position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5); - position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5); - } else if (type == 0x0A) { - ChannelBuffer brakeData = buf.readBytes(length); - position.set("absStatusCounter", brakeData.readUnsignedShort()); - position.set("atvbStatusCounter", brakeData.readUnsignedShort()); - position.set("vdcActiveCounter", brakeData.readUnsignedShort()); - } else if (type == 0x0B) { - position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length))); - } else if (type == 0x0C) { - position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length))); - } else if (type == 0x0D) { - switch (buf.readUnsignedByte()) { - case 1: - position.set("brakeManufacturer", "Wabco"); - break; - case 2: - position.set("brakeManufacturer", "Knorr"); - break; - case 3: - position.set("brakeManufacturer", "Haldex"); - break; - default: - position.set("brakeManufacturer", "Unknown"); - break; - } - buf.readUnsignedByte(); - position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII)); - position.set("towedDetectionStatus", buf.readUnsignedByte()); - } else if (type == 0x0E) { - buf.skipBytes(length); + switch (type) { + case 0x01: + position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length))); + break; + case 0x02: + position.set("wheelSpeed", buf.readUnsignedShort() / 256.0); + position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0); + position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5); + position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0); + break; + case 0x03: + position.set("axleLoadSum", buf.readUnsignedShort() * 2); + break; + case 0x04: + position.set("tyrePressure", buf.readUnsignedByte() * 10); + position.set("pneumaticPressure", buf.readUnsignedByte() * 5); + break; + case 0x05: + position.set("brakeLining", buf.readUnsignedByte() * 0.4); + position.set("brakeTemperature", buf.readUnsignedByte() * 10); + break; + case 0x06: + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5L); + position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5L); + position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5L); + break; + case 0x0A: + position.set("absStatusCounter", buf.readUnsignedShort()); + position.set("atvbStatusCounter", buf.readUnsignedShort()); + position.set("vdcActiveCounter", buf.readUnsignedShort()); + break; + case 0x0B: + position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length))); + break; + case 0x0C: + position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length))); + break; + case 0x0D: + buf.readUnsignedByte(); + position.set("towedDetectionStatus", buf.readUnsignedInt()); + buf.skipBytes(17); // vin + break; + case 0x0E: + default: + break; } + + buf.readerIndex(end); } } @@ -554,7 +568,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte()); position.set("speedMax", buf.readUnsignedByte()); position.set("speedMin", buf.readUnsignedByte()); - position.set("hardBreaking", buf.readUnsignedByte()); + position.set("hardBraking", buf.readUnsignedByte()); } if ((selector & 0x0200) != 0) { diff --git a/src/org/traccar/protocol/AquilaProtocolDecoder.java b/src/org/traccar/protocol/AquilaProtocolDecoder.java index 5ff974a7d..773210b04 100644 --- a/src/org/traccar/protocol/AquilaProtocolDecoder.java +++ b/src/org/traccar/protocol/AquilaProtocolDecoder.java @@ -81,7 +81,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder { .number("[01],") // corner packet .number("(?:d+,){6}") // reserved .number("[01],") // hard acceleration - .number("[01],") // hard breaking + .number("[01],") // hard braking .number("[01],[01],[01],[01],") // course bits .number("(d+),") // external voltage .number("(d+),") // internal voltage @@ -115,7 +115,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder { .number("[01],") // do 1 .number("[01],") // reserved .number("[01],") // hard acceleration - .number("[01],") // hard breaking + .number("[01],") // hard braking .number("(?:[01],){4}") // reserved .number("(d+),") // external voltage .number("(d+),") // internal voltage diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/org/traccar/protocol/AstraProtocolDecoder.java index ea6aa7b30..8d86cd2be 100644 --- a/src/org/traccar/protocol/AstraProtocolDecoder.java +++ b/src/org/traccar/protocol/AstraProtocolDecoder.java @@ -105,7 +105,7 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // geofence events if (BitUtil.check(status, 8)) { - position.set(Position.KEY_RFID, buf.readBytes(7).toString(StandardCharsets.US_ASCII)); + position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readBytes(7).toString(StandardCharsets.US_ASCII)); position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium() * 1000); position.set(Position.KEY_HOURS, buf.readUnsignedShort()); } diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java index 79b3c36cc..23cb67e15 100644 --- a/src/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java @@ -327,7 +327,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OUTPUT, buf.readUnsignedByte()); position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001); - position.set("driver", readString(buf)); + position.set(Position.KEY_DRIVER_UNIQUE_ID, readString(buf)); position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1); position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1); diff --git a/src/org/traccar/protocol/CarscopProtocolDecoder.java b/src/org/traccar/protocol/CarscopProtocolDecoder.java index ac3df1cd7..2a081bcdd 100644 --- a/src/org/traccar/protocol/CarscopProtocolDecoder.java +++ b/src/org/traccar/protocol/CarscopProtocolDecoder.java @@ -44,8 +44,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder { .number("(ddd.d)") // speed .number("(dd)(dd)(dd)") // date (yymmdd) .number("(ddd.dd)") // course + .groupBegin() .number("(d{8})") // state .number("L(d{6})") // odometer + .groupEnd("?") .compile(); @Override @@ -88,8 +90,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder { position.setCourse(parser.nextDouble(0)); - position.set(Position.KEY_STATUS, parser.next()); - position.set(Position.KEY_ODOMETER, parser.nextInt(0)); + if (parser.hasNext(2)) { + position.set(Position.KEY_STATUS, parser.next()); + position.set(Position.KEY_ODOMETER, parser.nextInt(0)); + } return position; } diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java index 83664fa5a..3a0ccea78 100644 --- a/src/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/org/traccar/protocol/CastelProtocolDecoder.java @@ -440,7 +440,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { } - } else if (version == 4) { + } else if (version == 3 || version == 4) { return decodeSc(channel, remoteAddress, buf, version, id, type, deviceSession); diff --git a/src/org/traccar/protocol/CityeasyProtocolEncoder.java b/src/org/traccar/protocol/CityeasyProtocolEncoder.java index c800131d6..387926e03 100644 --- a/src/org/traccar/protocol/CityeasyProtocolEncoder.java +++ b/src/org/traccar/protocol/CityeasyProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package org.traccar.protocol; +import java.util.TimeZone; + import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.traccar.BaseProtocolEncoder; @@ -56,13 +58,13 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder { content.writeShort(0); return encodeContent(CityeasyProtocolDecoder.MSG_LOCATION_INTERVAL, content); case Command.TYPE_SET_TIMEZONE: - int timezone = command.getInteger(Command.KEY_TIMEZONE); + int timezone = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000; if (timezone < 0) { content.writeByte(1); } else { content.writeByte(0); } - content.writeShort(Math.abs(timezone) / 60); + content.writeShort(Math.abs(timezone)); return encodeContent(CityeasyProtocolDecoder.MSG_TIMEZONE, content); default: Log.warning(new UnsupportedOperationException(command.getType())); diff --git a/src/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/org/traccar/protocol/CradlepointProtocolDecoder.java index 1ba50f04b..e8f95a60c 100644 --- a/src/org/traccar/protocol/CradlepointProtocolDecoder.java +++ b/src/org/traccar/protocol/CradlepointProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 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,11 +18,13 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; +import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; import java.net.SocketAddress; +import java.util.Date; import java.util.regex.Pattern; public class CradlepointProtocolDecoder extends BaseProtocolDecoder { @@ -33,18 +35,18 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN = new PatternBuilder() .expression("([^,]+),") // id - .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(d{1,6}),") // time (hhmmss) .number("(d+)(dd.d+),") // latitude .expression("([NS]),") .number("(d+)(dd.d+),") // longitude .expression("([EW]),") .number("(d+.d+)?,") // speed .number("(d+.d+)?,") // course - .expression("([^,]+),") // carrier + .expression("([^,]+)?,") // carrier .expression("([^,]+)?,") // serdis - .number("(-?d+),") // rsrp - .number("(-?d+),") // dbm - .number("(-?d+),") // rsrq + .number("(-?d+)?,") // rsrp + .number("(-?d+)?,") // rssi + .number("(-?d+)?,") // rsrq .expression("([^,]+)?,") // ecio .expression("([^,]+)?") // wan ip .compile(); @@ -58,16 +60,21 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(); - position.setProtocol(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(); + position.setProtocol(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS)); + int time = parser.nextInt(); + DateBuilder dateBuilder = new DateBuilder(new Date()); + dateBuilder.setHour(time / 100 / 100); + dateBuilder.setMinute(time / 100 % 100); + dateBuilder.setSecond(time % 100); + position.setTime(dateBuilder.getDate()); position.setValid(true); position.setLatitude(parser.nextCoordinate()); @@ -75,9 +82,12 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(parser.nextDouble(0)); position.setCourse(parser.nextDouble(0)); - parser.skip(4); - - position.set(Position.KEY_RSSI, parser.nextDouble()); + position.set("carrid", parser.next()); + position.set("serdis", parser.next()); + position.set("rsrp", parser.next()); + position.set("dbm", parser.next()); + position.set("rsrq", parser.next()); + position.set("ecio", parser.next()); return position; } diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java index f44c91c4a..799254b65 100644 --- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2017 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. @@ -22,6 +22,7 @@ import org.traccar.helper.BitUtil; 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; @@ -92,7 +93,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(parser.nextHexInt(0) / 600000.0); } - position.setSpeed(parser.nextHexInt(0) / 100.0); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt(0) / 100.0)); position.setCourse(parser.nextHexInt(0) / 100.0); position.set(Position.KEY_STATUS, parser.next()); diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java index 0f6551cc3..8d0f8016a 100644 --- a/src/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java @@ -96,6 +96,22 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { } } + private void decodeStatus(Position position, int status) { + if (BitUtil.check(status, 1)) { + position.set(Position.KEY_IGNITION, BitUtil.check(status, 2)); + } + if (BitUtil.check(status, 3)) { + position.set(Position.KEY_ARMED, BitUtil.check(status, 4)); + } + if (BitUtil.check(status, 5)) { + position.set(Position.KEY_BLOCKED, !BitUtil.check(status, 6)); + } + if (BitUtil.check(status, 7)) { + position.set(Position.KEY_CHARGE, BitUtil.check(status, 8)); + } + position.set(Position.KEY_STATUS, status); + } + private Position decodeOld(DeviceSession deviceSession, ChannelBuffer buf, int type, int index) { Position position = new Position(); @@ -115,27 +131,39 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { position.setValid((buf.readUnsignedByte() & 0x01) != 0); - if (type == MSG_ALARM) { - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); - } - - if (buf.readableBytes() >= 2 * 5) { + if (type == MSG_GPS) { - int status = buf.readUnsignedShort(); - if (BitUtil.check(status, 1)) { - position.set(Position.KEY_IGNITION, BitUtil.check(status, 2)); + if (buf.readableBytes() >= 2) { + decodeStatus(position, buf.readUnsignedShort()); } - if (BitUtil.check(status, 7)) { - position.set(Position.KEY_CHARGE, BitUtil.check(status, 8)); + + if (buf.readableBytes() >= 2 * 4) { + + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + + position.set(Position.KEY_RSSI, buf.readUnsignedShort()); + + position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort()); + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort()); + } - position.set(Position.KEY_STATUS, status); - position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + } else if (type == MSG_ALARM) { - position.set(Position.KEY_RSSI, buf.readUnsignedShort()); + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); - position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort()); - position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort()); + } else if (type == MSG_STATE) { + + int statusType = buf.readUnsignedByte(); + + position.set(Position.KEY_EVENT, statusType); + + if (statusType == 0x01 || statusType == 0x02 || statusType == 0x03) { + buf.readUnsignedInt(); // device time + if (buf.readableBytes() >= 2) { + decodeStatus(position, buf.readUnsignedShort()); + } + } } @@ -189,6 +217,45 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(7); // bss2 } + if (buf.readableBytes() >= 2) { + int status = buf.readUnsignedShort(); + position.setValid(BitUtil.check(status, 0)); + if (BitUtil.check(status, 1)) { + position.set(Position.KEY_IGNITION, BitUtil.check(status, 2)); + } + position.set(Position.KEY_STATUS, status); + } + + if (buf.readableBytes() >= 2) { + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + } + + if (buf.readableBytes() >= 4) { + position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort()); + position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort()); + } + + if (buf.readableBytes() >= 4) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + } + + if (buf.readableBytes() >= 4) { + buf.readUnsignedShort(); // gsm counter + buf.readUnsignedShort(); // gps counter + } + + if (buf.readableBytes() >= 4) { + position.set(Position.KEY_STEPS, buf.readUnsignedShort()); + buf.readUnsignedShort(); // walking time + } + + if (buf.readableBytes() >= 12) { + position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0); + position.set("humidity", buf.readUnsignedShort() * 0.1); + position.set("illuminance", buf.readUnsignedInt() / 256.0); + position.set("co2", buf.readUnsignedInt()); + } + return position; } @@ -212,6 +279,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1)); } else { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); if (deviceSession == null) { return null; @@ -221,7 +289,20 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { return decodeOld(deviceSession, buf, type, index); } else if (type >= MSG_NORMAL && type <= MSG_OBD_CODE) { return decodeNew(deviceSession, buf, index); + } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) { + + Position position = new Position(); + position.setDeviceId(deviceSession.getDeviceId()); + position.setProtocol(getProtocolName()); + + getLastLocation(position, null); + + decodeStatus(position, buf.readUnsignedShort()); + + return position; + } + } return null; diff --git a/src/org/traccar/protocol/EskyFrameDecoder.java b/src/org/traccar/protocol/EskyFrameDecoder.java new file mode 100644 index 000000000..3175698fd --- /dev/null +++ b/src/org/traccar/protocol/EskyFrameDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +public class EskyFrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + buf.readerIndex(buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 'E')); + + int endIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 'E'); + if (endIndex > 0) { + return buf.readBytes(endIndex - buf.readerIndex()); + } else { + return buf.readBytes(buf.readableBytes()); // assume full frame + } + } + +} diff --git a/src/org/traccar/protocol/EskyProtocol.java b/src/org/traccar/protocol/EskyProtocol.java new file mode 100644 index 000000000..4c1d11f7d --- /dev/null +++ b/src/org/traccar/protocol/EskyProtocol.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.string.StringDecoder; +import org.jboss.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class EskyProtocol extends BaseProtocol { + + public EskyProtocol() { + super("esky"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new EskyFrameDecoder()); + pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new EskyProtocolDecoder(EskyProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/EskyProtocolDecoder.java b/src/org/traccar/protocol/EskyProtocolDecoder.java new file mode 100644 index 000000000..d524224af --- /dev/null +++ b/src/org/traccar/protocol/EskyProtocolDecoder.java @@ -0,0 +1,89 @@ +/* + * Copyright 2017 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.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class EskyProtocolDecoder extends BaseProtocolDecoder { + + public EskyProtocolDecoder(EskyProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("EO;") // header + .number("d+;") // index + .number("(d+);") // imei + .text("R;") // data type + .number("(d+)").text("+") // satellites + .number("(dd)(dd)(dd)") // date + .number("(dd)(dd)(dd)").text("+") // time + .number("(-?d+.d+)").text("+") // latitude + .number("(-?d+.d+)").text("+") // longitude + .number("(d+.d+)").text("+") // speed + .number("(d+)").text("+") // course + .text("0x").number("(d+)").text("+") // input + .number("(d+)").text("+") // message type + .number("(d+)").text("+") // odometer + .number("(d+)") // voltage + .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(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_SATELLITES, parser.nextInt()); + + position.setValid(true); + position.setTime(parser.nextDateTime()); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromMps(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + + position.set(Position.KEY_INPUT, parser.nextHexInt()); + position.set(Position.KEY_EVENT, parser.nextInt()); + position.set(Position.KEY_ODOMETER, parser.nextInt()); + position.set(Position.KEY_POWER, parser.nextInt()); + + return position; + } + +} diff --git a/src/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/org/traccar/protocol/FifotrackProtocolDecoder.java index f8f4fb078..304f6a2c3 100644 --- a/src/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -110,7 +110,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16)); } - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); if (parser.hasNext()) { String[] sensors = parser.next().split("\\|"); diff --git a/src/org/traccar/protocol/GenxProtocol.java b/src/org/traccar/protocol/GenxProtocol.java new file mode 100644 index 000000000..2b5b1a43d --- /dev/null +++ b/src/org/traccar/protocol/GenxProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder; +import org.jboss.netty.handler.codec.string.StringDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class GenxProtocol extends BaseProtocol { + + public GenxProtocol() { + super("genx"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new GenxProtocolDecoder(GenxProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/GenxProtocolDecoder.java b/src/org/traccar/protocol/GenxProtocolDecoder.java new file mode 100644 index 000000000..3b716796c --- /dev/null +++ b/src/org/traccar/protocol/GenxProtocolDecoder.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 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.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; +import org.traccar.DeviceSession; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.text.SimpleDateFormat; + +public class GenxProtocolDecoder extends BaseProtocolDecoder { + + private int[] reportColumns; + + public GenxProtocolDecoder(GenxProtocol protocol) { + super(protocol); + setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4")); + } + + public void setReportColumns(String format) { + String[] columns = format.split(","); + reportColumns = new int[columns.length]; + for (int i = 0; i < columns.length; i++) { + reportColumns[i] = Integer.parseInt(columns[i]); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String[] values = ((String) msg).split(","); + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setValid(true); + + for (int i = 0; i < Math.min(values.length, reportColumns.length); i++) { + switch (reportColumns[i]) { + case 1: + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]); + if (deviceSession != null) { + position.setDeviceId(deviceSession.getDeviceId()); + } + break; + case 2: + position.setTime(new SimpleDateFormat("MM/dd/yy HH:mm:ss").parse(values[i])); + break; + case 3: + position.setLatitude(Double.parseDouble(values[i])); + break; + case 4: + position.setLongitude(Double.parseDouble(values[i])); + break; + default: + break; + } + } + + return position.getDeviceId() != 0 ? position : null; + } + +} diff --git a/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java new file mode 100644 index 000000000..071960e49 --- /dev/null +++ b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java @@ -0,0 +1,406 @@ +/* + * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.helper.BitBuffer; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +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; + +public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder { + + public Gl200BinaryProtocolDecoder(Gl200Protocol protocol) { + super(protocol); + } + + private Date decodeTime(ChannelBuffer buf) { + DateBuilder dateBuilder = new DateBuilder() + .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + return dateBuilder.getDate(); + } + + public static final int MSG_RSP_LCB = 3; + public static final int MSG_RSP_GEO = 8; + public static final int MSG_RSP_COMPRESSED = 100; + + private List<Position> decodeLocation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { + + List<Position> positions = new LinkedList<>(); + + int type = buf.readUnsignedByte(); + + buf.readUnsignedInt(); // mask + buf.readUnsignedShort(); // length + buf.readUnsignedByte(); // device type + buf.readUnsignedShort(); // protocol version + buf.readUnsignedShort(); // firmware version + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong())); + if (deviceSession == null) { + return null; + } + + int battery = buf.readUnsignedByte(); + int power = buf.readUnsignedShort(); + + if (type == MSG_RSP_GEO) { + buf.readUnsignedByte(); // reserved + buf.readUnsignedByte(); // reserved + } + + buf.readUnsignedByte(); // motion status + int satellites = buf.readUnsignedByte(); + + if (type != MSG_RSP_COMPRESSED) { + buf.readUnsignedByte(); // index + } + + if (type == MSG_RSP_LCB) { + buf.readUnsignedByte(); // phone length + for (int b = buf.readUnsignedByte();; b = buf.readUnsignedByte()) { + if ((b & 0xf) == 0xf || (b & 0xf0) == 0xf0) { + break; + } + } + } + + if (type == MSG_RSP_COMPRESSED) { + + int count = buf.readUnsignedShort(); + + BitBuffer bits; + int speed = 0; + int heading = 0; + int latitude = 0; + int longitude = 0; + long time = 0; + + for (int i = 0; i < count; i++) { + + if (time > 0) { + time += 1; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + switch (BitUtil.from(buf.getUnsignedByte(buf.readerIndex()), 8 - 2)) { + case 1: + bits = new BitBuffer(buf.readBytes(3)); + bits.readUnsigned(2); // point attribute + bits.readUnsigned(1); // fix type + speed = bits.readUnsigned(12); + heading = bits.readUnsigned(9); + longitude = buf.readInt(); + latitude = buf.readInt(); + if (time == 0) { + time = buf.readUnsignedInt(); + } + break; + case 2: + bits = new BitBuffer(buf.readBytes(5)); + bits.readUnsigned(2); // point attribute + bits.readUnsigned(1); // fix type + speed += bits.readSigned(7); + heading += bits.readSigned(7); + longitude += bits.readSigned(12); + latitude += bits.readSigned(11); + break; + default: + buf.readUnsignedByte(); // invalid or same + continue; + } + + position.setValid(true); + position.setTime(new Date(time * 1000)); + position.setSpeed(UnitsConverter.knotsFromKph(speed * 0.1)); + position.setCourse(heading); + position.setLongitude(longitude * 0.000001); + position.setLatitude(latitude * 0.000001); + + positions.add(position); + + } + + } else { + + int count = buf.readUnsignedByte(); + + for (int i = 0; i < count; i++) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_BATTERY_LEVEL, battery); + position.set(Position.KEY_POWER, power); + position.set(Position.KEY_SATELLITES, satellites); + + int hdop = buf.readUnsignedByte(); + position.setValid(hdop > 0); + position.set(Position.KEY_HDOP, hdop); + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1)); + position.setCourse(buf.readUnsignedShort()); + position.setAltitude(buf.readShort()); + position.setLongitude(buf.readInt() * 0.000001); + position.setLatitude(buf.readInt() * 0.000001); + + position.setTime(decodeTime(buf)); + + position.setNetwork(new Network(CellTower.from( + buf.readUnsignedShort(), buf.readUnsignedShort(), + buf.readUnsignedShort(), buf.readUnsignedShort()))); + + buf.readUnsignedByte(); // reserved + + positions.add(position); + + } + + } + + return positions; + } + + public static final int MSG_EVT_BPL = 6; + public static final int MSG_EVT_VGN = 45; + public static final int MSG_EVT_VGF = 46; + public static final int MSG_EVT_UPD = 15; + public static final int MSG_EVT_IDF = 17; + public static final int MSG_EVT_GSS = 21; + public static final int MSG_EVT_GES = 26; + public static final int MSG_EVT_GPJ = 31; + public static final int MSG_EVT_RMD = 35; + public static final int MSG_EVT_JDS = 33; + public static final int MSG_EVT_CRA = 23; + public static final int MSG_EVT_UPC = 34; + + private Position decodeEvent(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + int type = buf.readUnsignedByte(); + + buf.readUnsignedInt(); // mask + buf.readUnsignedShort(); // length + buf.readUnsignedByte(); // device type + buf.readUnsignedShort(); // protocol version + + position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort())); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong())); + if (deviceSession == null) { + return null; + } + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set(Position.KEY_POWER, buf.readUnsignedShort()); + + buf.readUnsignedByte(); // motion status + + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + + switch (type) { + case MSG_EVT_BPL: + buf.readUnsignedShort(); // backup battery voltage + break; + case MSG_EVT_VGN: + case MSG_EVT_VGF: + buf.readUnsignedShort(); // reserved + buf.readUnsignedByte(); // report type + buf.readUnsignedInt(); // ignition duration + break; + case MSG_EVT_UPD: + buf.readUnsignedShort(); // code + buf.readUnsignedByte(); // retry + break; + case MSG_EVT_IDF: + buf.readUnsignedInt(); // idling duration + break; + case MSG_EVT_GSS: + buf.readUnsignedByte(); // gps signal status + buf.readUnsignedInt(); // reserved + break; + case MSG_EVT_GES: + buf.readUnsignedShort(); // trigger geo id + buf.readUnsignedByte(); // trigger geo enable + buf.readUnsignedByte(); // trigger mode + buf.readUnsignedInt(); // radius + buf.readUnsignedInt(); // check interval + break; + case MSG_EVT_GPJ: + buf.readUnsignedByte(); // cw jamming value + buf.readUnsignedByte(); // gps jamming state + break; + case MSG_EVT_RMD: + buf.readUnsignedByte(); // roaming state + break; + case MSG_EVT_JDS: + buf.readUnsignedByte(); // jamming state + break; + case MSG_EVT_CRA: + buf.readUnsignedByte(); // crash counter + break; + case MSG_EVT_UPC: + buf.readUnsignedByte(); // command id + buf.readUnsignedShort(); // result + break; + default: + break; + } + + buf.readUnsignedByte(); // count + + int hdop = buf.readUnsignedByte(); + position.setValid(hdop > 0); + position.set(Position.KEY_HDOP, hdop); + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1)); + position.setCourse(buf.readUnsignedShort()); + position.setAltitude(buf.readShort()); + position.setLongitude(buf.readInt() * 0.000001); + position.setLatitude(buf.readInt() * 0.000001); + + position.setTime(decodeTime(buf)); + + position.setNetwork(new Network(CellTower.from( + buf.readUnsignedShort(), buf.readUnsignedShort(), + buf.readUnsignedShort(), buf.readUnsignedShort()))); + + buf.readUnsignedByte(); // reserved + + return position; + } + + public static final int MSG_INF_GPS = 2; + public static final int MSG_INF_CID = 4; + public static final int MSG_INF_CSQ = 5; + public static final int MSG_INF_VER = 6; + public static final int MSG_INF_BAT = 7; + public static final int MSG_INF_TMZ = 9; + public static final int MSG_INF_GIR = 10; + + private Position decodeInformation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + int type = buf.readUnsignedByte(); + + buf.readUnsignedInt(); // mask + buf.readUnsignedShort(); // length + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong())); + if (deviceSession == null) { + return null; + } + position.setDeviceId(deviceSession.getDeviceId()); + + buf.readUnsignedByte(); // device type + buf.readUnsignedShort(); // protocol version + + position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort())); + + if (type == MSG_INF_VER) { + buf.readUnsignedShort(); // hardware version + buf.readUnsignedShort(); // mcu version + buf.readUnsignedShort(); // reserved + } + + buf.readUnsignedByte(); // motion status + buf.readUnsignedByte(); // reserved + + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + + buf.readUnsignedByte(); // mode + buf.skipBytes(7); // last fix time + buf.readUnsignedByte(); // reserved + buf.readUnsignedByte(); + buf.readUnsignedShort(); // response report mask + buf.readUnsignedShort(); // ign interval + buf.readUnsignedShort(); // igf interval + buf.readUnsignedInt(); // reserved + buf.readUnsignedByte(); // reserved + + if (type == MSG_INF_BAT) { + position.set(Position.KEY_CHARGE, buf.readUnsignedByte() != 0); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + } + + buf.skipBytes(10); // iccid + + if (type == MSG_INF_CSQ) { + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + buf.readUnsignedByte(); + } + + buf.readUnsignedByte(); // time zone flags + buf.readUnsignedShort(); // time zone offset + + if (type == MSG_INF_GIR) { + buf.readUnsignedByte(); // gir trigger + buf.readUnsignedByte(); // cell number + position.setNetwork(new Network(CellTower.from( + buf.readUnsignedShort(), buf.readUnsignedShort(), + buf.readUnsignedShort(), buf.readUnsignedShort()))); + buf.readUnsignedByte(); // ta + buf.readUnsignedByte(); // rx level + } + + getLastLocation(position, decodeTime(buf)); + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + switch (buf.readBytes(4).toString(StandardCharsets.US_ASCII)) { + case "+RSP": + return decodeLocation(channel, remoteAddress, buf); + case "+INF": + return decodeInformation(channel, remoteAddress, buf); + case "+EVT": + return decodeEvent(channel, remoteAddress, buf); + default: + return null; + } + } + +} diff --git a/src/org/traccar/protocol/Gl200FrameDecoder.java b/src/org/traccar/protocol/Gl200FrameDecoder.java new file mode 100644 index 000000000..960c3779a --- /dev/null +++ b/src/org/traccar/protocol/Gl200FrameDecoder.java @@ -0,0 +1,96 @@ +/* + * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class Gl200FrameDecoder extends FrameDecoder { + + private static final int MINIMUM_LENGTH = 11; + + private static final Set<String> BINARY_HEADERS = new HashSet<>( + Arrays.asList("+RSP", "+BSP", "+EVT", "+BVT", "+INF", "+BNF", "+HBD", "+CRD", "+BRD")); + + public static boolean isBinary(ChannelBuffer buf) { + String header = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII); + if (header.equals("+ACK")) { + return buf.getByte(buf.readerIndex() + header.length()) != (byte) ':'; + } else { + return BINARY_HEADERS.contains(header); + } + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + if (buf.readableBytes() < MINIMUM_LENGTH) { + return null; + } + + if (isBinary(buf)) { + + int length; + switch (buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII)) { + case "+ACK": + length = buf.getUnsignedByte(buf.readerIndex() + 6); + break; + case "+INF": + case "+BNF": + length = buf.getUnsignedShort(buf.readerIndex() + 7); + break; + case "+HBD": + length = buf.getUnsignedByte(buf.readerIndex() + 5); + break; + case "+CRD": + case "+BRD": + length = buf.getUnsignedShort(buf.readerIndex() + 6); + break; + default: + length = buf.getUnsignedShort(buf.readerIndex() + 9); + break; + } + + if (buf.readableBytes() >= length) { + return buf.readBytes(length); + } + + } else { + + int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '$'); + if (endIndex < 0) { + endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0); + } + if (endIndex > 0) { + ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex()); + buf.readByte(); // delimiter + return frame; + } + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java index b3743042c..799d7fe36 100644 --- a/src/org/traccar/protocol/Gl200Protocol.java +++ b/src/org/traccar/protocol/Gl200Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 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,10 +18,8 @@ package org.traccar.protocol; import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.handler.codec.string.StringDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; -import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.TrackerServer; import org.traccar.model.Command; @@ -44,9 +42,8 @@ public class Gl200Protocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(4096, "$", "\0")); + pipeline.addLast("frameDecoder", new Gl200FrameDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder()); pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); } @@ -55,7 +52,6 @@ public class Gl200Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder()); pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); } diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java index a3062c942..0de7bb926 100644 --- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,864 +15,34 @@ */ package org.traccar.protocol; +import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; -import org.traccar.helper.BitUtil; -import org.traccar.helper.Parser; -import org.traccar.helper.PatternBuilder; -import org.traccar.helper.UnitsConverter; -import org.traccar.model.CellTower; -import org.traccar.model.Network; -import org.traccar.model.Position; -import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; -import java.util.LinkedList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class Gl200ProtocolDecoder extends BaseProtocolDecoder { - private boolean ignoreFixTime; + private final Gl200TextProtocolDecoder textProtocolDecoder; + private final Gl200BinaryProtocolDecoder binaryProtocolDecoder; public Gl200ProtocolDecoder(Gl200Protocol protocol) { super(protocol); - - ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime"); - } - - private static final Pattern PATTERN_ACK = new PatternBuilder() - .text("+ACK:GT") - .expression("...,") // type - .number("([0-9A-Z]{2}xxxx),") // protocol version - .number("(d{15}|x{14}),") // imei - .any().text(",") - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_INF = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTINF,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:[0-9A-Z]{17},)?") // vin - .expression("(?:[^,]+)?,") // device name - .number("(xx),") // state - .expression("(?:[0-9F]{20})?,") // iccid - .number("d{1,2},") - .number("d{1,2},") - .expression("[01],") // external power - .number("([d.]+)?,") // odometer or external power - .number("d*,") // backup battery or lightness - .number("(d+.d+),") // battery - .expression("([01]),") // charging - .number("(?:d),") // led - .number("(?:d)?,") // gps on need - .number("(?:d)?,") // gps antenna type - .number("(?:d),").optional() // gps antenna state - .number("d{14},") // last fix time - .groupBegin() - .number("(d+),") // battery percentage - .expression("[01]?,") // flash type - .number("(-?[d.]+)?,,,") // temperature - .or() - .expression("(?:[01])?,").optional() // pin15 mode - .number("(d+)?,") // adc1 - .number("(d+)?,").optional() // adc2 - .number("(xx)?,") // digital input - .number("(xx)?,") // digital output - .number("[-+]dddd,") // timezone - .expression("[01],") // daylight saving - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_VER = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTVER,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .expression("([^,]*),") // device type - .number("(xxxx),") // firmware version - .number("(xxxx),") // hardware version - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_LOCATION = new PatternBuilder() - .number("(d{1,2})?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3})?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+)?,") // mcc - .number("(d+)?,") // mnc - .groupBegin() - .number("(d+),") // lac - .number("(d+),") // cid - .or() - .number("(x+)?,") // lac - .number("(x+)?,") // cid - .groupEnd() - .number("(?:d+|(d+.d))?,") // odometer - .compile(); - - private static final Pattern PATTERN_OBD = new PatternBuilder() - .text("+RESP:GTOBD,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:[0-9A-Z]{17})?,") // vin - .expression("[^,]{0,20},") // device name - .expression("[01],") // report type - .number("x{1,8},") // report mask - .expression("(?:[0-9A-Z]{17})?,") // vin - .number("[01],") // obd connect - .number("(?:d{1,5})?,") // obd voltage - .number("(?:x{8})?,") // support pids - .number("(d{1,5})?,") // engine rpm - .number("(d{1,3})?,") // speed - .number("(-?d{1,3})?,") // coolant temp - .number("(d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d{1,5})?,") // dtcs cleared distance - .number("(?:d{1,5})?,") - .expression("([01])?,") // obd connect - .number("(d{1,3})?,") // number of dtcs - .number("(x*),") // dtcs - .number("(d{1,3})?,") // throttle - .number("(?:d{1,3})?,") // engine load - .number("(d{1,3})?,") // fuel level - .expression("(?:[0-9A],)?") // obd protocol - .number("(d+),") // odometer - .expression(PATTERN_LOCATION.pattern()) - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_FRI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTFRI,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:([0-9A-Z]{17}),)?") // vin - .expression("[^,]*,") // device name - .number("(d+)?,") // power - .number("d{1,2},") // report type - .number("d{1,2},") // count - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .groupBegin() - .number("(d{1,7}.d)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .or() - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,") // adc 2 - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .number("(d+)?,") // rpm - .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d+)?,") // fuel level - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_ERI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTERI,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("x{8},") // mask - .number("(d+)?,") // power - .number("d{1,2},") // report type - .number("d{1,2},") // count - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,") // adc 2 - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .expression("(.*)") // additional data - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IGN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTIG[NF],") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d+,") // ignition off duration - .expression(PATTERN_LOCATION.pattern()) - .number("(d{5}:dd:dd)?,") // hour meter - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IDA = new PatternBuilder() - .text("+RESP:GTIDA,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,,") // device name - .number("([^,]+),") // rfid - .expression("[01],") // report type - .number("1,") // count - .expression(PATTERN_LOCATION.pattern()) - .number("(d+.d),") // odometer - .text(",,,,") - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_WIF = new PatternBuilder() - .text("+RESP:GTWIF,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(d+),") // count - .number("((?:x{12},-?d+,,,,)+),,,,") // wifi - .number("(d{1,3}),") // battery - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_GSM = new PatternBuilder() - .text("+RESP:GTGSM,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:STR|CTN|NMR|RTL),") // fix type - .expression("(.*)") // cells - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d*,") - .number("(d{1,2}),") // report type - .number("d{1,2},") // count - .expression(PATTERN_LOCATION.pattern()) - .groupBegin() - .number("(d{1,7}.d)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .or() - .number("(d{1,7}.d)?,") // odometer - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)") // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_BASIC = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF)").text(":") - .expression("GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version - .number("(d{15}|x{14}),") // imei - .any() - .number("(d{1,2})?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3})?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+),") // mcc - .number("(d+),") // mnc - .number("(x+),") // lac - .number("(x+),").optional(4) // cell - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) { - Parser parser = new Parser(PATTERN_ACK, sentence); - if (parser.matches()) { - String protocolVersion = parser.next(); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - if (type.equals("HBD")) { - if (channel != null) { - parser.skip(6); - channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress); - } - } else { - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - getLastLocation(position, parser.nextDateTime()); - position.setValid(false); - position.set(Position.KEY_RESULT, "Command " + type + " accepted"); - return position; - } - } - return null; - } - - private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { - if (parser.matches()) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession != null) { - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - return position; - } - } - return null; - } - - private void decodeDeviceTime(Position position, Parser parser) { - if (parser.hasNext(6)) { - if (ignoreFixTime) { - position.setTime(parser.nextDateTime()); - } else { - position.setDeviceTime(parser.nextDateTime()); - } - } - } - - private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_INF, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - position.set(Position.KEY_STATUS, parser.next()); - - parser.next(); // odometer or external power - - position.set(Position.KEY_BATTERY, parser.nextDouble(0)); - position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1); - - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - - position.set(Position.PREFIX_TEMP + 1, parser.next()); - - position.set(Position.PREFIX_ADC + 1, parser.next()); - position.set(Position.PREFIX_ADC + 2, parser.next()); - - position.set(Position.KEY_INPUT, parser.next()); - position.set(Position.KEY_OUTPUT, parser.next()); - - getLastLocation(position, parser.nextDateTime()); - - position.set(Position.KEY_INDEX, parser.nextHexInt(0)); - - return position; - } - - private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_VER, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - position.set("deviceType", parser.next()); - position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0)); - position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0)); - - getLastLocation(position, parser.nextDateTime()); - - return position; - } - - private void decodeLocation(Position position, Parser parser) { - int hdop = parser.nextInt(0); - position.setValid(hdop > 0); - position.set(Position.KEY_HDOP, hdop); - - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); - - if (parser.hasNext(8)) { - position.setValid(true); - position.setLongitude(parser.nextDouble(0)); - position.setLatitude(parser.nextDouble(0)); - position.setTime(parser.nextDateTime()); - } else { - getLastLocation(position, null); - } - - if (parser.hasNext(6)) { - int mcc = parser.nextInt(0); - int mnc = parser.nextInt(0); - if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0)))); - } - if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0)))); - } - } - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - } - - private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_OBD, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - position.set(Position.KEY_RPM, parser.nextInt()); - position.set(Position.KEY_OBD_SPEED, parser.nextInt()); - position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); - position.set(Position.KEY_FUEL_CONSUMPTION, parser.next()); - position.set("dtcsClearedDistance", parser.nextInt()); - position.set("odbConnect", parser.nextInt(0) == 1); - position.set("dtcsNumber", parser.nextInt()); - position.set("dtcsCodes", parser.next()); - position.set(Position.KEY_THROTTLE, parser.nextInt()); - position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000); - - decodeLocation(position, parser); - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - - decodeDeviceTime(position, parser); - - return position; - } - - private void decodeStatus(Position position, Parser parser) { - if (parser.hasNext(3)) { - int ignition = parser.nextHexInt(0); - if (BitUtil.check(ignition, 4)) { - position.set(Position.KEY_IGNITION, false); - } else if (BitUtil.check(ignition, 5)) { - position.set(Position.KEY_IGNITION, true); - } - position.set(Position.KEY_INPUT, parser.nextHexInt(0)); - position.set(Position.KEY_OUTPUT, parser.nextHexInt(0)); - } - } - - private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_FRI, sentence); - if (!parser.matches()) { - return null; - } - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - - LinkedList<Position> positions = new LinkedList<>(); - - String vin = parser.next(); - int power = parser.nextInt(0); - - Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); - while (itemParser.find()) { - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - position.set(Position.KEY_VIN, vin); - - decodeLocation(position, itemParser); - - positions.add(position); - } - - Position position = positions.getLast(); - - decodeLocation(position, parser); - - // power value only on some devices - if (power > 10) { - position.set(Position.KEY_POWER, power); - } - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - position.set(Position.KEY_HOURS, 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); - - position.set(Position.KEY_RPM, parser.nextInt()); - position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - - decodeDeviceTime(position, parser); - - return positions; - } - - private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_ERI, sentence); - if (!parser.matches()) { - return null; - } - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - - LinkedList<Position> positions = new LinkedList<>(); - - int power = parser.nextInt(0); - - Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); - while (itemParser.find()) { - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - decodeLocation(position, itemParser); - - positions.add(position); - } - - Position position = positions.getLast(); - - decodeLocation(position, parser); - - position.set(Position.KEY_POWER, power); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - position.set(Position.KEY_HOURS, 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); - - int index = 0; - String[] data = parser.next().split(","); - if (data.length > 1) { - int deviceType = Integer.parseInt(data[index++]); - if (deviceType == 2) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index++; // id - index++; // type - position.set(Position.PREFIX_TEMP + i, Short.parseShort(data[index++], 16) * 0.0625); - } - } - } - - decodeDeviceTime(position, parser); - - return positions; - } - - private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_IGN, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - decodeLocation(position, parser); - - position.set(Position.KEY_HOURS, parser.next()); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - - decodeDeviceTime(position, parser); - - return position; - } - - private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_IDA, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - position.set(Position.KEY_RFID, parser.next()); - - decodeLocation(position, parser); - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - - decodeDeviceTime(position, parser); - - return position; - } - - private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_WIF, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - getLastLocation(position, null); - - Network network = new Network(); - - parser.nextInt(0); // count - Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next()); - while (matcher.find()) { - String mac = matcher.group(1).replaceAll("(..)", "$1:"); - network.addWifiAccessPoint(WifiAccessPoint.from( - mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2)))); - } - - position.setNetwork(network); - - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); - - return position; - } - - private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_GSM, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - getLastLocation(position, null); - - Network network = new Network(); - - String[] data = parser.next().split(","); - for (int i = 0; i < 6; i++) { - if (!data[i * 6].isEmpty()) { - network.addCellTower(CellTower.from( - Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]), - Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16), - Integer.parseInt(data[i * 6 + 4]))); - } - } - - position.setNetwork(network); - - return position; - } - - private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) { - Parser parser = new Parser(PATTERN, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - int reportType = parser.nextInt(0); - if (type.equals("NMR")) { - position.set(Position.KEY_MOTION, reportType == 1); - } else if (type.equals("SOS")) { - position.set(Position.KEY_ALARM, Position.ALARM_SOS); - } - - decodeLocation(position, parser); - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); - - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - - decodeDeviceTime(position, parser); - - if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) { - channel.write("+SACK:" + parser.next() + "$", remoteAddress); - } - - return position; - } - - private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) { - Parser parser = new Parser(PATTERN_BASIC, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { - return null; - } - - int hdop = parser.nextInt(0); - position.setValid(hdop > 0); - position.set(Position.KEY_HDOP, hdop); - - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); - - if (parser.hasNext(2)) { - position.setLongitude(parser.nextDouble(0)); - position.setLatitude(parser.nextDouble(0)); - } else { - getLastLocation(position, null); - } - - if (parser.hasNext(6)) { - position.setTime(parser.nextDateTime()); - } - - if (parser.hasNext(4)) { - position.setNetwork(new Network(CellTower.from( - parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0)))); - } - - decodeDeviceTime(position, parser); - - switch (type) { - case "PNA": - position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON); - break; - case "PFA": - position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); - break; - case "EPN": - position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED); - break; - case "EPF": - position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); - break; - case "BPL": - position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); - break; - case "STT": - position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT); - break; - case "SWG": - position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE); - break; - case "TMP": - case "TEM": - position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE); - break; - case "JDR": - case "JDS": - position.set(Position.KEY_ALARM, Position.ALARM_JAMMING); - break; - default: - break; - } - - return position; + textProtocolDecoder = new Gl200TextProtocolDecoder(protocol); + binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol); } @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; + ChannelBuffer buf = (ChannelBuffer) msg; - int typeIndex = sentence.indexOf(":GT"); - if (typeIndex < 0) { - return null; - } - - Object result; - String type = sentence.substring(typeIndex + 3, typeIndex + 6); - if (sentence.startsWith("+ACK")) { - result = decodeAck(channel, remoteAddress, sentence, type); + if (Gl200FrameDecoder.isBinary(buf)) { + return binaryProtocolDecoder.decode(channel, remoteAddress, msg); } else { - switch (type) { - case "INF": - result = decodeInf(channel, remoteAddress, sentence); - break; - case "OBD": - result = decodeObd(channel, remoteAddress, sentence); - break; - case "FRI": - result = decodeFri(channel, remoteAddress, sentence); - break; - case "ERI": - result = decodeEri(channel, remoteAddress, sentence); - break; - case "IGN": - case "IGF": - result = decodeIgn(channel, remoteAddress, sentence); - break; - case "IDA": - result = decodeIda(channel, remoteAddress, sentence); - break; - case "WIF": - result = decodeWif(channel, remoteAddress, sentence); - break; - case "GSM": - result = decodeGsm(channel, remoteAddress, sentence); - break; - case "VER": - result = decodeVer(channel, remoteAddress, sentence); - break; - default: - result = decodeOther(channel, remoteAddress, sentence, type); - break; - } - - if (result == null) { - result = decodeBasic(channel, remoteAddress, sentence, type); - } - - if (result != null) { - if (result instanceof Position) { - ((Position) result).set(Position.KEY_TYPE, type); - } else { - for (Position p : (List<Position>) result) { - p.set(Position.KEY_TYPE, type); - } - } - } + return textProtocolDecoder.decode(channel, remoteAddress, msg); } - - return result; } } diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java new file mode 100644 index 000000000..2f03cbb8f --- /dev/null +++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -0,0 +1,917 @@ +/* + * Copyright 2012 - 2017 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.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; +import org.traccar.DeviceSession; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { + + private boolean ignoreFixTime; + + public Gl200TextProtocolDecoder(Gl200Protocol protocol) { + super(protocol); + + ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime"); + } + + private static final Pattern PATTERN_ACK = new PatternBuilder() + .text("+ACK:GT") + .expression("...,") // type + .number("([0-9A-Z]{2}xxxx),") // protocol version + .number("(d{15}|x{14}),") // imei + .any().text(",") + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_INF = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTINF,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:[0-9A-Z]{17},)?") // vin + .expression("(?:[^,]+)?,") // device name + .number("(xx),") // state + .expression("(?:[0-9Ff]{20})?,") // iccid + .number("(d{1,2}),") // rssi + .number("d{1,2},") + .expression("[01],") // external power + .number("([d.]+)?,") // odometer or external power + .number("d*,") // backup battery or lightness + .number("(d+.d+),") // battery + .expression("([01]),") // charging + .number("(?:d),") // led + .number("(?:d)?,") // gps on need + .number("(?:d)?,") // gps antenna type + .number("(?:d)?,").optional() // gps antenna state + .number("d{14},") // last fix time + .groupBegin() + .number("(d+),") // battery percentage + .number("[d.]*,") // flash type / power + .number("(-?[d.]+)?,,,") // temperature + .or() + .expression("(?:[01])?,").optional() // pin15 mode + .number("(d+)?,") // adc1 + .number("(d+)?,").optional() // adc2 + .number("(xx)?,") // digital input + .number("(xx)?,") // digital output + .number("[-+]dddd,") // timezone + .expression("[01],") // daylight saving + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_VER = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTVER,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .expression("([^,]*),") // device type + .number("(xxxx),") // firmware version + .number("(xxxx),") // hardware version + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_LOCATION = new PatternBuilder() + .number("(d{1,2})?,") // hdop + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(d+)?,") // mcc + .number("(d+)?,") // mnc + .groupBegin() + .number("(d+),") // lac + .number("(d+),") // cid + .or() + .number("(x+)?,") // lac + .number("(x+)?,") // cid + .groupEnd() + .number("(?:d+|(d+.d))?,") // odometer + .compile(); + + private static final Pattern PATTERN_OBD = new PatternBuilder() + .text("+RESP:GTOBD,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:[0-9A-Z]{17})?,") // vin + .expression("[^,]{0,20},") // device name + .expression("[01],") // report type + .number("x{1,8},") // report mask + .expression("(?:[0-9A-Z]{17})?,") // vin + .number("[01],") // obd connect + .number("(?:d{1,5})?,") // obd voltage + .number("(?:x{8})?,") // support pids + .number("(d{1,5})?,") // engine rpm + .number("(d{1,3})?,") // speed + .number("(-?d{1,3})?,") // coolant temp + .number("(d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d{1,5})?,") // dtcs cleared distance + .number("(?:d{1,5})?,") + .expression("([01])?,") // obd connect + .number("(d{1,3})?,") // number of dtcs + .number("(x*),") // dtcs + .number("(d{1,3})?,") // throttle + .number("(?:d{1,3})?,") // engine load + .number("(d{1,3})?,") // fuel level + .expression("(?:[0-9A],)?") // obd protocol + .number("(d+),") // odometer + .expression(PATTERN_LOCATION.pattern()) + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_FRI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTFRI,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:([0-9A-Z]{17}),)?") // vin + .expression("[^,]*,") // device name + .number("(d+)?,") // power + .number("d{1,2},") // report type + .number("d{1,2},") // count + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") + .groupBegin() + .number("(d{1,7}.d)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .or() + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,") // adc 2 + .number("(d{1,3})?,") // battery + .number("(?:(xx)(xx)(xx))?,") // device status + .number("(d+)?,") // rpm + .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d+)?,") // fuel level + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_ERI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTERI,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("x{8},") // mask + .number("(d+)?,") // power + .number("d{1,2},") // report type + .number("d{1,2},") // count + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,") // adc 2 + .number("(d{1,3})?,") // battery + .number("(?:(xx)(xx)(xx))?,") // device status + .expression("(.*)") // additional data + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_IGN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTIG[NF],") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d+,") // ignition off duration + .expression(PATTERN_LOCATION.pattern()) + .number("(d{5}:dd:dd)?,") // hour meter + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_IDA = new PatternBuilder() + .text("+RESP:GTIDA,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,,") // device name + .number("([^,]+),") // rfid + .expression("[01],") // report type + .number("1,") // count + .expression(PATTERN_LOCATION.pattern()) + .number("(d+.d),") // odometer + .text(",,,,") + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_WIF = new PatternBuilder() + .text("+RESP:GTWIF,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d+),") // count + .number("((?:x{12},-?d+,,,,)+),,,,") // wifi + .number("(d{1,3}),") // battery + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_GSM = new PatternBuilder() + .text("+RESP:GTGSM,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:STR|CTN|NMR|RTL),") // fix type + .expression("(.*)") // cells + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d*,") + .number("(d{1,2}),") // report type + .number("d{1,2},") // count + .expression(PATTERN_LOCATION.pattern()) + .groupBegin() + .number("(d{1,7}.d)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .or() + .number("(d{1,7}.d)?,") // odometer + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)") // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_BASIC = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF)").text(":") + .expression("GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version + .number("(d{15}|x{14}),") // imei + .any() + .number("(d{1,2})?,") // hdop + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+),").optional(4) // cell + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) { + Parser parser = new Parser(PATTERN_ACK, sentence); + if (parser.matches()) { + String protocolVersion = parser.next(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + if (type.equals("HBD")) { + if (channel != null) { + parser.skip(6); + channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress); + } + } else { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, parser.nextDateTime()); + position.setValid(false); + position.set(Position.KEY_RESULT, "Command " + type + " accepted"); + return position; + } + } + return null; + } + + private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { + if (parser.matches()) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession != null) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + return position; + } + } + return null; + } + + private void decodeDeviceTime(Position position, Parser parser) { + if (parser.hasNext(6)) { + if (ignoreFixTime) { + position.setTime(parser.nextDateTime()); + } else { + position.setDeviceTime(parser.nextDateTime()); + } + } + } + + private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_INF, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + switch (parser.nextHexInt()) { + case 0x16: + case 0x1A: + case 0x12: + position.set(Position.KEY_IGNITION, false); + position.set(Position.KEY_MOTION, true); + break; + case 0x11: + position.set(Position.KEY_IGNITION, false); + position.set(Position.KEY_MOTION, false); + break; + case 0x21: + position.set(Position.KEY_IGNITION, true); + position.set(Position.KEY_MOTION, false); + break; + case 0x22: + position.set(Position.KEY_IGNITION, true); + position.set(Position.KEY_MOTION, true); + break; + case 0x41: + position.set(Position.KEY_MOTION, false); + break; + case 0x42: + position.set(Position.KEY_MOTION, true); + break; + default: + break; + } + + position.set(Position.KEY_RSSI, parser.nextInt()); + + parser.next(); // odometer or external power + + position.set(Position.KEY_BATTERY, parser.nextDouble(0)); + position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1); + + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + + position.set(Position.PREFIX_TEMP + 1, parser.next()); + + position.set(Position.PREFIX_ADC + 1, parser.next()); + position.set(Position.PREFIX_ADC + 2, parser.next()); + + position.set(Position.KEY_INPUT, parser.next()); + position.set(Position.KEY_OUTPUT, parser.next()); + + getLastLocation(position, parser.nextDateTime()); + + position.set(Position.KEY_INDEX, parser.nextHexInt(0)); + + return position; + } + + private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_VER, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + position.set("deviceType", parser.next()); + position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0)); + position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0)); + + getLastLocation(position, parser.nextDateTime()); + + return position; + } + + private void decodeLocation(Position position, Parser parser) { + int hdop = parser.nextInt(0); + position.setValid(hdop > 0); + position.set(Position.KEY_HDOP, hdop); + + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); + position.setCourse(parser.nextDouble(0)); + position.setAltitude(parser.nextDouble(0)); + + if (parser.hasNext(8)) { + position.setValid(true); + position.setLongitude(parser.nextDouble(0)); + position.setLatitude(parser.nextDouble(0)); + position.setTime(parser.nextDateTime()); + } else { + getLastLocation(position, null); + } + + if (parser.hasNext(6)) { + int mcc = parser.nextInt(0); + int mnc = parser.nextInt(0); + if (parser.hasNext(2)) { + position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0)))); + } + if (parser.hasNext(2)) { + position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0)))); + } + } + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + } + + private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_OBD, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + position.set(Position.KEY_RPM, parser.nextInt()); + position.set(Position.KEY_OBD_SPEED, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); + position.set(Position.KEY_FUEL_CONSUMPTION, parser.next()); + position.set("dtcsClearedDistance", parser.nextInt()); + position.set("odbConnect", parser.nextInt(0) == 1); + position.set("dtcsNumber", parser.nextInt()); + position.set("dtcsCodes", parser.next()); + position.set(Position.KEY_THROTTLE, parser.nextInt()); + position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); + position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000); + + decodeLocation(position, parser); + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + + decodeDeviceTime(position, parser); + + return position; + } + + private void decodeStatus(Position position, Parser parser) { + if (parser.hasNext(3)) { + int ignition = parser.nextHexInt(0); + if (BitUtil.check(ignition, 4)) { + position.set(Position.KEY_IGNITION, false); + } else if (BitUtil.check(ignition, 5)) { + position.set(Position.KEY_IGNITION, true); + } + position.set(Position.KEY_INPUT, parser.nextHexInt(0)); + position.set(Position.KEY_OUTPUT, parser.nextHexInt(0)); + } + } + + private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_FRI, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + LinkedList<Position> positions = new LinkedList<>(); + + String vin = parser.next(); + int power = parser.nextInt(0); + + Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); + while (itemParser.find()) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_VIN, vin); + + decodeLocation(position, itemParser); + + positions.add(position); + } + + Position position = positions.getLast(); + + decodeLocation(position, parser); + + // power value only on some devices + if (power > 10) { + position.set(Position.KEY_POWER, power); + } + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_HOURS, 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); + + position.set(Position.KEY_RPM, parser.nextInt()); + position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); + + decodeDeviceTime(position, parser); + if (ignoreFixTime) { + positions.clear(); + positions.add(position); + } + + return positions; + } + + private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_ERI, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + LinkedList<Position> positions = new LinkedList<>(); + + int power = parser.nextInt(0); + + Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); + while (itemParser.find()) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeLocation(position, itemParser); + + positions.add(position); + } + + Position position = positions.getLast(); + + decodeLocation(position, parser); + + position.set(Position.KEY_POWER, power); + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_HOURS, 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); + + int index = 0; + String[] data = parser.next().split(","); + if (data.length > 1) { + int deviceType = Integer.parseInt(data[index++]); + if (deviceType == 2) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index++; // id + index++; // type + position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index++], 16) * 0.0625); + } + } + } + + decodeDeviceTime(position, parser); + if (ignoreFixTime) { + positions.clear(); + positions.add(position); + } + + return positions; + } + + private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_IGN, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + decodeLocation(position, parser); + + position.set(Position.KEY_HOURS, parser.next()); + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + + decodeDeviceTime(position, parser); + + return position; + } + + private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_IDA, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + + decodeLocation(position, parser); + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + + decodeDeviceTime(position, parser); + + return position; + } + + private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_WIF, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + getLastLocation(position, null); + + Network network = new Network(); + + parser.nextInt(0); // count + Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next()); + while (matcher.find()) { + String mac = matcher.group(1).replaceAll("(..)", "$1:"); + network.addWifiAccessPoint(WifiAccessPoint.from( + mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2)))); + } + + position.setNetwork(network); + + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + + return position; + } + + private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_GSM, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + getLastLocation(position, null); + + Network network = new Network(); + + String[] data = parser.next().split(","); + for (int i = 0; i < 6; i++) { + if (!data[i * 6].isEmpty()) { + network.addCellTower(CellTower.from( + Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]), + Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16), + Integer.parseInt(data[i * 6 + 4]))); + } + } + + position.setNetwork(network); + + return position; + } + + private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) { + Parser parser = new Parser(PATTERN, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int reportType = parser.nextInt(0); + if (type.equals("NMR")) { + position.set(Position.KEY_MOTION, reportType == 1); + } else if (type.equals("SOS")) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + + decodeLocation(position, parser); + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + + position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + + decodeDeviceTime(position, parser); + + if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) { + channel.write("+SACK:" + parser.next() + "$", remoteAddress); + } + + return position; + } + + private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) { + Parser parser = new Parser(PATTERN_BASIC, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int hdop = parser.nextInt(0); + position.setValid(hdop > 0); + position.set(Position.KEY_HDOP, hdop); + + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); + position.setCourse(parser.nextDouble(0)); + position.setAltitude(parser.nextDouble(0)); + + if (parser.hasNext(2)) { + position.setLongitude(parser.nextDouble(0)); + position.setLatitude(parser.nextDouble(0)); + } else { + getLastLocation(position, null); + } + + if (parser.hasNext(6)) { + position.setTime(parser.nextDateTime()); + } + + if (parser.hasNext(4)) { + position.setNetwork(new Network(CellTower.from( + parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0)))); + } + + decodeDeviceTime(position, parser); + + switch (type) { + case "PNA": + position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON); + break; + case "PFA": + position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); + break; + case "EPN": + position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED); + break; + case "EPF": + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case "BPL": + position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + break; + case "STT": + position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT); + break; + case "SWG": + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE); + break; + case "TMP": + case "TEM": + position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE); + break; + case "JDR": + case "JDS": + position.set(Position.KEY_ALARM, Position.ALARM_JAMMING); + break; + default: + break; + } + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = ((ChannelBuffer) msg).toString(StandardCharsets.US_ASCII); + + int typeIndex = sentence.indexOf(":GT"); + if (typeIndex < 0) { + return null; + } + + Object result; + String type = sentence.substring(typeIndex + 3, typeIndex + 6); + if (sentence.startsWith("+ACK")) { + result = decodeAck(channel, remoteAddress, sentence, type); + } else { + switch (type) { + case "INF": + result = decodeInf(channel, remoteAddress, sentence); + break; + case "OBD": + result = decodeObd(channel, remoteAddress, sentence); + break; + case "FRI": + result = decodeFri(channel, remoteAddress, sentence); + break; + case "ERI": + result = decodeEri(channel, remoteAddress, sentence); + break; + case "IGN": + case "IGF": + result = decodeIgn(channel, remoteAddress, sentence); + break; + case "IDA": + result = decodeIda(channel, remoteAddress, sentence); + break; + case "WIF": + result = decodeWif(channel, remoteAddress, sentence); + break; + case "GSM": + result = decodeGsm(channel, remoteAddress, sentence); + break; + case "VER": + result = decodeVer(channel, remoteAddress, sentence); + break; + default: + result = decodeOther(channel, remoteAddress, sentence, type); + break; + } + + if (result == null) { + result = decodeBasic(channel, remoteAddress, sentence, type); + } + + if (result != null) { + if (result instanceof Position) { + ((Position) result).set(Position.KEY_TYPE, type); + } else { + for (Position p : (List<Position>) result) { + p.set(Position.KEY_TYPE, type); + } + } + } + } + + return result; + } + +} diff --git a/src/org/traccar/protocol/GnxProtocolDecoder.java b/src/org/traccar/protocol/GnxProtocolDecoder.java index 070d394e8..2274ec164 100644 --- a/src/org/traccar/protocol/GnxProtocolDecoder.java +++ b/src/org/traccar/protocol/GnxProtocolDecoder.java @@ -102,7 +102,7 @@ public class GnxProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); if (type.equals("MIF")) { - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); } return position; diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java index f5ba3cff7..099047aa0 100644 --- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2017 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. @@ -248,6 +248,8 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, alarm.substring(2)); } else if (alarm.startsWith("oil ")) { position.set("oil", alarm.substring(4)); + } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) { + position.set(Position.KEY_EVENT, alarm); } DateBuilder dateBuilder = new DateBuilder() @@ -258,7 +260,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { String rfid = parser.next(); if (alarm.equals("rfid")) { - position.set(Position.KEY_RFID, rfid); + position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid); } String utcHours = parser.next(); diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/org/traccar/protocol/Gt06FrameDecoder.java index f35af6572..c8b5e56ae 100644 --- a/src/org/traccar/protocol/Gt06FrameDecoder.java +++ b/src/org/traccar/protocol/Gt06FrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2017 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. @@ -26,7 +26,6 @@ public class Gt06FrameDecoder extends FrameDecoder { protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { - // Check minimum length if (buf.readableBytes() < 5) { return null; } @@ -35,21 +34,22 @@ public class Gt06FrameDecoder extends FrameDecoder { if (buf.getByte(buf.readerIndex()) == 0x78) { length += 1 + buf.getUnsignedByte(buf.readerIndex() + 2); - - int type = buf.getUnsignedByte(buf.readerIndex() + 3); - if (type == Gt06ProtocolDecoder.MSG_STATUS && length == 13) { - length += 2; // workaround for #1727 - } - } else { length += 2 + buf.getUnsignedShort(buf.readerIndex() + 2); } - // Check length and return buffer - if (buf.readableBytes() >= length) { + if (buf.readableBytes() >= length && buf.getUnsignedShort(buf.readerIndex() + length - 2) == 0x0d0a) { return buf.readBytes(length); } + int endIndex = buf.readerIndex() - 1; + do { + endIndex = buf.indexOf(endIndex + 1, buf.writerIndex(), (byte) 0x0d); + if (endIndex > 0 && buf.writerIndex() > endIndex + 1 && buf.getByte(endIndex + 1) == 0x0a) { + return buf.readBytes(endIndex + 2 - buf.readerIndex()); + } + } while (endIndex > 0); + return null; } diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java index 24bedcabf..fbd1adfc6 100644 --- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -31,6 +31,7 @@ import org.traccar.model.CellTower; import org.traccar.model.Device; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; @@ -68,6 +69,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_GPS_LBS_STATUS_1 = 0x16; public static final int MSG_GPS_LBS_STATUS_2 = 0x26; public static final int MSG_GPS_LBS_STATUS_3 = 0x27; + public static final int MSG_LBS_MULTIPLE = 0x28; + public static final int MSG_LBS_WIFI = 0x2C; public static final int MSG_LBS_PHONE = 0x17; public static final int MSG_LBS_EXTEND = 0x18; public static final int MSG_LBS_STATUS = 0x19; @@ -152,9 +155,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return false; } - int length = buf.readUnsignedByte(); - position.set(Position.KEY_SATELLITES, BitUtil.to(length, 4)); - length = BitUtil.from(length, 4); + position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4)); double latitude = buf.readUnsignedInt() / 60.0 / 30000.0; double longitude = buf.readUnsignedInt() / 60.0 / 30000.0; @@ -178,10 +179,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_IGNITION, BitUtil.check(flags, 15)); } - if (length > 0) { - buf.skipBytes(length - 12); // skip reserved - } - return true; } @@ -199,7 +196,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedShort(), buf.readUnsignedMedium()))); if (length > 0) { - buf.skipBytes(length - 8); + buf.skipBytes(length - (hasLength ? 9 : 8)); } return true; @@ -336,7 +333,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1); buf.readUnsignedShort(); // type - // Timezone offset if (dataLength > 10) { int extensionBits = buf.readUnsignedShort(); int hours = (extensionBits >> 4) / 100; @@ -391,77 +387,104 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else { - Position position = new Position(); - position.setDeviceId(deviceSession.getDeviceId()); - position.setProtocol(getProtocolName()); - - if (type == MSG_LBS_EXTEND) { + return decodeBasicOther(channel, buf, deviceSession, type, dataLength); - DateBuilder dateBuilder = new DateBuilder(timeZone) - .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) - .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + } - getLastLocation(position, dateBuilder.getDate()); + return null; + } - int mcc = buf.readUnsignedShort(); - int mnc = buf.readUnsignedByte(); + private Object decodeBasicOther(Channel channel, ChannelBuffer buf, + DeviceSession deviceSession, int type, int dataLength) throws Exception { - Network network = new Network(); - for (int i = 0; i < 7; i++) { - network.addCellTower(CellTower.from( - mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedMedium(), -buf.readUnsignedByte())); - } - position.setNetwork(network); + Position position = new Position(); + position.setDeviceId(deviceSession.getDeviceId()); + position.setProtocol(getProtocolName()); - } else if (type == MSG_STRING) { + if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI) { - getLastLocation(position, null); + DateBuilder dateBuilder = new DateBuilder(timeZone) + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); - int commandLength = buf.readUnsignedByte(); + getLastLocation(position, dateBuilder.getDate()); - if (commandLength > 0) { - buf.readUnsignedByte(); // server flag (reserved) - position.set(Position.KEY_RESULT, - buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII)); + int mcc = buf.readUnsignedShort(); + int mnc = buf.readUnsignedByte(); + Network network = new Network(); + for (int i = 0; i < 7; i++) { + int lac = buf.readUnsignedShort(); + int cid = buf.readUnsignedMedium(); + int rssi = -buf.readUnsignedByte(); + if (lac > 0) { + network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi)); } + } - } else if (isSupported(type)) { + buf.readUnsignedByte(); // time leads - if (hasGps(type)) { - decodeGps(position, buf, false); - } else { - getLastLocation(position, null); + if (type != MSG_LBS_MULTIPLE) { + int wifiCount = buf.readUnsignedByte(); + for (int i = 0; i < wifiCount; i++) { + String mac = ChannelBuffers.hexDump(buf.readBytes(6)).replaceAll("(..)", "$1:"); + network.addWifiAccessPoint(WifiAccessPoint.from( + mac.substring(0, mac.length() - 1), buf.readUnsignedByte())); } + } - if (hasLbs(type)) { - decodeLbs(position, buf, hasStatus(type)); - } + position.setNetwork(network); - if (hasStatus(type)) { - decodeStatus(position, buf); - } + } else if (type == MSG_STRING) { - if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 4 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - } + getLastLocation(position, null); + int commandLength = buf.readUnsignedByte(); + + if (commandLength > 0) { + buf.readUnsignedByte(); // server flag (reserved) + position.set(Position.KEY_RESULT, + buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII)); + } + + } else if (isSupported(type)) { + + if (hasGps(type)) { + decodeGps(position, buf, false); } else { + getLastLocation(position, null); + } - buf.skipBytes(dataLength); - if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) { - sendResponse(channel, false, type); - } - return null; + if (hasLbs(type)) { + decodeLbs(position, buf, hasStatus(type)); + } + if (hasStatus(type)) { + decodeStatus(position, buf); } - sendResponse(channel, false, type); + if (type == MSG_GPS_LBS_1 && buf.readableBytes() >= 4 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + } - return position; + 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); + } + + } else { + + buf.skipBytes(dataLength); + if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) { + sendResponse(channel, false, type); + } + return null; } - return null; + sendResponse(channel, false, type); + + return position; } private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception { @@ -509,7 +532,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (subType == 0x05) { int flags = buf.readUnsignedByte(); - position.set("door", BitUtil.check(flags, 0)); + position.set(Position.KEY_DOOR, BitUtil.check(flags, 0)); position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2)); return position; @@ -533,7 +556,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { if (photo.writableBytes() > 0) { sendPhotoRequest(channel, pictureId); } else { - Device device = Context.getDeviceManager().getDeviceById(deviceSession.getDeviceId()); + Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId()); Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg"); photos.remove(pictureId); } @@ -544,7 +567,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, position.getDeviceTime()); } - decodeLbs(position, buf, true); + if (decodeLbs(position, buf, true)) { + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + } buf.skipBytes(buf.readUnsignedByte()); // additional cell towers buf.skipBytes(buf.readUnsignedByte()); // wifi access point diff --git a/src/org/traccar/protocol/H02Protocol.java b/src/org/traccar/protocol/H02Protocol.java index df64402f8..66965e9db 100644 --- a/src/org/traccar/protocol/H02Protocol.java +++ b/src/org/traccar/protocol/H02Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.traccar.protocol; +import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.handler.codec.string.StringEncoder; @@ -50,5 +51,13 @@ public class H02Protocol extends BaseProtocol { pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this)); } }); + serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("objectEncoder", new H02ProtocolEncoder()); + pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this)); + } + }); } } diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java index aa3d47650..4414870d2 100644 --- a/src/org/traccar/protocol/H02ProtocolDecoder.java +++ b/src/org/traccar/protocol/H02ProtocolDecoder.java @@ -150,10 +150,22 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { .text("*") .expression("..,") // manufacturer .number("(d+),") // imei - .expression("[^,]+,") - .any() + .groupBegin() + .text("VP1,") + .or() + .groupBegin() + .text("V4,") + .expression("(.*),") // response + .or() + .expression("V[^,]*,") + .groupEnd() .number("(?:(dd)(dd)(dd))?,") // time (hhmmss) - .expression("([AV])?,") // validity + .groupEnd() + .groupBegin() + .expression("([ABV])?,") // validity + .or() + .number("(d+),") // coding scheme + .groupEnd() .groupBegin() .number("-(d+)-(d+.d+),") // latitude .or() @@ -169,25 +181,28 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { .number("(d+.?d*),") // speed .number("(d+.?d*)?,") // course .number("(?:(dd)(dd)(dd))?") // date (ddmmyy) - .any() - .number(",(x{8})") // status + .groupBegin() + .expression(",[^,]*,") + .expression("[^,]*,") + .expression("[^,]*") // sim info + .groupEnd("?") + .groupBegin() + .number(",(x{8})") .groupBegin() .number(",(d+),") // odometer .number("(-?d+),") // temperature .number("(d+.d+),") // fuel .number("(-?d+),") // altitude .number("(x+),") // lac - .number("(x+)#") // cid + .number("(x+)") // cid .or() - .number(",(d+),") - .number("(d+),") - .number("(d+),") - .number("(d+)#") + .text(",") + .expression("(.*)") // data .or() - .expression(",.*") + .groupEnd() .or() - .text("#") .groupEnd() + .text("#") .compile(); private static final Pattern PATTERN_NBR = new PatternBuilder() @@ -222,6 +237,24 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_V3 = new PatternBuilder() + .text("*") + .expression("..,") // manufacturer + .number("(d+),") // imei + .text("V3,") + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(ddd)") // mcc + .number("(d+),") // mnc + .number("(d+),") // count + .expression("(.*),") // cell info + .number("(x{4}),") // battery + .number("d+,") // reboot info + .text("X,") + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(x{8})") // status + .text("#").optional() + .compile(); + private Position decodeText(String sentence, Channel channel, SocketAddress remoteAddress) { Parser parser = new Parser(PATTERN, sentence); @@ -238,6 +271,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + if (parser.hasNext()) { + position.set(Position.KEY_RESULT, parser.next()); + } + DateBuilder dateBuilder = new DateBuilder(); if (parser.hasNext(3)) { dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); @@ -246,6 +283,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext()) { position.setValid(parser.next().equals("A")); } + if (parser.hasNext()) { + parser.nextInt(); // coding scheme + position.setValid(true); + } if (parser.hasNext(2)) { position.setLatitude(-parser.nextCoordinate()); @@ -271,7 +312,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { position.setTime(new Date()); } - processStatus(position, parser.nextLong(16, 0)); + if (parser.hasNext()) { + processStatus(position, parser.nextLong(16, 0)); + } if (parser.hasNext(6)) { position.set(Position.KEY_ODOMETER, parser.nextInt(0)); @@ -284,8 +327,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext(4)) { - for (int i = 1; i <= 4; i++) { - position.set(Position.PREFIX_IO + i, parser.nextInt(0)); + String[] values = parser.next().split(","); + for (int i = 0; i < values.length; i++) { + position.set(Position.PREFIX_IO + (i + 1), values[i].trim()); } } @@ -354,7 +398,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextInt()); position.set(Position.KEY_SATELLITES, parser.nextInt()); position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - position.set("steps", parser.nextInt()); + position.set(Position.KEY_STEPS, parser.nextInt()); position.set("turnovers", parser.nextInt()); dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); @@ -366,6 +410,48 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeV3(String sentence, Channel channel, SocketAddress remoteAddress) { + + Parser parser = new Parser(PATTERN_V3, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + + int mcc = parser.nextInt(); + int mnc = parser.nextInt(); + + int count = parser.nextInt(); + Network network = new Network(); + String[] values = parser.next().split(","); + for (int i = 0; i < count; i++) { + network.addCellTower(CellTower.from( + mcc, mnc, Integer.parseInt(values[i * 4]), Integer.parseInt(values[i * 4 + 1]))); + } + position.setNetwork(network); + + position.set(Position.KEY_BATTERY, parser.nextHexInt()); + + dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + + getLastLocation(position, dateBuilder.getDate()); + + processStatus(position, parser.nextLong(16, 0)); + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -385,6 +471,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { return decodeLbs(sentence, channel, remoteAddress); case "LINK": return decodeLink(sentence, channel, remoteAddress); + case "V3": + return decodeV3(sentence, channel, remoteAddress); default: return decodeText(sentence, channel, remoteAddress); } diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java index 2bd02d3f1..f76fd8069 100644 --- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2017 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,6 +21,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.helper.BcdUtil; +import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -31,6 +32,8 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; import java.util.regex.Pattern; public class Jt600ProtocolDecoder extends BaseProtocolDecoder { @@ -45,10 +48,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { return degrees + minutes / 60; } - private Position decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) { + private List<Position> decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) { - Position position = new Position(); - position.setProtocol(getProtocolName()); + List<Position> positions = new LinkedList<>(); buf.readByte(); // header @@ -59,97 +61,106 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { if (deviceSession == null) { return null; } - position.setDeviceId(deviceSession.getDeviceId()); if (longFormat) { buf.readUnsignedByte(); // protocol } - int version = buf.readUnsignedByte() >> 4; + int version = BitUtil.from(buf.readUnsignedByte(), 4); buf.readUnsignedShort(); // length - DateBuilder dateBuilder = new DateBuilder() - .setDay(BcdUtil.readInteger(buf, 2)) - .setMonth(BcdUtil.readInteger(buf, 2)) - .setYear(BcdUtil.readInteger(buf, 2)) - .setHour(BcdUtil.readInteger(buf, 2)) - .setMinute(BcdUtil.readInteger(buf, 2)) - .setSecond(BcdUtil.readInteger(buf, 2)); - position.setTime(dateBuilder.getDate()); - - double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8)); - double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9)); - - byte flags = buf.readByte(); - position.setValid((flags & 0x1) == 0x1); - if ((flags & 0x2) == 0) { - latitude = -latitude; - } - position.setLatitude(latitude); - if ((flags & 0x4) == 0) { - longitude = -longitude; - } - position.setLongitude(longitude); + while (buf.readableBytes() > 1) { - position.setSpeed(BcdUtil.readInteger(buf, 2)); - position.setCourse(buf.readUnsignedByte() * 2.0); + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); - if (longFormat) { + DateBuilder dateBuilder = new DateBuilder() + .setDay(BcdUtil.readInteger(buf, 2)) + .setMonth(BcdUtil.readInteger(buf, 2)) + .setYear(BcdUtil.readInteger(buf, 2)) + .setHour(BcdUtil.readInteger(buf, 2)) + .setMinute(BcdUtil.readInteger(buf, 2)) + .setSecond(BcdUtil.readInteger(buf, 2)); + position.setTime(dateBuilder.getDate()); - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8)); + double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9)); - buf.readUnsignedInt(); // vehicle id combined + byte flags = buf.readByte(); + position.setValid((flags & 0x1) == 0x1); + if ((flags & 0x2) == 0) { + latitude = -latitude; + } + position.setLatitude(latitude); + if ((flags & 0x4) == 0) { + longitude = -longitude; + } + position.setLongitude(longitude); - position.set(Position.KEY_STATUS, buf.readUnsignedShort()); + position.setSpeed(BcdUtil.readInteger(buf, 2)); + position.setCourse(buf.readUnsignedByte() * 2.0); - int battery = buf.readUnsignedByte(); - if (battery == 0xff) { - position.set(Position.KEY_CHARGE, true); - } else { - position.set(Position.KEY_BATTERY_LEVEL, battery); - } + if (longFormat) { + + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort()); - cellTower.setSignalStrength((int) buf.readUnsignedByte()); - position.setNetwork(new Network(cellTower)); + buf.readUnsignedInt(); // vehicle id combined - position.set(Position.KEY_INDEX, buf.readUnsignedByte()); + position.set(Position.KEY_STATUS, buf.readUnsignedShort()); - } else if (version == 1) { + int battery = buf.readUnsignedByte(); + if (battery == 0xff) { + position.set(Position.KEY_CHARGE, true); + } else { + position.set(Position.KEY_BATTERY_LEVEL, battery); + } - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - position.set(Position.KEY_POWER, buf.readUnsignedByte()); + CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort()); + cellTower.setSignalStrength((int) buf.readUnsignedByte()); + position.setNetwork(new Network(cellTower)); - buf.readByte(); // other flags and sensors + } else if (version == 1) { - position.setAltitude(buf.readUnsignedShort()); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_POWER, buf.readUnsignedByte()); - int cid = buf.readUnsignedShort(); - int lac = buf.readUnsignedShort(); - int rssi = buf.readUnsignedByte(); + buf.readByte(); // other flags and sensors - if (cid != 0 && lac != 0) { - CellTower cellTower = CellTower.fromCidLac(cid, lac); - cellTower.setSignalStrength(rssi); - position.setNetwork(new Network(cellTower)); - } else { - position.set(Position.KEY_RSSI, rssi); - } + position.setAltitude(buf.readUnsignedShort()); + + int cid = buf.readUnsignedShort(); + int lac = buf.readUnsignedShort(); + int rssi = buf.readUnsignedByte(); + + if (cid != 0 && lac != 0) { + CellTower cellTower = CellTower.fromCidLac(cid, lac); + cellTower.setSignalStrength(rssi); + position.setNetwork(new Network(cellTower)); + } else { + position.set(Position.KEY_RSSI, rssi); + } - } else if (version == 2) { + } else if (version == 2) { - int fuel = buf.readUnsignedByte() << 8; + int fuel = buf.readUnsignedByte() << 8; - position.set(Position.KEY_STATUS, buf.readUnsignedInt()); - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); + position.set(Position.KEY_STATUS, buf.readUnsignedInt()); + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); - fuel += buf.readUnsignedByte(); - position.set(Position.KEY_FUEL_LEVEL, fuel); + fuel += buf.readUnsignedByte(); + position.set(Position.KEY_FUEL_LEVEL, fuel); + + } + + positions.add(position); } - return position; + buf.readUnsignedByte(); // index + + return positions; } private static final Pattern PATTERN_W01 = new PatternBuilder() diff --git a/src/org/traccar/protocol/Jt600ProtocolEncoder.java b/src/org/traccar/protocol/Jt600ProtocolEncoder.java index 0bf389460..377f104a3 100644 --- a/src/org/traccar/protocol/Jt600ProtocolEncoder.java +++ b/src/org/traccar/protocol/Jt600ProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package org.traccar.protocol; +import java.util.TimeZone; + import org.traccar.StringProtocolEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; @@ -29,7 +31,7 @@ public class Jt600ProtocolEncoder extends StringProtocolEncoder { case Command.TYPE_ENGINE_RESUME: return "(S07,1)"; case Command.TYPE_SET_TIMEZONE: - int offset = command.getInteger(Command.KEY_TIMEZONE) / 60; + int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000; return "(S09,1," + offset + ")"; case Command.TYPE_REBOOT_DEVICE: return "(S17)"; diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java index 15a384cc0..3ef52acd1 100644 --- a/src/org/traccar/protocol/MegastekProtocolDecoder.java +++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java @@ -267,7 +267,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { .or().text(" ") .groupEnd("?").text(",") .number("(d+)?,") // rfid - .number("d*,") + .expression("[^,]*,") .number("(d+)?,") // battery .expression("([^,]*);") // alert .any() @@ -280,13 +280,13 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(); - position.setProtocol(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(); + position.setProtocol(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); if (parser.next().equals("S")) { @@ -327,7 +327,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { } } - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); String battery = parser.next(); if (battery != null) { diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java index 44e01d5e0..e41a42843 100644 --- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java +++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java @@ -94,8 +94,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { .number("(d+.d+),") // single fuel consumption .number("(d+.d+),") // total fuel consumption .number("(d+),") // error code count - .number("(d+),") // harsh acceleration count - .number("(d+)") // harsh break count + .number("(d+),") // hard acceleration count + .number("(d+)") // hard brake count .compile(); private static final Pattern PATTERN_OBDA = new PatternBuilder() @@ -106,8 +106,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // average speed .number("(d+),") // history highest speed .number("(d+),") // history highest rpm - .number("(d+),") // total harsh acceleration - .number("(d+)") // total harsh break n0 + .number("(d+),") // total hard acceleration + .number("(d+)") // total hard brake .compile(); public static final int MSG_HEARTBEAT = 0x0001; @@ -194,10 +194,16 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_MOVEMENT; case 0x13: return Position.ALARM_GEOFENCE_ENTER; + case 0x14: + return Position.ALARM_ACCIDENT; case 0x50: return Position.ALARM_POWER_OFF; case 0x53: return Position.ALARM_GPS_ANTENNA_CUT; + case 0x72: + return Position.ALARM_BRAKING; + case 0x73: + return Position.ALARM_ACCELERATION; default: return null; } @@ -253,7 +259,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { - position.set(Position.KEY_RFID, parser.nextHexInt(0)); + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(parser.nextHexInt(0))); } return position; @@ -295,8 +301,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { position.set("singleFuelConsumption", parser.nextDouble()); position.set("totalFuelConsumption", parser.nextDouble()); position.set(Position.KEY_DTCS, parser.nextInt()); - position.set("harshAcelerationNo", parser.nextInt()); - position.set("harshBreakerNo", parser.nextInt()); + position.set("hardAccelerationCount", parser.nextInt()); + position.set("hardBrakingCount", parser.nextInt()); return position; } @@ -353,7 +359,13 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); if (command == MSG_ALARM) { - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + short alarmCode = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(alarmCode)); + if (alarmCode >= 0x02 && alarmCode <= 0x05) { + position.set(Position.PREFIX_IN + alarmCode, 1); + } else if (alarmCode >= 0x32 && alarmCode <= 0x35) { + position.set(Position.PREFIX_IN + (alarmCode - 0x30), 0); + } } else if (command == MSG_POSITION_LOGGED) { buf.skipBytes(6); } @@ -370,7 +382,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { if (rfid != 0) { String card = String.format("%010d", rfid); position.set("card" + (i + 1), card); - position.set(Position.KEY_RFID, card); + position.set(Position.KEY_DRIVER_UNIQUE_ID, card); } } } diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java index 268bae392..2e0a1e84c 100644 --- a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java +++ b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 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.model.Command; import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; +import java.util.TimeZone; public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { @@ -78,7 +79,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { content.writeShort(command.getInteger(Command.KEY_RADIUS)); return encodeContent(command.getDeviceId(), MSG_MOVEMENT_ALARM, content); case Command.TYPE_SET_TIMEZONE: - int offset = command.getInteger(Command.KEY_TIMEZONE) / 60; + int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000; content.writeBytes(String.valueOf(offset).getBytes(StandardCharsets.US_ASCII)); return encodeContent(command.getDeviceId(), MSG_TIME_ZONE, content); case Command.TYPE_REBOOT_DEVICE: diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java index 38ecde519..efc9c24db 100644 --- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -20,6 +20,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.DeviceSession; +import org.traccar.helper.Checksum; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; @@ -69,16 +70,15 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { .number("(x+)?|") // adc2 .number("(x+)?|") // adc3 .number("(x+)|") // battery - .number("(x+),") // power + .number("(x+)?,") // power .groupBegin() .expression("([^,]+)?,") // event specific .expression("[^,]*,") // reserved - .number("d*,") // protocol + .number("(d+)?,") // protocol .number("(x{4})?") // fuel .number("(?:,(x{6}(?:|x{6})*))?") // temperature - .or() + .groupEnd("?") .any() - .groupEnd() .text("*") .number("xx") .text("\r\n").optional() @@ -160,7 +160,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { } } - String deviceModel = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getModel(); + String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel(); if (deviceModel == null) { deviceModel = ""; } @@ -201,7 +201,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { if (eventData != null && !eventData.isEmpty()) { switch (event) { case 37: - position.set(Position.KEY_RFID, eventData); + position.set(Position.KEY_DRIVER_UNIQUE_ID, eventData); break; default: position.set("eventData", eventData); @@ -209,6 +209,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { } } + int protocol = parser.nextInt(0); + if (parser.hasNext()) { String fuel = parser.next(); position.set(Position.KEY_FUEL_LEVEL, @@ -218,9 +220,14 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext()) { for (String temp : parser.next().split("\\|")) { int index = Integer.valueOf(temp.substring(0, 2), 16); - double value = Byte.valueOf(temp.substring(2, 4), 16); - value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16); - position.set(Position.PREFIX_TEMP + index, value); + if (protocol >= 3) { + double value = Short.valueOf(temp.substring(2), 16); + position.set(Position.PREFIX_TEMP + index, value * 0.01); + } else { + double value = Byte.valueOf(temp.substring(2, 4), 16); + value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16); + position.set(Position.PREFIX_TEMP + index, value); + } } } @@ -307,7 +314,6 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { ChannelBuffer buf = (ChannelBuffer) msg; - // Find type int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ','); index = buf.indexOf(index + 1, buf.writerIndex(), (byte) ','); @@ -316,8 +322,12 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case "D03": if (channel != null) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - String imei = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getUniqueId(); - channel.write("@@O46," + imei + ",D00,camera_picture.jpg,0*00\r\n"); + String imei = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId(); + String content = "D00,camera_picture.jpg,0"; + int length = 1 + imei.length() + 1 + content.length() + 5; + String response = String.format("@@O%02d,%s,%s*", length, imei, content); + response += Checksum.sum(response) + "\r\n"; + channel.write(response); } return null; case "CCC": diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java index 8bfb4fb36..05994b697 100644 --- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java +++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java @@ -133,7 +133,11 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { String sentence = (String) msg; if (sentence.startsWith("!1,")) { - getDeviceSession(channel, remoteAddress, sentence.substring(3, sentence.length())); + int index = sentence.indexOf(',', 3); + if (index < 0) { + index = sentence.length(); + } + getDeviceSession(channel, remoteAddress, sentence.substring(3, index)); return null; } diff --git a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java index e5c43e29a..486f406a5 100644 --- a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java +++ b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package org.traccar.protocol; +import java.util.TimeZone; + import org.traccar.StringProtocolEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; @@ -27,7 +29,7 @@ public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements if (key.equals(Command.KEY_ENABLE)) { return (Boolean) value ? "1" : "0"; } else if (key.equals(Command.KEY_TIMEZONE)) { - return String.format("%+03d", ((Number) value).longValue() / 3600); + return String.format("%+03d", TimeZone.getTimeZone((String) value).getRawOffset() / 3600000); } else if (key.equals(Command.KEY_INDEX)) { switch (((Number) value).intValue()) { case 0: diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java index 49987ce57..6d82e4a4b 100644 --- a/src/org/traccar/protocol/MxtProtocolDecoder.java +++ b/src/org/traccar/protocol/MxtProtocolDecoder.java @@ -159,7 +159,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder { } if (BitUtil.check(infoGroups, 7)) { - position.set(Position.KEY_RFID, buf.readUnsignedInt()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt())); } buf.readerIndex(buf.writerIndex() - 3); diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java index 15f6f40b8..15a71c88b 100644 --- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java +++ b/src/org/traccar/protocol/OsmAndProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2017 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. @@ -110,7 +110,7 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(Double.parseDouble(location[1])); break; case "speed": - position.setSpeed(Double.parseDouble(value)); + position.setSpeed(convertSpeed(Double.parseDouble(value), "kn")); break; case "bearing": case "heading": @@ -128,11 +128,24 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { case "batt": position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value)); break; + case "driverUniqueId": + position.set(Position.KEY_DRIVER_UNIQUE_ID, value); + break; default: try { position.set(entry.getKey(), Double.parseDouble(value)); } catch (NumberFormatException e) { - position.set(entry.getKey(), value); + switch (value) { + case "true": + position.set(entry.getKey(), true); + break; + case "false": + position.set(entry.getKey(), false); + break; + default: + position.set(entry.getKey(), value); + break; + } } break; } diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java index b1851f8ca..fef5d9b39 100644 --- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -67,7 +67,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { case "HDA":
return Position.ALARM_ACCELERATION;
case "HDB":
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case "FDA":
return Position.ALARM_FATIGUE_DRIVING;
case "SKA":
@@ -129,7 +129,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { }
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
int value = parser.nextHexInt(0);
diff --git a/src/org/traccar/protocol/Pt502ProtocolEncoder.java b/src/org/traccar/protocol/Pt502ProtocolEncoder.java index 5f7665e50..4a876f6da 100644 --- a/src/org/traccar/protocol/Pt502ProtocolEncoder.java +++ b/src/org/traccar/protocol/Pt502ProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,27 @@ */ package org.traccar.protocol; +import java.util.TimeZone; + import org.traccar.StringProtocolEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; -public class Pt502ProtocolEncoder extends StringProtocolEncoder { +public class Pt502ProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter { + + @Override + public String formatValue(String key, Object value) { + if (key.equals(Command.KEY_TIMEZONE)) { + return String.valueOf(TimeZone.getTimeZone((String) value).getRawOffset() / 3600000); + } + + return null; + } + + @Override + protected String formatCommand(Command command, String format, String... keys) { + return formatCommand(command, format, this, keys); + } @Override protected Object encodeCommand(Command command) { diff --git a/src/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/org/traccar/protocol/RuptelaProtocolDecoder.java index ac95ed27a..8752d30c0 100644 --- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java +++ b/src/org/traccar/protocol/RuptelaProtocolDecoder.java @@ -74,6 +74,43 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { } } + private long readValue(ChannelBuffer buf, int length, boolean signed) { + switch (length) { + case 1: + return signed ? buf.readByte() : buf.readUnsignedByte(); + case 2: + return signed ? buf.readShort() : buf.readUnsignedShort(); + case 4: + return signed ? buf.readInt() : buf.readUnsignedInt(); + default: + return buf.readLong(); + } + } + + private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) { + switch (id) { + case 2: + case 3: + case 4: + position.set("di" + (id - 1), readValue(buf, length, false)); + break; + case 5: + position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1); + break; + case 74: + position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1); + break; + case 78: + case 79: + case 80: + position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1); + break; + default: + position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); + break; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -133,28 +170,28 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { int cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte(); - position.set(Position.PREFIX_IO + id, buf.readUnsignedByte()); + decodeParameter(position, id, buf, 1); } // Read 2 byte data cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte(); - position.set(Position.PREFIX_IO + id, buf.readUnsignedShort()); + decodeParameter(position, id, buf, 2); } // Read 4 byte data cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte(); - position.set(Position.PREFIX_IO + id, buf.readUnsignedInt()); + decodeParameter(position, id, buf, 4); } // Read 8 byte data cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte(); - position.set(Position.PREFIX_IO + id, buf.readLong()); + decodeParameter(position, id, buf, 8); } positions.add(position); diff --git a/src/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/org/traccar/protocol/StarLinkProtocolDecoder.java index e90dde455..79f013fac 100644 --- a/src/org/traccar/protocol/StarLinkProtocolDecoder.java +++ b/src/org/traccar/protocol/StarLinkProtocolDecoder.java @@ -69,6 +69,29 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { return value.charAt(0) == '+' ? result : -result; } + private String decodeAlarm(int event) { + switch (event) { + case 6: + return Position.ALARM_OVERSPEED; + case 7: + return Position.ALARM_GEOFENCE_ENTER; + case 8: + return Position.ALARM_GEOFENCE_EXIT; + case 9: + return Position.ALARM_POWER_CUT; + case 11: + return Position.ALARM_LOW_BATTERY; + case 26: + return Position.ALARM_TOW; + case 36: + return Position.ALARM_SOS; + case 42: + return Position.ALARM_JAMMING; + default: + return null; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -109,6 +132,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { break; case "#EID#": event = Integer.parseInt(data[i]); + position.set(Position.KEY_ALARM, decodeAlarm(event)); position.set(Position.KEY_EVENT, event); break; case "#PDT#": @@ -196,7 +220,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { if (rfid.matches("0+")) { rfid = data[data.length - 2]; } - position.set(Position.KEY_RFID, rfid); + position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid); } return position; diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/org/traccar/protocol/Stl060ProtocolDecoder.java index c81e83aab..26817a5c8 100644 --- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java +++ b/src/org/traccar/protocol/Stl060ProtocolDecoder.java @@ -104,7 +104,7 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1); position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1); position.set(Position.KEY_INPUT, parser.nextInt(0)); - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextInt(0)); position.set(Position.PREFIX_TEMP + 1, parser.nextInt(0)); position.set(Position.KEY_FUEL_LEVEL, parser.nextInt(0)); diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java index 42e81f60c..6dfc6f77f 100644 --- a/src/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java @@ -19,6 +19,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.DeviceSession; +import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -32,18 +33,34 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { private int protocolType; private boolean hbm; + private boolean includeAdc; + private boolean includeTemp; public SuntechProtocolDecoder(SuntechProtocol protocol) { super(protocol); protocolType = Context.getConfig().getInteger(getProtocolName() + ".protocolType"); hbm = Context.getConfig().getBoolean(getProtocolName() + ".hbm"); + includeAdc = Context.getConfig().getBoolean(getProtocolName() + ".includeAdc"); + includeTemp = Context.getConfig().getBoolean(getProtocolName() + ".includeTemp"); } public void setProtocolType(int protocolType) { this.protocolType = protocolType; } + public void setHbm(boolean hbm) { + this.hbm = hbm; + } + + public void setIncludeAdc(boolean includeAdc) { + this.includeAdc = includeAdc; + } + + public void setIncludeTemp(boolean includeTemp) { + this.includeTemp = includeTemp; + } + private Position decode9( Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException { int index = 1; @@ -54,19 +71,19 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return null; } + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); + if (deviceSession == null) { + return null; + } + Position position = new Position(); position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); if (type.equals("Emergency") || type.equals("Alert")) { position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); - if (!type.equals("Alert") || protocolType == 0) { position.set(Position.KEY_VERSION_FW, values[index++]); } @@ -87,13 +104,60 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setValid(values[index++].equals("1")); if (protocolType == 1) { - position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index])); + position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++])); } return position; } - private Position decode23( + private String decodeEmergency(int value) { + switch (value) { + case 1: + return Position.ALARM_SOS; + case 2: + return Position.ALARM_PARKING; + case 3: + return Position.ALARM_POWER_CUT; + case 5: + case 6: + return Position.ALARM_DOOR; + case 7: + return Position.ALARM_MOVEMENT; + case 8: + return Position.ALARM_SHOCK; + default: + return null; + } + } + + private String decodeAlert(int value) { + switch (value) { + case 1: + return Position.ALARM_OVERSPEED; + case 5: + return Position.ALARM_GEOFENCE_EXIT; + case 6: + return Position.ALARM_GEOFENCE_ENTER; + case 14: + return Position.ALARM_LOW_BATTERY; + case 15: + return Position.ALARM_SHOCK; + case 16: + return Position.ALARM_ACCIDENT; + case 46: + return Position.ALARM_ACCELERATION; + case 47: + return Position.ALARM_BRAKING; + case 48: + return Position.ALARM_ACCIDENT; + case 50: + return Position.ALARM_JAMMING; + default: + return null; + } + } + + private Position decode235( Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException { int index = 0; @@ -103,20 +167,17 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(); - position.setProtocol(getProtocolName()); - - if (type.equals("EMG") || type.equals("ALT")) { - position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); - } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); if (deviceSession == null) { return null; } + + Position position = new Position(); + position.setProtocol(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_TYPE, type); - if (protocol.equals("ST300")) { + if (protocol.equals("ST300") || protocol.equals("ST500")) { index += 1; // model } @@ -126,7 +187,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); position.setTime(dateFormat.parse(values[index++] + values[index++])); - index += 1; // cell + if (!protocol.equals("ST500")) { + index += 1; // cell + } position.setLatitude(Double.parseDouble(values[index++])); position.setLongitude(Double.parseDouble(values[index++])); @@ -140,12 +203,32 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++])); position.set(Position.KEY_POWER, Double.parseDouble(values[index++])); - position.set(Position.PREFIX_IO + 1, values[index++]); - - index += 1; // mode + String io = values[index++]; + if (io.length() == 6) { + position.set(Position.KEY_IGNITION, io.charAt(0) == '1'); + position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1'); + position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1'); + position.set(Position.PREFIX_IN + 3, io.charAt(3) == '1'); + position.set(Position.PREFIX_OUT + 1, io.charAt(4) == '1'); + position.set(Position.PREFIX_OUT + 2, io.charAt(5) == '1'); + } - if (type.equals("STT")) { - position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); + switch (type) { + case "STT": + index += 1; // mode + position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); + break; + case "EMG": + position.set(Position.KEY_ALARM, decodeEmergency(Integer.parseInt(values[index++]))); + break; + case "EVT": + position.set(Position.KEY_EVENT, Integer.parseInt(values[index++])); + break; + case "ALT": + position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++]))); + break; + default: + break; } if (hbm) { @@ -155,7 +238,35 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } if (index < values.length) { - position.set(Position.KEY_BATTERY, Double.parseDouble(values[index])); + position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++])); + } + + if (index < values.length && values[index++].equals("0")) { + position.set(Position.KEY_ARCHIVE, true); + } + + if (includeAdc) { + position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++])); + position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++])); + position.set(Position.PREFIX_ADC + 3, Double.parseDouble(values[index++])); + } + + if (values.length - index >= 2) { + String driverUniqueId = values[index++]; + if (values[index++].equals("1") && !driverUniqueId.isEmpty()) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId); + } + } + + if (includeTemp) { + for (int i = 1; i <= 3; i++) { + String temperature = values[index++]; + String value = temperature.substring(temperature.indexOf(':') + 1); + if (!value.isEmpty()) { + position.set(Position.PREFIX_TEMP + i, Double.parseDouble(value)); + } + } + } } @@ -163,18 +274,105 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeUniversal( + Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException { + int index = 0; + + String type = values[index++]; + + if (!type.equals("STT")) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_TYPE, type); + + int mask = Integer.parseInt(values[index++], 16); + + if (BitUtil.check(mask, 1)) { + index += 1; // model + } + + if (BitUtil.check(mask, 2)) { + position.set(Position.KEY_VERSION_FW, values[index++]); + } + + if (BitUtil.check(mask, 3) && values[index++].equals("0")) { + position.set(Position.KEY_ARCHIVE, true); + } + + if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) { + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + position.setTime(dateFormat.parse(values[index++] + values[index++])); + } + + if (BitUtil.check(mask, 6)) { + index += 1; // cell + } + + if (BitUtil.check(mask, 7)) { + index += 1; // mcc + } + + if (BitUtil.check(mask, 8)) { + index += 1; // mnc + } + + if (BitUtil.check(mask, 9)) { + index += 1; // lac + } + + if (BitUtil.check(mask, 10)) { + position.set(Position.KEY_RSSI, Integer.parseInt(values[index++])); + } + + if (BitUtil.check(mask, 11)) { + position.setLatitude(Double.parseDouble(values[index++])); + } + + if (BitUtil.check(mask, 12)) { + position.setLongitude(Double.parseDouble(values[index++])); + } + + if (BitUtil.check(mask, 13)) { + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); + } + + if (BitUtil.check(mask, 14)) { + position.setCourse(Double.parseDouble(values[index++])); + } + + if (BitUtil.check(mask, 15)) { + position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + } + + if (BitUtil.check(mask, 16)) { + position.setValid(values[index++].equals("1")); + } + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String[] values = ((String) msg).split(";"); - String protocol = values[0].substring(0, 5); - - if (protocol.equals("ST910")) { + if (values[0].length() < 5) { + return decodeUniversal(channel, remoteAddress, values); + } else if (values[0].equals("ST910")) { return decode9(channel, remoteAddress, values); } else { - return decode23(channel, remoteAddress, protocol, values); + return decode235(channel, remoteAddress, values[0].substring(0, 5), values); } } diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java index dee7210b1..6b4ee6ebd 100644 --- a/src/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/org/traccar/protocol/T55ProtocolDecoder.java @@ -18,6 +18,7 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.socket.DatagramChannel; import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; @@ -98,8 +99,11 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { private Position decodeGprmc( DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) { - if (channel != null && !(channel instanceof DatagramChannel)) { - channel.write("OK1\r\n"); + if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) { + if (Context.getIdentityManager().lookupAttributeBoolean( + deviceSession.getDeviceId(), getProtocolName() + ".ack", false, true)) { + channel.write("OK1\r\n"); + } } Parser parser = new Parser(PATTERN_GPRMC, sentence); diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/org/traccar/protocol/TaipProtocolDecoder.java index 7702a89fb..e7117a5c9 100644 --- a/src/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/org/traccar/protocol/TaipProtocolDecoder.java @@ -18,6 +18,7 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; +import org.traccar.helper.BitUtil; import org.traccar.helper.Checksum; import org.traccar.helper.DateBuilder; import org.traccar.helper.DateUtil; @@ -67,7 +68,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .number("(x{8})") // odometer .number("[01]") // gps power .groupEnd("?") - .number("(d)") // fix mode .any() .compile(); @@ -104,15 +104,38 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(); position.setProtocol(getProtocolName()); + Integer event = null; + if (parser.hasNext(3)) { - position.set(Position.KEY_EVENT, parser.nextInt(0)); + event = parser.nextInt(); position.setTime(getTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0))); } else if (parser.hasNext()) { position.setTime(getTime(parser.nextInt(0))); } if (parser.hasNext()) { - position.set(Position.KEY_EVENT, parser.nextInt(0)); + event = parser.nextInt(); + } + + if (event != null) { + switch (event) { + case 22: + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 23: + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 24: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + case 26: + case 28: + position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + break; + default: + position.set(Position.KEY_EVENT, event); + break; + } } if (parser.hasNext(6)) { @@ -138,7 +161,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0)); } - position.setValid(parser.nextInt(0) != 0); + position.setValid(true); String[] attributes = null; beginIndex = sentence.indexOf(';'); @@ -150,6 +173,12 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { attributes = sentence.substring(beginIndex, endIndex).split(";"); } + return decodeAttributes(channel, remoteAddress, position, attributes); + } + + private Position decodeAttributes( + Channel channel, SocketAddress remoteAddress, Position position, String[] attributes) { + String uniqueId = null; DeviceSession deviceSession = null; String messageIndex = null; @@ -161,7 +190,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { String key = attribute.substring(0, index).toLowerCase(); String value = attribute.substring(index + 1); switch (key) { - case "id": uniqueId = value; deviceSession = getDeviceSession(channel, remoteAddress, value); @@ -169,23 +197,30 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); } break; - + case "io": + position.set(Position.KEY_IGNITION, BitUtil.check(value.charAt(0) - '0', 0)); + position.set(Position.KEY_CHARGE, BitUtil.check(value.charAt(0) - '0', 1)); + position.set(Position.KEY_OUTPUT, value.charAt(1) - '0'); + position.set(Position.KEY_INPUT, value.charAt(2) - '0'); + break; + case "ix": + position.set(Position.PREFIX_IO + 1, value); + break; + case "ad": + position.set(Position.PREFIX_ADC + 1, Integer.parseInt(value)); + break; case "sv": position.set(Position.KEY_SATELLITES, Integer.parseInt(value)); break; - case "bl": - position.set(Position.KEY_BATTERY, Integer.parseInt(value)); + position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.001); break; - case "vo": position.set(Position.KEY_ODOMETER, Long.parseLong(value)); break; - default: position.set(key, value); break; - } } else if (attribute.startsWith("#")) { messageIndex = attribute; diff --git a/src/org/traccar/protocol/TelicProtocolDecoder.java b/src/org/traccar/protocol/TelicProtocolDecoder.java index 62b756ab5..2c0d714c6 100644 --- a/src/org/traccar/protocol/TelicProtocolDecoder.java +++ b/src/org/traccar/protocol/TelicProtocolDecoder.java @@ -34,8 +34,8 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN = new PatternBuilder() .number("dddd") - .number("(d{6})") // device id - .number("(d+),") // type + .number("(d{6}|d{15})") // device id + .number("(d{1,2}),") // type .number("d{12},") // event time .number("d+,") .number("(dd)(dd)(dd)") // date (ddmmyy) diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java index 524e6d5b5..d0177da97 100644 --- a/src/org/traccar/protocol/TeltonikaProtocol.java +++ b/src/org/traccar/protocol/TeltonikaProtocol.java @@ -39,14 +39,14 @@ public class TeltonikaProtocol extends BaseProtocol { protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new TeltonikaFrameDecoder()); pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder()); - pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this)); + pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder()); - pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this)); + pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true)); } }); } diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java index 3f5b68f67..80f0045d5 100644 --- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -18,8 +18,8 @@ package org.traccar.protocol; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.socket.DatagramChannel; import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; @@ -35,8 +35,17 @@ import java.util.List; public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { - public TeltonikaProtocolDecoder(TeltonikaProtocol protocol) { + private boolean connectionless; + private boolean extended; + + public void setExtended(boolean extended) { + this.extended = extended; + } + + public TeltonikaProtocolDecoder(TeltonikaProtocol protocol, boolean connectionless) { super(protocol); + this.connectionless = connectionless; + this.extended = Context.getConfig().getBoolean(getProtocolName() + ".extended"); } private DeviceSession parseIdentification(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { @@ -67,7 +76,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_TYPE, buf.readUnsignedByte()); - position.set(Position.KEY_COMMAND, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII)); + position.set(Position.KEY_RESULT, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII)); } @@ -84,7 +93,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) { + private void decodeOtherParameter(Position position, int id, ChannelBuffer buf, int length) { switch (id) { case 1: case 2: @@ -95,15 +104,24 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 9: position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false)); break; + case 17: + position.set("axisX", readValue(buf, length, true)); + break; + case 18: + position.set("axisY", readValue(buf, length, true)); + break; + case 19: + position.set("axisZ", readValue(buf, length, true)); + break; + case 21: + position.set(Position.KEY_RSSI, readValue(buf, length, false)); + break; case 66: position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001); break; case 67: position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); break; - case 70: - position.set(Position.KEY_DEVICE_TEMP, readValue(buf, length, true) * 0.1); - break; case 72: position.set(Position.PREFIX_TEMP + 1, readValue(buf, length, true) * 0.1); break; @@ -114,17 +132,102 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1); break; case 78: - position.set(Position.KEY_RFID, readValue(buf, length, false)); + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", readValue(buf, length, false))); + break; + case 80: + position.set("workMode", readValue(buf, length, false)); + break; + case 179: + position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1); + break; + case 180: + position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1); + break; + case 181: + position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1); break; case 182: position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1); break; + case 239: + position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1); + break; + case 240: + position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1); + break; + case 241: + position.set(Position.KEY_OPERATOR, readValue(buf, length, false)); + break; default: position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); break; } } + private void decodeGh3000Parameter(Position position, int id, ChannelBuffer buf, int length) { + switch (id) { + case 1: + position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false)); + break; + case 2: + position.set("usbConnected", readValue(buf, length, false) == 1); + break; + case 5: + position.set("uptime", readValue(buf, length, false)); + break; + case 20: + position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1); + break; + case 21: + position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1); + break; + case 22: + position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1); + break; + case 67: + position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); + break; + case 221: + position.set("button", readValue(buf, length, false)); + break; + case 222: + if (readValue(buf, length, false) == 1) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + break; + case 240: + position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1); + break; + case 244: + position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1); + break; + default: + position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); + break; + } + } + + private void decodeParameter(Position position, int id, ChannelBuffer buf, int length, int codec) { + if (codec == CODEC_GH3000) { + decodeGh3000Parameter(position, id, buf, length); + } else { + decodeOtherParameter(position, id, buf, length); + } + } + + private void decodeNetwork(Position position) { + long cid = position.getLong(Position.PREFIX_IO + 205); + int lac = position.getInteger(Position.PREFIX_IO + 206); + if (cid != 0 && lac != 0) { + CellTower cellTower = CellTower.fromLacCid(lac, cid); + long operator = position.getInteger(Position.KEY_OPERATOR); + if (operator != 0) { + cellTower.setOperator(operator); + } + position.setNetwork(new Network(cellTower)); + } + } + private void decodeLocation(Position position, ChannelBuffer buf, int codec) { int globalMask = 0x0f; @@ -171,14 +274,19 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { cellTower.setSignalStrength((int) buf.readUnsignedByte()); } - position.setNetwork(new Network(cellTower)); + if (BitUtil.check(locationMask, 7)) { + cellTower.setOperator(buf.readUnsignedInt()); + } - } else if (BitUtil.check(locationMask, 6)) { - position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - } + position.setNetwork(new Network(cellTower)); - if (BitUtil.check(locationMask, 7)) { - position.set(Position.KEY_OPERATOR, buf.readUnsignedInt()); + } else { + if (BitUtil.check(locationMask, 6)) { + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + } + if (BitUtil.check(locationMask, 7)) { + position.set(Position.KEY_OPERATOR, buf.readUnsignedInt()); + } } } else { @@ -215,7 +323,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 1)) { int cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { - decodeParameter(position, buf.readUnsignedByte(), buf, 1); + decodeParameter(position, buf.readUnsignedByte(), buf, 1, codec); } } @@ -223,7 +331,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 2)) { int cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { - decodeParameter(position, buf.readUnsignedByte(), buf, 2); + decodeParameter(position, buf.readUnsignedByte(), buf, 2, codec); } } @@ -231,7 +339,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 3)) { int cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { - decodeParameter(position, buf.readUnsignedByte(), buf, 4); + decodeParameter(position, buf.readUnsignedByte(), buf, 4, codec); } } @@ -239,17 +347,27 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (codec == CODEC_FM4X00) { int cnt = buf.readUnsignedByte(); for (int j = 0; j < cnt; j++) { - decodeParameter(position, buf.readUnsignedByte(), buf, 8); + decodeOtherParameter(position, buf.readUnsignedByte(), buf, 8); } } + // Read 16 byte data + if (extended) { + int cnt = buf.readUnsignedByte(); + for (int j = 0; j < cnt; j++) { + position.set(Position.PREFIX_IO + buf.readUnsignedByte(), ChannelBuffers.hexDump(buf.readBytes(16))); + } + } + + decodeNetwork(position); + } private List<Position> parseData( Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int locationPacketId, String... imei) { List<Position> positions = new LinkedList<>(); - if (!(channel instanceof DatagramChannel)) { + if (!connectionless) { buf.readUnsignedInt(); // data length } @@ -278,7 +396,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } if (channel != null) { - if (channel instanceof DatagramChannel) { + if (connectionless) { ChannelBuffer response = ChannelBuffers.dynamicBuffer(); response.writeShort(5); response.writeShort(0); @@ -301,7 +419,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { ChannelBuffer buf = (ChannelBuffer) msg; - if (channel instanceof DatagramChannel) { + if (connectionless) { return decodeUdp(channel, remoteAddress, buf); } else { return decodeTcp(channel, remoteAddress, buf); diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java index 4c7da12e0..be3def453 100644 --- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java @@ -23,7 +23,6 @@ import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; -import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; @@ -33,8 +32,11 @@ import java.util.regex.Pattern; public class Tk103ProtocolDecoder extends BaseProtocolDecoder { + private boolean decodeLow; + public Tk103ProtocolDecoder(Tk103Protocol protocol) { super(protocol); + decodeLow = Context.getConfig().getBoolean(getProtocolName() + ".decodeLow"); } private static final Pattern PATTERN = new PatternBuilder() @@ -50,7 +52,14 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .number("(d+.d)(?:d*,)?") // speed .number("(dd)(dd)(dd),?") // time (hhmmss) .number("(d+.?d{1,2}),?") // course - .number("(?:([01]{8})|(x{8}))?,?") // state + .groupBegin() + .number("([01])") // charge + .number("([01])") // ignition + .number("(x)") // io + .number("(x)") // io + .number("(x)") // io + .number("(xxx),?") // fuel + .groupEnd("?") .number("(?:L(x+))?") // odometer .any() .number("([+-]ddd.d)?") // temperature @@ -200,17 +209,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { if (channel != null) { String id = sentence.substring(0, 12); String type = sentence.substring(12, 16); - if (type.equals("BP00") || type.equals("BP05")) { - String content = sentence.substring(16); - if (content.length() >= 15) { - getDeviceSession(channel, remoteAddress, content.substring(0, 15)); - } - if (type.equals("BP00")) { - channel.write("(" + id + "AP01HSO)"); - return null; - } else if (type.equals("BP05")) { - channel.write("(" + id + "AP05)"); - } + if (type.equals("BP00")) { + channel.write("(" + id + "AP01HSO)"); + return null; + } else if (type.equals("BP05")) { + channel.write("(" + id + "AP05)"); } } @@ -249,32 +252,44 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - switch (Context.getConfig().getString(getProtocolName() + ".speed", "kmh")) { - case "kn": - position.setSpeed(parser.nextDouble(0)); - break; - case "mph": - position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0))); - break; - default: - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - break; - } + position.setSpeed(convertSpeed(parser.nextDouble(0), "kmh")); dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); position.setTime(dateBuilder.getDate()); position.setCourse(parser.nextDouble(0)); - String status = parser.next(); - if (status != null) { - position.set(Position.KEY_STATUS, status); // binary status + if (parser.hasNext(6)) { + position.set(Position.KEY_CHARGE, parser.nextInt() == 0); + position.set(Position.KEY_IGNITION, parser.nextInt() == 1); + + int mask1 = parser.nextHexInt(); + position.set(Position.PREFIX_IN + 2, BitUtil.check(mask1, 0) ? 1 : 0); + position.set("panic", BitUtil.check(mask1, 1) ? 1 : 0); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(mask1, 2) ? 1 : 0); + if (decodeLow || BitUtil.check(mask1, 3)) { + position.set(Position.KEY_BLOCKED, BitUtil.check(mask1, 3) ? 1 : 0); + } + + int mask2 = parser.nextHexInt(); + for (int i = 0; i < 3; i++) { + if (decodeLow || BitUtil.check(mask2, i)) { + position.set("hs" + (3 - i), BitUtil.check(mask2, i) ? 1 : 0); + } + } + if (decodeLow || BitUtil.check(mask2, 3)) { + position.set(Position.KEY_DOOR, BitUtil.check(mask2, 3) ? 1 : 0); + } + + int mask3 = parser.nextHexInt(); + for (int i = 1; i <= 3; i++) { + if (decodeLow || BitUtil.check(mask3, i)) { + position.set("ls" + (3 - i + 1), BitUtil.check(mask3, i) ? 1 : 0); + } + } - int value = Integer.parseInt(new StringBuilder(status).reverse().toString(), 2); - position.set(Position.KEY_CHARGE, !BitUtil.check(value, 0)); - position.set(Position.KEY_IGNITION, BitUtil.check(value, 1)); + position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt()); } - position.set(Position.KEY_STATUS, parser.next()); // hex status if (parser.hasNext()) { position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0)); diff --git a/src/org/traccar/protocol/TlvProtocol.java b/src/org/traccar/protocol/TlvProtocol.java new file mode 100644 index 000000000..da8d88b8a --- /dev/null +++ b/src/org/traccar/protocol/TlvProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.traccar.BaseProtocol; +import org.traccar.CharacterDelimiterFrameDecoder; +import org.traccar.TrackerServer; + +import java.util.List; + +public class TlvProtocol extends BaseProtocol { + + public TlvProtocol() { + super("tlv"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder('\0')); + pipeline.addLast("objectDecoder", new TlvProtocolDecoder(TlvProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/TlvProtocolDecoder.java b/src/org/traccar/protocol/TlvProtocolDecoder.java new file mode 100644 index 000000000..41d65be09 --- /dev/null +++ b/src/org/traccar/protocol/TlvProtocolDecoder.java @@ -0,0 +1,109 @@ +/* + * Copyright 2017 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.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public class TlvProtocolDecoder extends BaseProtocolDecoder { + + public TlvProtocolDecoder(TlvProtocol protocol) { + super(protocol); + } + + private void sendResponse(Channel channel, String type, String... arguments) { + if (channel != null) { + ChannelBuffer response = ChannelBuffers.dynamicBuffer(); + response.writeBytes(ChannelBuffers.copiedBuffer(type, StandardCharsets.US_ASCII)); + for (String argument : arguments) { + response.writeByte(argument.length()); + response.writeBytes(ChannelBuffers.copiedBuffer(argument, StandardCharsets.US_ASCII)); + } + response.writeByte(0); + channel.write(response); + } + } + + private String readArgument(ChannelBuffer buf) { + return buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + String type = buf.readBytes(2).toString(StandardCharsets.US_ASCII); + + if (channel != null) { + switch (type) { + case "0A": + case "0C": + sendResponse(channel, type); + break; + case "0B": + sendResponse(channel, type, "1482202689", "10", "20", "15"); + break; + case "0E": + case "0F": + sendResponse(channel, type, "30", "Unknown"); + break; + default: + break; + } + } + + if (type.equals("0E")) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, readArgument(buf)); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(new Date(Long.parseLong(readArgument(buf)) * 1000)); + + readArgument(buf); // location identifier + + position.setLongitude(Double.parseDouble(readArgument(buf))); + position.setLatitude(Double.parseDouble(readArgument(buf))); + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(readArgument(buf)))); + position.setCourse(Double.parseDouble(readArgument(buf))); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(readArgument(buf))); + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java index c10523117..b8458dd52 100644 --- a/src/org/traccar/protocol/TmgProtocolDecoder.java +++ b/src/org/traccar/protocol/TmgProtocolDecoder.java @@ -144,7 +144,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0)); position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0)); position.set(Position.KEY_VERSION_FW, parser.next()); - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); return position; } diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java index a4ae4cb8f..3c2dee8ec 100644 --- a/src/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/org/traccar/protocol/TotemProtocolDecoder.java @@ -171,6 +171,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_LOW_BATTERY; case 0x11: return Position.ALARM_OVERSPEED; + case 0x30: + return Position.ALARM_PARKING; case 0x42: return Position.ALARM_GEOFENCE_EXIT; case 0x43: diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/org/traccar/protocol/TramigoProtocolDecoder.java index b39e3eab1..b1e28e17d 100644 --- a/src/org/traccar/protocol/TramigoProtocolDecoder.java +++ b/src/org/traccar/protocol/TramigoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2017 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. @@ -130,12 +130,13 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2)))); } - pattern = Pattern.compile("(\\d{1,2}:\\d{2} \\w{3} \\d{1,2})"); + pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})"); matcher = pattern.matcher(sentence); if (!matcher.find()) { return null; } - DateFormat dateFormat = new SimpleDateFormat("HH:mm MMM d yyyy", Locale.ENGLISH); + DateFormat dateFormat = new SimpleDateFormat( + matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH); position.setTime(DateUtil.correctYear( dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java index 88ac76134..918748f7b 100644 --- a/src/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/org/traccar/protocol/TrvProtocolDecoder.java @@ -53,8 +53,8 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .number("(ddd)") // satellites .number("(ddd)") // battery .number("(d)") // acc - .number("dd") // arm status - .number("dd,") // working mode + .number("(dd)") // arm status + .number("(dd),") // working mode .number("(d+),") // mcc .number("(d+),") // mnc .number("(d+),") // lac @@ -71,9 +71,41 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .number("(d)") // acc .number("(dd)") // arm status .number("(dd)") // working mode + .groupBegin() + .number("(ddd)") // interval + .number("d") // vibration alarm + .number("ddd") // vibration sensitivity + .number("d") // automatic arm + .number("dddd") // automatic arm time + .number("(d)") // blocked + .number("(d)") // power status + .number("(d)") // movement status + .groupEnd("?") .any() .compile(); + private Boolean decodeOptionalValue(Parser parser, int activeValue) { + int value = parser.nextInt(); + if (value != 0) { + return value == activeValue; + } + return null; + } + + private void decodeCommon(Position position, Parser parser) { + + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_BATTERY, parser.nextInt()); + position.set(Position.KEY_IGNITION, decodeOptionalValue(parser, 1)); + position.set(Position.KEY_ARMED, decodeOptionalValue(parser, 1)); + + int mode = parser.nextInt(); + if (mode != 0) { + position.set("mode", mode); + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -84,11 +116,14 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { String type = sentence.substring(id.length(), id.length() + 4); if (channel != null) { + String responseHeader = id + (char) (type.charAt(0) + 1) + type.substring(1); if (type.equals("AP00") && id.equals("IW")) { String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); - channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "," + time + ",0#"); + channel.write(responseHeader + "," + time + ",0#"); + } else if (type.equals("AP14")) { + channel.write(responseHeader + ",0.000,0.000#"); } else { - channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "#"); + channel.write(responseHeader + "#"); } } @@ -115,13 +150,13 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); - position.set(Position.KEY_RSSI, parser.nextInt(0)); - position.set(Position.KEY_SATELLITES, parser.nextInt(0)); - position.set(Position.KEY_BATTERY, parser.nextInt(0)); - position.set(Position.KEY_IGNITION, parser.nextInt(0) != 0); + decodeCommon(position, parser); - position.set("arm", parser.nextInt(0)); - position.set("mode", parser.nextInt(0)); + if (parser.hasNext(3)) { + position.set(Position.KEY_BLOCKED, decodeOptionalValue(parser, 2)); + position.set(Position.KEY_CHARGE, decodeOptionalValue(parser, 1)); + position.set(Position.KEY_MOTION, decodeOptionalValue(parser, 1)); + } return position; @@ -137,31 +172,25 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); DateBuilder dateBuilder = new DateBuilder() - .setDate(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setValid(parser.next().equals("A")); position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); - dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); - position.setCourse(parser.nextDouble(0)); + position.setCourse(parser.nextDouble()); - int rssi = parser.nextInt(0); - position.set(Position.KEY_SATELLITES, parser.nextInt(0)); - position.set(Position.KEY_BATTERY, parser.nextInt(0)); - - int acc = parser.nextInt(0); - if (acc != 0) { - position.set(Position.KEY_IGNITION, acc == 1); - } + decodeCommon(position, parser); position.setNetwork(new Network(CellTower.from( - parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), rssi))); + parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()))); return position; + } return null; diff --git a/src/org/traccar/protocol/TytanProtocolDecoder.java b/src/org/traccar/protocol/TytanProtocolDecoder.java index 030fbce78..0ae669784 100644 --- a/src/org/traccar/protocol/TytanProtocolDecoder.java +++ b/src/org/traccar/protocol/TytanProtocolDecoder.java @@ -111,7 +111,7 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 5); break; case 150: - position.set("door", buf.readUnsignedByte()); + position.set(Position.KEY_DOOR, buf.readUnsignedByte()); break; default: buf.skipBytes(length); diff --git a/src/org/traccar/protocol/TzoneProtocolDecoder.java b/src/org/traccar/protocol/TzoneProtocolDecoder.java index 69aa916df..079ad3126 100644 --- a/src/org/traccar/protocol/TzoneProtocolDecoder.java +++ b/src/org/traccar/protocol/TzoneProtocolDecoder.java @@ -43,7 +43,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { case 0x11: return Position.ALARM_OVERSPEED; case 0x14: - return Position.ALARM_BREAKING; + return Position.ALARM_BRAKING; case 0x15: return Position.ALARM_ACCELERATION; case 0x30: @@ -203,7 +203,16 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { if (blockLength >= 13) { position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); position.set("terminalInfo", buf.readUnsignedByte()); - position.set(Position.PREFIX_IO + 1, buf.readUnsignedShort()); + + int status = buf.readUnsignedByte(); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 0)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 1)); + status = buf.readUnsignedByte(); + position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 4)); + if (BitUtil.check(status, 0)) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); position.set("gsmStatus", buf.readUnsignedByte()); position.set(Position.KEY_BATTERY, buf.readUnsignedShort()); diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java index 1b22eeb75..31a3d2cfe 100644 --- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java +++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java @@ -308,7 +308,8 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { break; case DATA_RFID: - position.set(Position.KEY_RFID, buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII)); + position.set(Position.KEY_DRIVER_UNIQUE_ID, + buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII)); position.set("authorized", buf.readUnsignedByte() != 0); break; diff --git a/src/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/org/traccar/protocol/VisiontekProtocolDecoder.java index 636a3d640..f32c9fbfe 100644 --- a/src/org/traccar/protocol/VisiontekProtocolDecoder.java +++ b/src/org/traccar/protocol/VisiontekProtocolDecoder.java @@ -130,7 +130,7 @@ public class VisiontekProtocolDecoder extends BaseProtocolDecoder { position.setValid(parser.next().equals("A")); - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); return position; } diff --git a/src/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/org/traccar/protocol/Vt200ProtocolDecoder.java index b9af11b43..2ae24efbb 100644 --- a/src/org/traccar/protocol/Vt200ProtocolDecoder.java +++ b/src/org/traccar/protocol/Vt200ProtocolDecoder.java @@ -27,6 +27,8 @@ import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; +import java.util.Arrays; +import java.util.Date; public class Vt200ProtocolDecoder extends BaseProtocolDecoder { @@ -40,6 +42,13 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { return degrees + minutes * 0.0001 / 60; } + protected Date decodeDate(ChannelBuffer buf) { + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2)) + .setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2)); + return dateBuilder.getDate(); + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -57,7 +66,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedShort(); buf.readUnsignedShort(); // length - if (type == 0x2084) { + if (type == 0x2086 || type == 0x2084 || type == 0x2082) { Position position = new Position(); position.setProtocol(getProtocolName()); @@ -66,12 +75,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // data type buf.readUnsignedShort(); // trip id - DateBuilder dateBuilder = new DateBuilder(); - dateBuilder.setDateReverse( - BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2)); - dateBuilder.setTime( - BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2)); - position.setTime(dateBuilder.getDate()); + position.setTime(decodeDate(buf)); position.setLatitude(decodeCoordinate(BcdUtil.readInteger(buf, 8))); position.setLongitude(decodeCoordinate(BcdUtil.readInteger(buf, 9))); @@ -97,6 +101,48 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == 0x3088) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + buf.readUnsignedShort(); // trip id + buf.skipBytes(8); // imei + buf.skipBytes(8); // imsi + + position.set("tripStart", decodeDate(buf).getTime()); + position.set("tripEnd", decodeDate(buf).getTime()); + position.set("drivingTime", buf.readUnsignedShort()); + + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); + position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt()); + + position.set("maxSpeed", UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.set("maxRpm", buf.readUnsignedShort()); + position.set("maxTemp", buf.readUnsignedByte() - 40); + position.set("hardAccelerationCount", buf.readUnsignedByte()); + position.set("hardBrakingCount", buf.readUnsignedByte()); + + for (String speedType : Arrays.asList("over", "high", "normal", "low")) { + position.set(speedType + "SpeedTime", buf.readUnsignedShort()); + position.set(speedType + "SpeedDistance", buf.readUnsignedInt()); + position.set(speedType + "SpeedFuel", buf.readUnsignedInt()); + } + + position.set("idleTime", buf.readUnsignedShort()); + position.set("idleFuel", buf.readUnsignedInt()); + + position.set("hardCorneringCount", buf.readUnsignedByte()); + position.set("overspeedCount", buf.readUnsignedByte()); + position.set("overheatCount", buf.readUnsignedShort()); + position.set("laneChangeCount", buf.readUnsignedByte()); + position.set("emergencyRefueling", buf.readUnsignedByte()); + + return position; + } return null; diff --git a/src/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/org/traccar/protocol/VtfmsProtocolDecoder.java index 852b9a749..5fb687e6d 100644 --- a/src/org/traccar/protocol/VtfmsProtocolDecoder.java +++ b/src/org/traccar/protocol/VtfmsProtocolDecoder.java @@ -84,7 +84,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder { case 15: return Position.ALARM_POWER_RESTORED; case 32: - return Position.ALARM_BREAKING; + return Position.ALARM_BRAKING; case 33: return Position.ALARM_ACCELERATION; default: diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/org/traccar/protocol/WatchProtocol.java index 42a640b85..2be2dc9ae 100644 --- a/src/org/traccar/protocol/WatchProtocol.java +++ b/src/org/traccar/protocol/WatchProtocol.java @@ -29,6 +29,7 @@ public class WatchProtocol extends BaseProtocol { public WatchProtocol() { super("watch"); setSupportedDataCommands( + Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, Command.TYPE_SOS_NUMBER, diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java index 57f5d7e78..86dc9456d 100644 --- a/src/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/org/traccar/protocol/WatchProtocolDecoder.java @@ -76,8 +76,6 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_GEOFENCE_ENTER; } else if (BitUtil.check(status, 3)) { return Position.ALARM_OVERSPEED; - } else if (BitUtil.check(status, 4)) { - return Position.ALARM_MOVEMENT; } else if (BitUtil.check(status, 16)) { return Position.ALARM_SOS; } else if (BitUtil.check(status, 17)) { @@ -207,9 +205,13 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextInt(0)); position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); - position.set("steps", parser.nextInt(0)); + position.set(Position.KEY_STEPS, parser.nextInt(0)); - position.set(Position.KEY_ALARM, decodeAlarm(parser.nextHexInt(0))); + int status = parser.nextHexInt(0); + position.set(Position.KEY_ALARM, decodeAlarm(status)); + if (BitUtil.check(status, 4)) { + position.set(Position.KEY_MOTION, true); + } decodeTail(position, parser.next()); diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java index c5d8fad86..d2d3b52d1 100644 --- a/src/org/traccar/protocol/WatchProtocolEncoder.java +++ b/src/org/traccar/protocol/WatchProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 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. @@ -26,13 +26,14 @@ import java.text.DecimalFormatSymbols; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.TimeZone; public class WatchProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter { @Override public String formatValue(String key, Object value) { if (key.equals(Command.KEY_TIMEZONE)) { - double offset = ((Number) value).longValue() / 3600.0; + double offset = TimeZone.getTimeZone((String) value).getRawOffset() / 3600000.0; DecimalFormat fmt = new DecimalFormat("+#.##;-#.##", DecimalFormatSymbols.getInstance(Locale.US)); return fmt.format(offset); } @@ -41,8 +42,9 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin } + @Override protected String formatCommand(Command command, String format, String... keys) { - String content = super.formatCommand(command, format, this, keys); + String content = formatCommand(command, format, this, keys); return String.format("[CS*%s*%04x*%s]", getUniqueId(command.getDeviceId()), content.length(), content); } @@ -97,6 +99,8 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin protected Object encodeCommand(Command command) { switch (command.getType()) { + case Command.TYPE_CUSTOM: + return formatCommand(command, command.getString(Command.KEY_DATA)); case Command.TYPE_POSITION_SINGLE: return formatCommand(command, "RG"); case Command.TYPE_SOS_NUMBER: diff --git a/src/org/traccar/protocol/WialonProtocolDecoder.java b/src/org/traccar/protocol/WialonProtocolDecoder.java index 82098413b..4eb3b9b8e 100644 --- a/src/org/traccar/protocol/WialonProtocolDecoder.java +++ b/src/org/traccar/protocol/WialonProtocolDecoder.java @@ -109,7 +109,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { } } - position.set(Position.KEY_RFID, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); if (parser.hasNext()) { String[] values = parser.next().split(","); diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/org/traccar/protocol/XexunProtocolDecoder.java index 20804bbb4..bb4b4f48c 100644 --- a/src/org/traccar/protocol/XexunProtocolDecoder.java +++ b/src/org/traccar/protocol/XexunProtocolDecoder.java @@ -17,13 +17,11 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; -import org.traccar.helper.UnitsConverter; import java.net.SocketAddress; import java.util.regex.Pattern; @@ -70,9 +68,11 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder { if (value != null) { switch (value.toLowerCase()) { case "acc on": + case "accstart": position.set(Position.KEY_IGNITION, true); break; case "acc off": + case "accstop": position.set(Position.KEY_IGNITION, false); break; case "help me!": @@ -121,14 +121,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - switch (Context.getConfig().getString(getProtocolName() + ".speed", "kn")) { - case "kmh": - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - break; - default: - position.setSpeed(parser.nextDouble(0)); - break; - } + position.setSpeed(convertSpeed(parser.nextDouble(0), "kn")); position.setCourse(parser.nextDouble(0)); diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java index a42c6175f..15e8558be 100644 --- a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -25,6 +25,7 @@ import org.traccar.model.Position; import javax.xml.bind.DatatypeConverter; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -77,8 +78,6 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { for (int i : l4) { TAG_LENGTH_MAP.put(i, 4); } - TAG_LENGTH_MAP.put(0x65, 17); - TAG_LENGTH_MAP.put(0x73, 16); TAG_LENGTH_MAP.put(0x95, 24); } @@ -163,12 +162,24 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { case 0x13: position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); break; + case 0x14: + position.set(Position.KEY_RSSI, buf.readShort()); + break; case 0x16: position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1); break; case 0x17: position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1); break; + case 0x57: + position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort())); + break; + case 0x65: + position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII)); + break; + case 0x73: + position.set(Position.KEY_VERSION_FW, buf.readBytes(16).toString(StandardCharsets.US_ASCII).trim()); + break; default: buf.skipBytes(getTagLength(tag)); break; |