diff options
author | Parveen Kumar Yadav <parveenkumardeeva@gmail.com> | 2018-04-13 10:47:42 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-13 10:47:42 +0530 |
commit | d448823edd0e8a7833c79b980cfe9778b849843a (patch) | |
tree | 50ad1ab51b93915cff422a1834baa5a2c579fce8 /src/org/traccar/protocol | |
parent | 2c8392a0e16044905d983da56ab0919eadbb3858 (diff) | |
parent | 617393cf9f052298f7fb35f0c58138c87a6dd5c3 (diff) | |
download | trackermap-server-d448823edd0e8a7833c79b980cfe9778b849843a.tar.gz trackermap-server-d448823edd0e8a7833c79b980cfe9778b849843a.tar.bz2 trackermap-server-d448823edd0e8a7833c79b980cfe9778b849843a.zip |
Merge pull request #1 from traccar/master
Bringing Fork Up to Date
Diffstat (limited to 'src/org/traccar/protocol')
53 files changed, 2550 insertions, 653 deletions
diff --git a/src/org/traccar/protocol/AquilaProtocolDecoder.java b/src/org/traccar/protocol/AquilaProtocolDecoder.java index d8081612d..960139b3f 100644 --- a/src/org/traccar/protocol/AquilaProtocolDecoder.java +++ b/src/org/traccar/protocol/AquilaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 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,8 @@ import org.traccar.DeviceSession; 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 java.net.SocketAddress; @@ -32,7 +34,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = new PatternBuilder() + private static final Pattern PATTERN_A = new PatternBuilder() .text("$$") .expression("[^,]*,") // client .number("(d+),") // device serial number @@ -129,11 +131,9 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder { .number("xx") // checksum .compile(); - @Override - protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + private Position decodeA(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN, (String) msg); + Parser parser = new Parser(PATTERN_A, sentence); if (!parser.matches()) { return null; } @@ -215,4 +215,119 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_B = new PatternBuilder() + .text("$Header,") + .expression("[^,]+,") // client + .expression("[^,]+,") // firmware version + .expression(".{2},") // type + .number("d+,") // message id + .expression("[LH],") // status + .number("(d+),") // imei + .expression("[^,]+,") // registration number + .number("([01]),") // validity + .number("(dd)(dd)(dddd),") // date (ddmmyyyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+),") // latitude + .expression("([NS]),") + .number("(-?d+.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+),") // speed + .number("(d+),") // course + .number("(d+),") // satellites + .number("(-?d+.d+),") // altitude + .number("(d+.d+),") // pdop + .number("(d+.d+),") // hdop + .expression("[^,]+,") // operator + .number("([01]),") // ignition + .number("([01]),") // charge + .number("(d+.d+),") // power + .number("(d+.d+),") // battery + .number("[01],") // emergency + .expression("[CO],") // tamper + .number("(d+),") // rssi + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+),") // cid + .number("(d+),(x+),(x+),") // cell 1 + .number("(d+),(x+),(x+),") // cell 2 + .number("(d+),(x+),(x+),") // cell 3 + .number("(d+),(x+),(x+),") // cell 4 + .number("([01])+,") // inputs + .number("([01])+,") // outputs + .number("d+,") // frame number + .number("(d+.d+),") // adc1 + .number("(d+.d+),") // adc2 + .number("d+,") // delta distance + .any() + .compile(); + + private Position decodeB(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_B, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(parser.nextInt() == 1); + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextInt()); + + position.set(Position.KEY_SATELLITES, parser.nextInt()); + + position.setAltitude(parser.nextDouble()); + + position.set(Position.KEY_PDOP, parser.nextDouble()); + position.set(Position.KEY_HDOP, parser.nextDouble()); + position.set(Position.KEY_IGNITION, parser.nextInt() == 1); + position.set(Position.KEY_CHARGE, parser.nextInt() == 1); + position.set(Position.KEY_POWER, parser.nextDouble()); + position.set(Position.KEY_BATTERY, parser.nextDouble()); + + Network network = new Network(); + + int rssi = parser.nextInt(); + int mcc = parser.nextInt(); + int mnc = parser.nextInt(); + + network.addCellTower(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt(), rssi)); + for (int i = 0; i < 4; i++) { + rssi = parser.nextInt(); + network.addCellTower(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt(), rssi)); + } + + position.setNetwork(network); + + position.set(Position.KEY_INPUT, parser.nextBinInt()); + position.set(Position.KEY_OUTPUT, parser.nextBinInt()); + position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); + position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + if (sentence.startsWith("$$")) { + return decodeA(channel, remoteAddress, sentence); + } else { + return decodeB(channel, remoteAddress, sentence); + } + } + } diff --git a/src/org/traccar/protocol/At2000ProtocolDecoder.java b/src/org/traccar/protocol/At2000ProtocolDecoder.java index 06d80c497..e0f2b4278 100644 --- a/src/org/traccar/protocol/At2000ProtocolDecoder.java +++ b/src/org/traccar/protocol/At2000ProtocolDecoder.java @@ -20,13 +20,13 @@ import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import javax.xml.bind.DatatypeConverter; import java.net.SocketAddress; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; @@ -84,7 +84,7 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder { IvParameterSpec ivSpec = new IvParameterSpec(iv); SecretKeySpec keySpec = new SecretKeySpec( - DatatypeConverter.parseHexBinary("000102030405060708090a0b0c0d0e0f"), "AES"); + DataConverter.parseHex("000102030405060708090a0b0c0d0e0f"), "AES"); cipher = Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java index 236b608d6..8138f0fcb 100644 --- a/src/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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. @@ -36,6 +36,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class AtrackProtocolDecoder extends BaseProtocolDecoder { @@ -43,6 +44,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { private static final int MIN_DATA_LENGTH = 40; private boolean longDate; + private boolean decimalFuel; private boolean custom; private String form; @@ -52,6 +54,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { super(protocol); longDate = Context.getConfig().getBoolean(getProtocolName() + ".longDate"); + decimalFuel = Context.getConfig().getBoolean(getProtocolName() + ".decimalFuel"); custom = Context.getConfig().getBoolean(getProtocolName() + ".custom"); form = Context.getConfig().getString(getProtocolName() + ".form"); @@ -330,7 +333,17 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1); position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1); - position.set("message", readString(buf)); + String message = readString(buf); + if (message != null && !message.isEmpty()) { + Pattern pattern = Pattern.compile("FULS:F=(\\p{XDigit}+) t=(\\p{XDigit}+) N=(\\p{XDigit}+)"); + Matcher matcher = pattern.matcher(message); + if (matcher.find()) { + int value = Integer.parseInt(matcher.group(3), decimalFuel ? 10 : 16); + position.set(Position.KEY_FUEL_LEVEL, value * 0.1); + } else { + position.set("message", message); + } + } if (custom) { String form = this.form; diff --git a/src/org/traccar/protocol/BceProtocolDecoder.java b/src/org/traccar/protocol/BceProtocolDecoder.java index 22d6a5aa8..a023e60c5 100644 --- a/src/org/traccar/protocol/BceProtocolDecoder.java +++ b/src/org/traccar/protocol/BceProtocolDecoder.java @@ -21,6 +21,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.helper.BitUtil; +import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; @@ -93,7 +94,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder { position.setValid(true); position.setLongitude(buf.readFloat()); position.setLatitude(buf.readFloat()); - position.setSpeed(buf.readUnsignedByte()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); int gps = buf.readUnsignedByte(); position.set(Position.KEY_SATELLITES, gps & 0xf); diff --git a/src/org/traccar/protocol/CastelProtocol.java b/src/org/traccar/protocol/CastelProtocol.java index db9df0674..d5ba5cd55 100644 --- a/src/org/traccar/protocol/CastelProtocol.java +++ b/src/org/traccar/protocol/CastelProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 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.ChannelPipeline; import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.TrackerServer; +import org.traccar.model.Command; import java.nio.ByteOrder; import java.util.List; @@ -29,6 +30,9 @@ public class CastelProtocol extends BaseProtocol { public CastelProtocol() { super("castel"); + setSupportedDataCommands( + Command.TYPE_ENGINE_STOP, + Command.TYPE_ENGINE_RESUME); } @Override @@ -37,6 +41,7 @@ public class CastelProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); + pipeline.addLast("objectEncoder", new CastelProtocolEncoder()); pipeline.addLast("objectDecoder", new CastelProtocolDecoder(CastelProtocol.this)); } }; @@ -46,6 +51,7 @@ public class CastelProtocol extends BaseProtocol { server = new TrackerServer(new ConnectionlessBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("objectEncoder", new CastelProtocolEncoder()); pipeline.addLast("objectDecoder", new CastelProtocolDecoder(CastelProtocol.this)); } }; diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java index ebd3d202c..44a5e213c 100644 --- a/src/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/org/traccar/protocol/CastelProtocolDecoder.java @@ -96,6 +96,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { public static final short MSG_CC_LOGIN = 0x4001; public static final short MSG_CC_LOGIN_RESPONSE = (short) 0x8001; public static final short MSG_CC_HEARTBEAT = 0x4206; + public static final short MSG_CC_PETROL_CONTROL = 0x4583; public static final short MSG_CC_HEARTBEAT_RESPONSE = (short) 0x8206; private Position readPosition(DeviceSession deviceSession, ChannelBuffer buf) { diff --git a/src/org/traccar/protocol/CastelProtocolEncoder.java b/src/org/traccar/protocol/CastelProtocolEncoder.java new file mode 100644 index 000000000..806dac19e --- /dev/null +++ b/src/org/traccar/protocol/CastelProtocolEncoder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2018 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.traccar.BaseProtocolEncoder; +import org.traccar.Context; +import org.traccar.helper.Checksum; +import org.traccar.helper.Log; +import org.traccar.model.Command; + +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; + +public class CastelProtocolEncoder extends BaseProtocolEncoder { + + private ChannelBuffer encodeContent(long deviceId, short type, ChannelBuffer content) { + ChannelBuffer buf = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + String uniqueId = Context.getIdentityManager().getById(deviceId).getUniqueId(); + + buf.writeByte('@'); + buf.writeByte('@'); + + buf.writeShort(2 + 2 + 1 + 20 + 2 + content.readableBytes() + 2 + 2); // length + + buf.writeByte(1); // protocol version + + buf.writeBytes(uniqueId.getBytes(StandardCharsets.US_ASCII)); + buf.writeZero(20 - uniqueId.length()); + + buf.writeShort(ChannelBuffers.swapShort(type)); + buf.writeBytes(content); + + buf.writeShort(Checksum.crc16(Checksum.CRC16_X25, buf.toByteBuffer())); + + buf.writeByte('\r'); + buf.writeByte('\n'); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + ChannelBuffer content = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + switch (command.getType()) { + case Command.TYPE_ENGINE_STOP: + content.writeByte(1); + return encodeContent(command.getDeviceId(), CastelProtocolDecoder.MSG_CC_PETROL_CONTROL, content); + case Command.TYPE_ENGINE_RESUME: + content.writeByte(0); + return encodeContent(command.getDeviceId(), CastelProtocolDecoder.MSG_CC_PETROL_CONTROL, content); + default: + Log.warning(new UnsupportedOperationException(command.getType())); + break; + } + return null; + } + +} diff --git a/src/org/traccar/protocol/CautelaProtocol.java b/src/org/traccar/protocol/CautelaProtocol.java new file mode 100644 index 000000000..89ab7a1d0 --- /dev/null +++ b/src/org/traccar/protocol/CautelaProtocol.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 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.jboss.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class CautelaProtocol extends BaseProtocol { + + public CautelaProtocol() { + super("cautela"); + } + + @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("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new CautelaProtocolDecoder(CautelaProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/CautelaProtocolDecoder.java b/src/org/traccar/protocol/CautelaProtocolDecoder.java new file mode 100644 index 000000000..d7bf4fb51 --- /dev/null +++ b/src/org/traccar/protocol/CautelaProtocolDecoder.java @@ -0,0 +1,77 @@ +/* + * Copyright 2018 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.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class CautelaProtocolDecoder extends BaseProtocolDecoder { + + public CautelaProtocolDecoder(CautelaProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .number("(d+),") // type + .number("(d+),") // imei + .number("(dd),(dd),(dd),") // date (ddmmyy) + .number("(-?d+.d+),") // latitude + .number("(-?d+.d+),") // longitude + .number("(dd)(dd),") // time (hhmm) + .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; + } + + String type = parser.next(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder(); + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setValid(true); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + + dateBuilder.setHour(parser.nextInt()).setMinute(parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + return position; + } + +} diff --git a/src/org/traccar/protocol/ContinentalProtocol.java b/src/org/traccar/protocol/ContinentalProtocol.java new file mode 100644 index 000000000..e2b1226cf --- /dev/null +++ b/src/org/traccar/protocol/ContinentalProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class ContinentalProtocol extends BaseProtocol { + + public ContinentalProtocol() { + super("continental"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2)); + pipeline.addLast("objectDecoder", new ContinentalProtocolDecoder(ContinentalProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/ContinentalProtocolDecoder.java b/src/org/traccar/protocol/ContinentalProtocolDecoder.java new file mode 100644 index 000000000..726d9e16b --- /dev/null +++ b/src/org/traccar/protocol/ContinentalProtocolDecoder.java @@ -0,0 +1,113 @@ +/* + * Copyright 2018 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.util.Date; + +public class ContinentalProtocolDecoder extends BaseProtocolDecoder { + + public ContinentalProtocolDecoder(ContinentalProtocol protocol) { + super(protocol); + } + + public static final int MSG_KEEPALIVE = 0x00; + public static final int MSG_STATUS = 0x02; + public static final int MSG_ACK = 0x06; + public static final int MSG_NACK = 0x15; + + private void sendResponse(Channel channel, long serialNumber) { + if (channel != null) { + ChannelBuffer response = ChannelBuffers.dynamicBuffer(); + + response.writeByte('S'); + response.writeByte('V'); + response.writeShort(2 + 2 + 1 + 4 + 2); // length + response.writeByte(1); // version + response.writeInt((int) serialNumber); + response.writeByte(0); // product + response.writeByte(MSG_ACK); + + channel.write(response); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.skipBytes(2); // header + buf.readUnsignedShort(); // length + buf.readUnsignedByte(); // software version + + long serialNumber = buf.readUnsignedInt(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(serialNumber)); + if (deviceSession == null) { + return null; + } + + sendResponse(channel, serialNumber); + + buf.readUnsignedByte(); // product + + int type = buf.readUnsignedByte(); + + if (type == MSG_STATUS) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setFixTime(new Date(buf.readUnsignedInt() * 1000L)); + + buf.readUnsignedByte(); + position.setLatitude(buf.readMedium() / 3600.0); + + buf.readUnsignedByte(); + position.setLongitude(buf.readMedium() / 3600.0); + + position.setCourse(buf.readUnsignedShort()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); + + position.setValid(buf.readUnsignedByte() > 0); + + position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000L)); + + position.set(Position.KEY_EVENT, buf.readUnsignedShort()); + position.set(Position.KEY_INPUT, buf.readUnsignedShort()); + position.set(Position.KEY_OUTPUT, buf.readUnsignedShort()); + position.set(Position.KEY_BATTERY, buf.readUnsignedByte()); + position.set(Position.KEY_DEVICE_TEMP, buf.readByte()); + + buf.readUnsignedShort(); // reserved + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/DmtProtocolDecoder.java b/src/org/traccar/protocol/DmtProtocolDecoder.java index 74db5a6f7..30a6ae13a 100644 --- a/src/org/traccar/protocol/DmtProtocolDecoder.java +++ b/src/org/traccar/protocol/DmtProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2018 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.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -42,6 +43,7 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_DATA_RECORD = 0x04; public static final int MSG_COMMIT = 0x05; public static final int MSG_COMMIT_RESPONSE = 0x06; + public static final int MSG_DATA_RECORD_64 = 0x10; public static final int MSG_CANNED_REQUEST_1 = 0x14; public static final int MSG_CANNED_RESPONSE_1 = 0x15; @@ -61,142 +63,221 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder { } } - @Override - protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + private List<Position> decodeFixed64(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { - ChannelBuffer buf = (ChannelBuffer) msg; + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } - buf.skipBytes(2); // header + List<Position> positions = new LinkedList<>(); - int type = buf.readUnsignedByte(); + while (buf.readableBytes() >= 64) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); - buf.readUnsignedShort(); // length + buf.readByte(); // type - if (type == MSG_HELLO) { + position.set(Position.KEY_INDEX, buf.readUnsignedInt()); - buf.readUnsignedInt(); // device serial number + long time = buf.readUnsignedInt(); + position.setTime(new DateBuilder() + .setYear((int) (2000 + (time & 0x3F))) + .setMonth((int) (time >> 6) & 0xF) + .setDay((int) (time >> 10) & 0x1F) + .setHour((int) (time >> 15) & 0x1F) + .setMinute((int) (time >> 20) & 0x3F) + .setSecond((int) (time >> 26) & 0x3F) + .getDate()); - DeviceSession deviceSession = getDeviceSession( - channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII)); + position.setLongitude(buf.readInt() * 0.0000001); + position.setLatitude(buf.readInt() * 0.0000001); + position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShort())); + position.setCourse(buf.readUnsignedByte() * 2); + position.setAltitude(buf.readShort()); - ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); - response.writeInt((int) ((System.currentTimeMillis() - 1356998400000L) / 1000)); - response.writeInt(deviceSession != null ? 0 : 1); // flags - sendResponse(channel, MSG_HELLO_RESPONSE, response); + buf.readUnsignedShort(); // position accuracy + buf.readUnsignedByte(); // speed accuracy - } else if (type == MSG_COMMIT) { + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); - ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); - response.writeByte(1); // flags (success) - sendResponse(channel, MSG_COMMIT_RESPONSE, response); + position.setValid(BitUtil.check(buf.readByte(), 0)); - } else if (type == MSG_CANNED_REQUEST_1) { + position.set(Position.KEY_INPUT, buf.readUnsignedInt()); + position.set(Position.KEY_OUTPUT, buf.readUnsignedShort()); - ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); - response.writeBytes(new byte[12]); - sendResponse(channel, MSG_CANNED_RESPONSE_1, response); + for (int i = 1; i <= 5; i++) { + position.set(Position.PREFIX_ADC + i, buf.readShort()); + } - } else if (type == MSG_CANNED_REQUEST_2) { + position.set(Position.KEY_DEVICE_TEMP, buf.readByte()); - sendResponse(channel, MSG_CANNED_RESPONSE_2, null); + buf.readShort(); // accelerometer x + buf.readShort(); // accelerometer y + buf.readShort(); // accelerometer z - } else if (type == MSG_DATA_RECORD) { + buf.skipBytes(8); // device id - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - if (deviceSession == null) { - return null; - } + position.set(Position.KEY_PDOP, buf.readUnsignedShort() * 0.01); - List<Position> positions = new LinkedList<>(); + buf.skipBytes(2); // reserved - while (buf.readable()) { + buf.readUnsignedShort(); // checksum - int recordEnd = buf.readerIndex() + buf.readUnsignedShort(); + positions.add(position); + } - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + return positions; + } - position.set(Position.KEY_INDEX, buf.readUnsignedInt()); + private List<Position> decodeStandard(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) { - position.setDeviceTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000)); // since 1 Jan 2013 + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } - position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + List<Position> positions = new LinkedList<>(); - while (buf.readerIndex() < recordEnd) { + while (buf.readable()) { + int recordEnd = buf.readerIndex() + buf.readUnsignedShort(); - int fieldId = buf.readUnsignedByte(); - int fieldLength = buf.readUnsignedByte(); - int fieldEnd = buf.readerIndex() + (fieldLength == 255 ? buf.readUnsignedShort() : fieldLength); + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); - if (fieldId == 0) { + position.set(Position.KEY_INDEX, buf.readUnsignedInt()); - position.setFixTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000)); - position.setLatitude(buf.readInt() * 0.0000001); - position.setLongitude(buf.readInt() * 0.0000001); - position.setAltitude(buf.readShort()); - position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShort())); + position.setDeviceTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000)); // since 1 Jan 2013 - buf.readUnsignedByte(); // speed accuracy + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); - position.setCourse(buf.readUnsignedByte() * 2); + while (buf.readerIndex() < recordEnd) { - position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1); + int fieldId = buf.readUnsignedByte(); + int fieldLength = buf.readUnsignedByte(); + int fieldEnd = buf.readerIndex() + (fieldLength == 255 ? buf.readUnsignedShort() : fieldLength); - position.setAccuracy(buf.readUnsignedByte()); - position.setValid(buf.readUnsignedByte() != 0); + if (fieldId == 0) { - } else if (fieldId == 2) { + position.setFixTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000)); + position.setLatitude(buf.readInt() * 0.0000001); + position.setLongitude(buf.readInt() * 0.0000001); + position.setAltitude(buf.readShort()); + position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShort())); - int input = buf.readInt(); - int output = buf.readUnsignedShort(); - int status = buf.readUnsignedShort(); + buf.readUnsignedByte(); // speed accuracy - position.set(Position.KEY_IGNITION, BitUtil.check(input, 0)); + position.setCourse(buf.readUnsignedByte() * 2); - position.set(Position.KEY_INPUT, input); - position.set(Position.KEY_OUTPUT, output); - position.set(Position.KEY_STATUS, status); + position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1); - } else if (fieldId == 6) { + position.setAccuracy(buf.readUnsignedByte()); + position.setValid(buf.readUnsignedByte() != 0); - while (buf.readerIndex() < fieldEnd) { - switch (buf.readUnsignedByte()) { - case 1: - position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); - break; - case 2: - position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); - break; - case 3: - position.set(Position.KEY_DEVICE_TEMP, buf.readShort() * 0.01); - break; - case 4: - position.set(Position.KEY_RSSI, buf.readUnsignedShort()); - break; - case 5: - position.set("solarPower", buf.readUnsignedShort() * 0.001); - break; - default: - break; - } - } + } else if (fieldId == 2) { - } + int input = buf.readInt(); + int output = buf.readUnsignedShort(); + int status = buf.readUnsignedShort(); - buf.readerIndex(fieldEnd); + position.set(Position.KEY_IGNITION, BitUtil.check(input, 0)); - } + position.set(Position.KEY_INPUT, input); + position.set(Position.KEY_OUTPUT, output); + position.set(Position.KEY_STATUS, status); + + } else if (fieldId == 6) { + + while (buf.readerIndex() < fieldEnd) { + switch (buf.readUnsignedByte()) { + case 1: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + break; + case 2: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 3: + position.set(Position.KEY_DEVICE_TEMP, buf.readShort() * 0.01); + break; + case 4: + position.set(Position.KEY_RSSI, buf.readUnsignedShort()); + break; + case 5: + position.set("solarPower", buf.readUnsignedShort() * 0.001); + break; + default: + break; + } + } - if (position.getFixTime() == null) { - getLastLocation(position, position.getDeviceTime()); } - positions.add(position); + buf.readerIndex(fieldEnd); + + } + + if (position.getFixTime() == null) { + getLastLocation(position, position.getDeviceTime()); + } + + positions.add(position); + } + + return positions; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.skipBytes(2); // header + + int type = buf.readUnsignedByte(); + int length = buf.readUnsignedShort(); + + if (type == MSG_HELLO) { + + buf.readUnsignedInt(); // device serial number + + DeviceSession deviceSession = getDeviceSession( + channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII)); + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + if (length == 51) { + response.writeByte(0); // reserved + response.writeInt(0); // reserved + } else { + response.writeInt((int) ((System.currentTimeMillis() - 1356998400000L) / 1000)); + response.writeInt(deviceSession != null ? 0 : 1); // flags } - return positions; + sendResponse(channel, MSG_HELLO_RESPONSE, response); + + } else if (type == MSG_COMMIT) { + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeByte(1); // flags (success) + sendResponse(channel, MSG_COMMIT_RESPONSE, response); + + } else if (type == MSG_CANNED_REQUEST_1) { + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeBytes(new byte[12]); + sendResponse(channel, MSG_CANNED_RESPONSE_1, response); + + } else if (type == MSG_CANNED_REQUEST_2) { + + sendResponse(channel, MSG_CANNED_RESPONSE_2, null); + + } else if (type == MSG_DATA_RECORD_64) { + + return decodeFixed64(channel, remoteAddress, buf); + + } else if (type == MSG_DATA_RECORD) { + + return decodeStandard(channel, remoteAddress, buf); } diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java index 50b21841b..16ced37ae 100644 --- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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. @@ -54,6 +54,31 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private String decodeAlarm(long status) { + if ((status & 0x02000000) > 0) { + return Position.ALARM_GEOFENCE_ENTER; + } + if ((status & 0x04000000) > 0) { + return Position.ALARM_GEOFENCE_EXIT; + } + if ((status & 0x08000000) > 0) { + return Position.ALARM_LOW_BATTERY; + } + if ((status & 0x20000000) > 0) { + return Position.ALARM_VIBRATION; + } + if ((status & 0x80000000) > 0) { + return Position.ALARM_OVERSPEED; + } + if ((status & 0x00010000) > 0) { + return Position.ALARM_SOS; + } + if ((status & 0x00040000) > 0) { + return Position.ALARM_POWER_CUT; + } + return null; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -95,7 +120,10 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt(0) / 100.0)); position.setCourse(parser.nextHexInt(0) / 100.0); - position.set(Position.KEY_STATUS, parser.next()); + long status = parser.nextHexLong(); + position.set(Position.KEY_STATUS, status); + position.set(Position.KEY_ALARM, decodeAlarm(status)); + position.set("signal", parser.next()); position.set(Position.KEY_POWER, parser.nextDouble(0)); position.set("oil", parser.nextHexInt(0)); diff --git a/src/org/traccar/protocol/EelinkProtocolEncoder.java b/src/org/traccar/protocol/EelinkProtocolEncoder.java index 76865a039..4d2d86e68 100644 --- a/src/org/traccar/protocol/EelinkProtocolEncoder.java +++ b/src/org/traccar/protocol/EelinkProtocolEncoder.java @@ -18,10 +18,10 @@ package org.traccar.protocol; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.traccar.BaseProtocolEncoder; +import org.traccar.helper.DataConverter; import org.traccar.helper.Log; import org.traccar.model.Command; -import javax.xml.bind.DatatypeConverter; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -47,7 +47,7 @@ public class EelinkProtocolEncoder extends BaseProtocolEncoder { ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); if (connectionless) { - buf.writeBytes(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary('0' + uniqueId))); + buf.writeBytes(ChannelBuffers.wrappedBuffer(DataConverter.parseHex('0' + uniqueId))); } buf.writeByte(0x67); diff --git a/src/org/traccar/protocol/EgtsFrameDecoder.java b/src/org/traccar/protocol/EgtsFrameDecoder.java new file mode 100644 index 000000000..71ffc1811 --- /dev/null +++ b/src/org/traccar/protocol/EgtsFrameDecoder.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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 EgtsFrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + if (buf.readableBytes() < 10) { + return null; + } + + int headerLength = buf.getUnsignedByte(buf.readerIndex() + 3); + int frameLength = buf.getUnsignedShort(buf.readerIndex() + 5); + + int length = headerLength + frameLength + (frameLength > 0 ? 2 : 0); + + if (buf.readableBytes() >= length) { + return buf.readBytes(length); + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/EgtsProtocol.java b/src/org/traccar/protocol/EgtsProtocol.java new file mode 100644 index 000000000..13ec6c9a7 --- /dev/null +++ b/src/org/traccar/protocol/EgtsProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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.TrackerServer; + +import java.nio.ByteOrder; +import java.util.List; + +public class EgtsProtocol extends BaseProtocol { + + public EgtsProtocol() { + super("egts"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new EgtsFrameDecoder()); + pipeline.addLast("objectDecoder", new EgtsProtocolDecoder(EgtsProtocol.this)); + } + }; + server.setEndianness(ByteOrder.LITTLE_ENDIAN); + serverList.add(server); + } + +} diff --git a/src/org/traccar/protocol/EgtsProtocolDecoder.java b/src/org/traccar/protocol/EgtsProtocolDecoder.java new file mode 100644 index 000000000..420701e6c --- /dev/null +++ b/src/org/traccar/protocol/EgtsProtocolDecoder.java @@ -0,0 +1,256 @@ +/* + * Copyright 2018 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.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class EgtsProtocolDecoder extends BaseProtocolDecoder { + + public EgtsProtocolDecoder(EgtsProtocol protocol) { + super(protocol); + } + + public static final int PT_RESPONSE = 0; + public static final int PT_APPDATA = 1; + public static final int PT_SIGNED_APPDATA = 2; + + public static final int SERVICE_AUTH = 1; + public static final int SERVICE_TELEDATA = 2; + public static final int SERVICE_COMMANDS = 4; + public static final int SERVICE_FIRMWARE = 9; + public static final int SERVICE_ECALL = 10; + + public static final int MSG_RECORD_RESPONSE = 0; + public static final int MSG_TERM_IDENTITY = 1; + public static final int MSG_MODULE_DATA = 2; + public static final int MSG_VEHICLE_DATA = 3; + public static final int MSG_AUTH_PARAMS = 4; + public static final int MSG_AUTH_INFO = 5; + public static final int MSG_SERVICE_INFO = 6; + public static final int MSG_RESULT_CODE = 7; + public static final int MSG_POS_DATA = 16; + public static final int MSG_EXT_POS_DATA = 17; + public static final int MSG_AD_SENSORS_DATA = 18; + public static final int MSG_COUNTERS_DATA = 19; + public static final int MSG_STATE_DATA = 20; + public static final int MSG_LOOPIN_DATA = 22; + public static final int MSG_ABS_DIG_SENS_DATA = 23; + public static final int MSG_ABS_AN_SENS_DATA = 24; + public static final int MSG_ABS_CNTR_DATA = 25; + public static final int MSG_ABS_LOOPIN_DATA = 26; + public static final int MSG_LIQUID_LEVEL_SENSOR = 27; + public static final int MSG_PASSENGERS_COUNTERS = 28; + + private int packetId; + + private void sendResponse( + Channel channel, int packetType, int index, int serviceType, int type, ChannelBuffer content) { + if (channel != null) { + + ChannelBuffer data = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + data.writeByte(type); + data.writeShort(content.readableBytes()); + data.writeBytes(content); + + ChannelBuffer record = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + record.writeShort(data.readableBytes()); + record.writeShort(index); + record.writeByte(1 << 6); // flags + record.writeByte(serviceType); + record.writeByte(serviceType); + record.writeBytes(data); + int recordChecksum = Checksum.crc16(Checksum.CRC16_CCITT_FALSE, record.toByteBuffer()); + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeByte(1); // protocol version + response.writeByte(0); // security key id + response.writeByte(0); // flags + response.writeByte(5 + 2 + 2 + 2); // header length + response.writeByte(0); // encoding + response.writeShort(record.readableBytes()); + response.writeShort(packetId++); + response.writeByte(packetType); + response.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, response.toByteBuffer())); + response.writeBytes(record); + response.writeShort(recordChecksum); + + channel.write(response); + + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.skipBytes(buf.getUnsignedByte(buf.readerIndex() + 3)); + + List<Position> positions = new LinkedList<>(); + + while (buf.readableBytes() > 2) { + + int length = buf.readUnsignedShort(); + int index = buf.readUnsignedShort(); + int recordFlags = buf.readUnsignedByte(); + + if (BitUtil.check(recordFlags, 0)) { + buf.readUnsignedInt(); // object id + } + + if (BitUtil.check(recordFlags, 1)) { + buf.readUnsignedInt(); // event id + } + if (BitUtil.check(recordFlags, 2)) { + buf.readUnsignedInt(); // time + } + + int serviceType = buf.readUnsignedByte(); + buf.readUnsignedByte(); // recipient service type + + int recordEnd = buf.readerIndex() + length; + + Position position = new Position(getProtocolName()); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession != null) { + position.setDeviceId(deviceSession.getDeviceId()); + } + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeShort(index); + response.writeByte(0); // success + sendResponse(channel, PT_RESPONSE, index, serviceType, MSG_RECORD_RESPONSE, response); + + while (buf.readerIndex() < recordEnd) { + int type = buf.readUnsignedByte(); + int end = buf.readUnsignedShort() + buf.readerIndex(); + + if (type == MSG_TERM_IDENTITY) { + + buf.readUnsignedInt(); // object id + int flags = buf.readUnsignedByte(); + + if (BitUtil.check(flags, 0)) { + buf.readUnsignedShort(); // home dispatcher identifier + } + if (BitUtil.check(flags, 1)) { + getDeviceSession( + channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII).trim()); + } + if (BitUtil.check(flags, 2)) { + getDeviceSession( + channel, remoteAddress, buf.readBytes(16).toString(StandardCharsets.US_ASCII).trim()); + } + if (BitUtil.check(flags, 3)) { + buf.skipBytes(3); // language identifier + } + if (BitUtil.check(flags, 5)) { + buf.skipBytes(3); // network identifier + } + if (BitUtil.check(flags, 6)) { + buf.readUnsignedShort(); // buffer size + } + if (BitUtil.check(flags, 7)) { + getDeviceSession( + channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII).trim()); + } + + response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeByte(0); // success + sendResponse(channel, PT_APPDATA, index, serviceType, MSG_RESULT_CODE, response); + + } else if (type == MSG_POS_DATA) { + + position.setTime(new Date((buf.readUnsignedInt() + 1262304000) * 1000)); // since 2010-01-01 + position.setLatitude(buf.readUnsignedInt() * 90.0 / 0xFFFFFFFFL); + position.setLongitude(buf.readUnsignedInt() * 180.0 / 0xFFFFFFFFL); + + int flags = buf.readUnsignedByte(); + position.setValid(BitUtil.check(flags, 0)); + if (BitUtil.check(flags, 5)) { + position.setLatitude(-position.getLatitude()); + } + if (BitUtil.check(flags, 6)) { + position.setLongitude(-position.getLongitude()); + } + + int speed = buf.readUnsignedShort(); + position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speed, 14) * 0.1)); + position.setCourse(buf.readUnsignedByte() + (BitUtil.check(speed, 15) ? 0x100 : 0)); + + position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium() * 100); + position.set(Position.KEY_INPUT, buf.readUnsignedByte()); + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + + if (BitUtil.check(flags, 7)) { + position.setAltitude(buf.readMedium()); + } + + } else if (type == MSG_EXT_POS_DATA) { + + int flags = buf.readUnsignedByte(); + + if (BitUtil.check(flags, 0)) { + position.set(Position.KEY_VDOP, buf.readUnsignedShort()); + } + if (BitUtil.check(flags, 1)) { + position.set(Position.KEY_HDOP, buf.readUnsignedShort()); + } + if (BitUtil.check(flags, 2)) { + position.set(Position.KEY_PDOP, buf.readUnsignedShort()); + } + if (BitUtil.check(flags, 3)) { + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + } + + } else if (type == MSG_AD_SENSORS_DATA) { + + buf.readUnsignedByte(); // inputs flags + + position.set(Position.KEY_OUTPUT, buf.readUnsignedByte()); + + buf.readUnsignedByte(); // adc flags + + } + + buf.readerIndex(end); + } + + if (serviceType == SERVICE_TELEDATA && deviceSession != null) { + positions.add(position); + } + } + + return positions.isEmpty() ? null : positions; + } + +} diff --git a/src/org/traccar/protocol/FlespiProtocolDecoder.java b/src/org/traccar/protocol/FlespiProtocolDecoder.java index be0e4a07f..526e10fa2 100644 --- a/src/org/traccar/protocol/FlespiProtocolDecoder.java +++ b/src/org/traccar/protocol/FlespiProtocolDecoder.java @@ -123,7 +123,7 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { return true; case "din": case "dout": - position.set((name.equals("din") ? Position.KEY_INPUT : Position.KEY_OUTPUT), + position.set(name.equals("din") ? Position.KEY_INPUT : Position.KEY_OUTPUT, ((JsonNumber) value).intValue()); return true; case "gps.vehicle.mileage": diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java index e664c4734..ff300d429 100644 --- a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -210,7 +210,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version .number("(d{15}|x{14}),") // imei .expression("[^,]*,") // device name - .number("x{8},") // mask + .number("(x{8}),") // mask .number("(d+)?,") // power .number("d{1,2},") // report type .number("d{1,2},") // count @@ -429,8 +429,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { 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, parser.nextDouble()); + position.set(Position.KEY_CHARGE, parser.nextInt() == 1); position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); @@ -444,7 +444,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, parser.nextDateTime()); - position.set(Position.KEY_INDEX, parser.nextHexInt(0)); + position.set(Position.KEY_INDEX, parser.nextHexInt()); return position; } @@ -457,8 +457,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } position.set("deviceType", parser.next()); - position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0)); - position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0)); + position.set(Position.KEY_VERSION_FW, parser.nextHexInt()); + position.set(Position.KEY_VERSION_HW, parser.nextHexInt()); getLastLocation(position, parser.nextDateTime()); @@ -466,8 +466,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } private void decodeLocation(Position position, Parser parser) { - int hdop = parser.nextInt(0); - position.setValid(hdop > 0); + Integer hdop = parser.nextInt(); + position.setValid(hdop == null || hdop > 0); position.set(Position.KEY_HDOP, hdop); position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); @@ -476,25 +476,27 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext(8)) { position.setValid(true); - position.setLongitude(parser.nextDouble(0)); - position.setLatitude(parser.nextDouble(0)); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); position.setTime(parser.nextDateTime()); } else { getLastLocation(position, null); } if (parser.hasNext(6)) { - int mcc = parser.nextInt(0); - int mnc = parser.nextInt(0); + int mcc = parser.nextInt(); + int mnc = parser.nextInt(); if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0)))); + position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt()))); } if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0)))); + position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt()))); } } - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } } private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { @@ -509,16 +511,22 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { 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); + if (parser.hasNext()) { + position.set("odbConnect", parser.nextInt() == 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); + if (parser.hasNext()) { + position.set(Position.KEY_OBD_ODOMETER, parser.nextInt() * 1000); + } decodeLocation(position, parser); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (parser.hasNext()) { + position.set(Position.KEY_OBD_ODOMETER, (int) (parser.nextDouble() * 1000)); + } decodeDeviceTime(position, parser); @@ -649,14 +657,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private void decodeStatus(Position position, Parser parser) { if (parser.hasNext(3)) { - int ignition = parser.nextHexInt(0); + int ignition = parser.nextHexInt(); 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)); + position.set(Position.KEY_INPUT, parser.nextHexInt()); + position.set(Position.KEY_OUTPUT, parser.nextHexInt()); } } @@ -674,7 +682,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { LinkedList<Position> positions = new LinkedList<>(); String vin = parser.next(); - int power = parser.nextInt(0); + Integer power = parser.nextInt(); Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); while (itemParser.find()) { @@ -692,14 +700,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { decodeLocation(position, parser); - if (power > 10) { + if (power != null && power > 10) { position.set(Position.KEY_POWER, power * 0.001); // only on some devices } - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } position.set(Position.KEY_HOURS, parser.next()); position.set(Position.PREFIX_ADC + 1, parser.next()); position.set(Position.PREFIX_ADC + 2, parser.next()); @@ -730,9 +742,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return null; } + long mask = parser.nextHexLong(); + LinkedList<Position> positions = new LinkedList<>(); - int power = parser.nextInt(0); + Integer power = parser.nextInt(); Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); while (itemParser.find()) { @@ -748,8 +762,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { decodeLocation(position, parser); - position.set(Position.KEY_POWER, power * 0.001); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (power != null) { + position.set(Position.KEY_POWER, power * 0.001); + } + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); position.set(Position.KEY_HOURS, parser.next()); position.set(Position.PREFIX_ADC + 1, parser.next()); position.set(Position.PREFIX_ADC + 2, parser.next()); @@ -759,14 +775,37 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { 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); + + index += 1; // device type + + if (BitUtil.check(mask, 0)) { + index += 1; // digital fuel sensor data + } + + if (BitUtil.check(mask, 1)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // id + index += 1; // type + if (!data[index++].isEmpty()) { + position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); + } + } + } + + if (BitUtil.check(mask, 2)) { + index += 1; // can data + } + + if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // type + if (BitUtil.check(mask, 3)) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); + } + if (BitUtil.check(mask, 4)) { + index += 1; // volume } } } @@ -790,7 +829,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { decodeLocation(position, parser); position.set(Position.KEY_HOURS, parser.next()); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); decodeDeviceTime(position, parser); @@ -808,7 +847,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { decodeLocation(position, parser); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); decodeDeviceTime(position, parser); @@ -826,7 +865,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { Network network = new Network(); - parser.nextInt(0); // count + parser.nextInt(); // count Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next()); while (matcher.find()) { String mac = matcher.group(1).replaceAll("(..)", "$1:"); @@ -836,7 +875,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(network); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); return position; } @@ -874,7 +913,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return null; } - int reportType = parser.nextInt(0); + int reportType = parser.nextInt(); if (type.equals("NMR")) { position.set(Position.KEY_MOTION, reportType == 1); } else if (type.equals("SOS")) { @@ -883,10 +922,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { decodeLocation(position, parser); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } decodeDeviceTime(position, parser); @@ -904,7 +947,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return null; } - int hdop = parser.nextInt(0); + int hdop = parser.nextInt(); position.setValid(hdop > 0); position.set(Position.KEY_HDOP, hdop); @@ -913,8 +956,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextDouble(0)); if (parser.hasNext(2)) { - position.setLongitude(parser.nextDouble(0)); - position.setLatitude(parser.nextDouble(0)); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); } else { getLastLocation(position, null); } @@ -925,7 +968,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext(4)) { position.setNetwork(new Network(CellTower.from( - parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0)))); + parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt()))); } decodeDeviceTime(position, parser); diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java index 13ce839ea..0fa1934da 100644 --- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java +++ b/src/org/traccar/protocol/GoSafeProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,75 +45,7 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // imei .number("(dd)(dd)(dd)") // time (hhmmss) .number("(dd)(dd)(dd),") // date (ddmmyy) - .expression("(.*)#?") // data - .compile(); - - private static final Pattern PATTERN_ITEM = new PatternBuilder() - .number("(x+)?,").optional() // event - .groupBegin() - .text("SYS:") - .expression("[^,]*,") - .groupEnd("?") - .groupBegin() - .text("GPS:") - .expression("([AV]);") // validity - .number("(d+);") // satellites - .number("([NS])(d+.d+);") // latitude - .number("([EW])(d+.d+);") // longitude - .number("(d+)?;") // speed - .number("(d+);") // course - .number("(d+);") // altitude - .number("(d+.d+)") // hdop - .number(";(d+.d+)").optional() // vdop - .expression(",?") - .groupEnd() - .groupBegin() - .text("GSM:") - .number("d*;") // registration - .number("d*;") // gsm signal - .number("(d+);") // mcc - .number("(d+);") // mnc - .number("(x+);") // lac - .number("(x+);") // cid - .number("(-d+)") // rssi - .expression("[^,]*,?") - .groupEnd("?") - .groupBegin() - .text("COT:") - .number("(d+)") // odometer - .number("(?:;d+:d+:d+)?") // engine hours - .expression(",?") - .groupEnd("?") - .groupBegin() - .text("ADC:") - .number("(d+.d+)") // power - .number("(?:;(d+.d+))?,?") // battery - .groupEnd("?") - .groupBegin() - .text("DTT:") - .number("(x+);") // status - .number("(x+)?;") // io - .number("(x+);") // geo-fence 0-119 - .number("(x+);") // geo-fence 120-155 - .number("(x+)") // event status - .number("(?:;(x+))?,?") // packet type - .groupEnd("?") - .groupBegin() - .text("ETD:").expression("([^,]+),?") - .groupEnd("?") - .groupBegin() - .text("OBD:") - .number("(x+),?") - .groupEnd("?") - .groupBegin() - .text("FUL:").expression("[^,]*,?") - .groupEnd("?") - .groupBegin() - .text("TRU:").expression("[^,]*,?") - .groupEnd("?") - .groupBegin() - .text("TAG:").expression("([^,]+),?") - .groupEnd("?") + .expression("([^#]*)#?") // data .compile(); private static final Pattern PATTERN_OLD = new PatternBuilder() @@ -133,70 +65,138 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private Position decodePosition(DeviceSession deviceSession, Parser parser, Date time) { + private void decodeFragment(Position position, String fragment) { + int dataIndex = fragment.indexOf(':'); + int index = 0; + String[] values = fragment.substring(dataIndex + 1).split(";"); + switch (fragment.substring(0, dataIndex)) { + case "GPS": + position.setValid(values[index++].equals("A")); + position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + position.setLatitude(Double.parseDouble(values[index].substring(1))); + if (values[index++].charAt(0) == 'S') { + position.setLatitude(-position.getLatitude()); + } + position.setLongitude(Double.parseDouble(values[index].substring(1))); + if (values[index++].charAt(0) == 'W') { + position.setLongitude(-position.getLongitude()); + } + if (!values[index++].isEmpty()) { + position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1]))); + } + position.setCourse(Integer.parseInt(values[index++])); + position.setAltitude(Integer.parseInt(values[index++])); + if (index < values.length) { + position.set(Position.KEY_HDOP, Double.parseDouble(values[index++])); + } + if (index < values.length) { + position.set(Position.KEY_VDOP, Double.parseDouble(values[index++])); + } + break; + case "GSM": + index += 1; // registration status + index += 1; // signal strength + position.setNetwork(new Network(CellTower.from( + Integer.parseInt(values[index++]), Integer.parseInt(values[index++]), + Integer.parseInt(values[index++], 16), Integer.parseInt(values[index++], 16), + Integer.parseInt(values[index++])))); + break; + case "COT": + if (index < values.length) { + position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++])); + } + if (index < values.length) { + String[] hours = values[index].split("-"); + position.set(Position.KEY_HOURS, Integer.parseInt(hours[0]) + + Integer.parseInt(hours[0]) / 60.0 + Integer.parseInt(hours[0]) / 3600.0); + } + break; + case "ADC": + position.set(Position.KEY_POWER, Double.parseDouble(values[index++])); + if (index < values.length) { + position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++])); + } + if (index < values.length) { + position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++])); + } + if (index < values.length) { + position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++])); + } + break; + case "DTT": + position.set(Position.KEY_STATUS, Integer.parseInt(values[index++], 16)); + if (!values[index++].isEmpty()) { + int io = Integer.parseInt(values[index - 1], 16); + position.set(Position.KEY_IGNITION, BitUtil.check(io, 0)); + position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 1)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(io, 2)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 3)); + position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 4)); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 5)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 6)); + position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 7)); + } + position.set(Position.KEY_GEOFENCE, values[index++] + values[index++]); + position.set("eventStatus", values[index++]); + if (index < values.length) { + position.set("packetType", values[index++]); + } + break; + case "ETD": + position.set("eventData", values[index++]); + break; + case "OBD": + position.set("obd", values[index++]); + break; + case "TAG": + position.set("tagData", values[index++]); + break; + case "IWD": + if (index < values.length && values[index + 1].equals("0")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, values[index + 2]); + } + break; + default: + break; + } + } - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + private Object decodeData(DeviceSession deviceSession, Date time, String data) { - if (time != null) { - position.setTime(time); - } + List<Position> positions = new LinkedList<>(); + Position position = null; + int index = 0; + String[] fragments = data.split(","); - position.set(Position.KEY_EVENT, parser.next()); + while (index < fragments.length) { - position.setValid(parser.next().equals("A")); - position.set(Position.KEY_SATELLITES, parser.nextInt()); + if (fragments[index].isEmpty() || Character.isDigit(fragments[index].charAt(0))) { - position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); - position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); + if (position != null) { + positions.add(position); + } - position.set(Position.KEY_HDOP, parser.nextDouble(0)); - position.set(Position.KEY_VDOP, parser.nextDouble(0)); + position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.setTime(time); - if (parser.hasNext(5)) { - position.setNetwork(new Network(CellTower.from(parser.nextInt(0), parser.nextInt(0), - parser.nextHexInt(0), parser.nextHexInt(0), parser.nextInt(0)))); - } - if (parser.hasNext()) { - position.set(Position.KEY_ODOMETER, parser.nextInt(0)); - } - position.set(Position.KEY_POWER, parser.nextDouble()); - position.set(Position.KEY_BATTERY, parser.nextDouble()); - - if (parser.hasNext(6)) { - position.set(Position.KEY_STATUS, parser.nextHexLong()); - Integer io = parser.nextHexInt(); - if (io != null) { - position.set(Position.KEY_IGNITION, BitUtil.check(io, 0)); - position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 1)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(io, 2)); - position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 3)); - position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 4)); - position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 5)); - position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 6)); - position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 7)); - } - position.set(Position.KEY_GEOFENCE, parser.next() + parser.next()); - position.set("eventStatus", parser.next()); - position.set("packetType", parser.next()); - } + if (!fragments[index++].isEmpty()) { + position.set(Position.KEY_EVENT, Integer.parseInt(fragments[index - 1])); + } - if (parser.hasNext()) { - position.set("eventData", parser.next()); - } + } else { + + decodeFragment(position, fragments[index++]); + + } - if (parser.hasNext()) { - position.set("obd", parser.next()); } - if (parser.hasNext()) { - position.set("tagData", parser.next()); + if (position != null) { + positions.add(position); } - return position; + return positions; } @Override @@ -246,18 +246,12 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { } else { - Date time = null; + Date time = new Date(); if (parser.hasNext(6)) { time = parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY); } - List<Position> positions = new LinkedList<>(); - Parser itemParser = new Parser(PATTERN_ITEM, parser.next()); - while (itemParser.find()) { - positions.add(decodePosition(deviceSession, itemParser, time)); - } - - return positions; + return decodeData(deviceSession, time, parser.next()); } } diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java index 85b4dcada..f32d22c00 100644 --- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2018 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,6 +26,7 @@ import org.traccar.model.Network; import org.traccar.model.Position; import java.net.SocketAddress; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class Gps103ProtocolDecoder extends BaseProtocolDecoder { @@ -38,10 +39,19 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { .text("imei:") .number("(d+),") // imei .expression("([^,]+),") // alarm + .groupBegin() .number("(dd)/?(dd)/?(dd) ?") // local date (yymmdd) .number("(dd):?(dd)(?:dd)?,") // local time (hhmmss) + .or() + .number("d*,") + .groupEnd() .expression("([^,]+)?,") // rfid - .expression("[FL],") // full / low + .groupBegin() + .text("L,,,") + .number("(x+),,") // lac + .number("(x+),,,") // cid + .or() + .text("F,") .groupBegin() .number("(dd)(dd)(dd).d+") // time utc (hhmmss) .or() @@ -63,24 +73,10 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { .expression("([^,;]+)?,?") .expression("([^,;]+)?,?") .expression("([^,;]+)?,?") + .groupEnd() .any() .compile(); - private static final Pattern PATTERN_NETWORK = new PatternBuilder() - .text("imei:") - .number("(d+),") // imei - .expression("[^,]+,") // alarm - .number("d*,,") - .text("L,,,") - .number("(x+),,") // lac - .number("(x+),,,") // cid - .any() - .compile(); - - private static final Pattern PATTERN_HANDSHAKE = new PatternBuilder() - .number("##,imei:(d+),A") - .compile(); - private static final Pattern PATTERN_OBD = new PatternBuilder() .text("imei:") .number("(d+),") // imei @@ -143,85 +139,9 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { } } - @Override - protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - - String sentence = (String) msg; - - // Send response #1 - if (sentence.contains("##")) { - if (channel != null) { - channel.write("LOAD", remoteAddress); - Parser handshakeParser = new Parser(PATTERN_HANDSHAKE, sentence); - if (handshakeParser.matches()) { - getDeviceSession(channel, remoteAddress, handshakeParser.next()); - } - } - return null; - } - - // Send response #2 - if (!sentence.isEmpty() && Character.isDigit(sentence.charAt(0))) { - if (channel != null) { - channel.write("ON", remoteAddress); - } - int start = sentence.indexOf("imei:"); - if (start >= 0) { - sentence = sentence.substring(start); - } else { - return null; - } - } - - Position position = new Position(getProtocolName()); - - Parser parser = new Parser(PATTERN_NETWORK, sentence); - if (parser.matches()) { - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); - - getLastLocation(position, null); - - position.setNetwork(new Network( - CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0)))); - - return position; - - } - - parser = new Parser(PATTERN_OBD, sentence); - if (parser.matches()) { - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); - - getLastLocation(position, parser.nextDateTime()); - - position.set(Position.KEY_ODOMETER, parser.nextInt(0)); - parser.nextDouble(0); // instant fuel consumption - position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble(0)); - position.set(Position.KEY_HOURS, parser.nextInt()); - position.set(Position.KEY_OBD_SPEED, parser.nextInt(0)); - position.set(Position.KEY_ENGINE_LOAD, parser.next()); - position.set(Position.KEY_COOLANT_TEMP, parser.nextInt()); - position.set(Position.KEY_THROTTLE, parser.next()); - position.set(Position.KEY_RPM, parser.nextInt(0)); - position.set(Position.KEY_BATTERY, parser.nextDouble(0)); - position.set(Position.KEY_DTCS, parser.next().replace(',', ' ').trim()); - - return position; + private Position decodeRegular(Channel channel, SocketAddress remoteAddress, String sentence) { - } - - parser = new Parser(PATTERN, sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } @@ -231,6 +151,8 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { if (deviceSession == null) { return null; } + + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); String alarm = parser.next(); @@ -262,36 +184,115 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid); } - String utcHours = parser.next(); - String utcMinutes = parser.next(); + if (parser.hasNext(2)) { - dateBuilder.setTime(localHours, localMinutes, parser.nextInt(0)); + getLastLocation(position, null); + + position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0)))); + + } else { + + String utcHours = parser.next(); + String utcMinutes = parser.next(); - // Timezone calculation - if (utcHours != null && utcMinutes != null) { - int deltaMinutes = (localHours - Integer.parseInt(utcHours)) * 60; - deltaMinutes += localMinutes - Integer.parseInt(utcMinutes); - if (deltaMinutes <= -12 * 60) { - deltaMinutes += 24 * 60; - } else if (deltaMinutes > 12 * 60) { - deltaMinutes -= 24 * 60; + dateBuilder.setTime(localHours, localMinutes, parser.nextInt(0)); + + // Timezone calculation + if (utcHours != null && utcMinutes != null) { + int deltaMinutes = (localHours - Integer.parseInt(utcHours)) * 60; + deltaMinutes += localMinutes - Integer.parseInt(utcMinutes); + if (deltaMinutes <= -12 * 60) { + deltaMinutes += 24 * 60; + } else if (deltaMinutes > 12 * 60) { + deltaMinutes -= 24 * 60; + } + dateBuilder.addMinute(-deltaMinutes); + } + position.setTime(dateBuilder.getDate()); + + position.setValid(parser.next().equals("A")); + position.setFixTime(position.getDeviceTime()); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM)); + position.setSpeed(parser.nextDouble(0)); + position.setCourse(parser.nextDouble(0)); + position.setAltitude(parser.nextDouble(0)); + + for (int i = 1; i <= 5; i++) { + position.set(Position.PREFIX_IO + i, parser.next()); } - dateBuilder.addMinute(-deltaMinutes); + } - position.setTime(dateBuilder.getDate()); - position.setValid(parser.next().equals("A")); - position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM)); - position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM)); - position.setSpeed(parser.nextDouble(0)); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); + return position; + } + + private Position decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { - for (int i = 1; i <= 5; i++) { - position.set(Position.PREFIX_IO + i, parser.next()); + Parser parser = new Parser(PATTERN_OBD, sentence); + if (!parser.matches()) { + return null; } + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, parser.nextDateTime()); + + position.set(Position.KEY_ODOMETER, parser.nextInt(0)); + parser.nextDouble(0); // instant fuel consumption + position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble(0)); + position.set(Position.KEY_HOURS, parser.nextInt()); + position.set(Position.KEY_OBD_SPEED, parser.nextInt(0)); + position.set(Position.KEY_ENGINE_LOAD, parser.next()); + position.set(Position.KEY_COOLANT_TEMP, parser.nextInt()); + position.set(Position.KEY_THROTTLE, parser.next()); + position.set(Position.KEY_RPM, parser.nextInt(0)); + position.set(Position.KEY_BATTERY, parser.nextDouble(0)); + position.set(Position.KEY_DTCS, parser.next().replace(',', ' ').trim()); + return position; } + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + if (sentence.contains("##")) { + if (channel != null) { + channel.write("LOAD", remoteAddress); + Matcher matcher = Pattern.compile("##,imei:(\\d+),A").matcher(sentence); + if (matcher.matches()) { + getDeviceSession(channel, remoteAddress, matcher.group(1)); + } + } + return null; + } + + if (!sentence.isEmpty() && Character.isDigit(sentence.charAt(0))) { + if (channel != null) { + channel.write("ON", remoteAddress); + } + int start = sentence.indexOf("imei:"); + if (start >= 0) { + sentence = sentence.substring(start); + } else { + return null; + } + } + + if (sentence.contains("OBD")) { + return decodeObd(channel, remoteAddress, sentence); + } else { + return decodeRegular(channel, remoteAddress, sentence); + } + } + } diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java index 5ee663ee1..9764adf04 100644 --- a/src/org/traccar/protocol/H02ProtocolDecoder.java +++ b/src/org/traccar/protocol/H02ProtocolDecoder.java @@ -456,7 +456,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { switch (marker) { case "*": - String sentence = buf.toString(StandardCharsets.US_ASCII); + String sentence = buf.toString(StandardCharsets.US_ASCII).trim(); int typeStart = sentence.indexOf(',', sentence.indexOf(',') + 1) + 1; int typeEnd = sentence.indexOf(',', typeStart); if (typeEnd > 0) { diff --git a/src/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/org/traccar/protocol/HuabaoProtocolEncoder.java index 7d6f0510d..d1889bf5e 100644 --- a/src/org/traccar/protocol/HuabaoProtocolEncoder.java +++ b/src/org/traccar/protocol/HuabaoProtocolEncoder.java @@ -18,10 +18,10 @@ package org.traccar.protocol; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.traccar.BaseProtocolEncoder; +import org.traccar.helper.DataConverter; import org.traccar.helper.Log; import org.traccar.model.Command; -import javax.xml.bind.DatatypeConverter; import java.text.SimpleDateFormat; import java.util.Date; @@ -31,10 +31,10 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder { protected Object encodeCommand(Command command) { ChannelBuffer id = ChannelBuffers.wrappedBuffer( - DatatypeConverter.parseHexBinary(getUniqueId(command.getDeviceId()))); + DataConverter.parseHex(getUniqueId(command.getDeviceId()))); ChannelBuffer data = ChannelBuffers.dynamicBuffer(); - byte[] time = DatatypeConverter.parseHexBinary(new SimpleDateFormat("yyMMddHHmmss").format(new Date())); + byte[] time = DataConverter.parseHex(new SimpleDateFormat("yyMMddHHmmss").format(new Date())); switch (command.getType()) { case Command.TYPE_ENGINE_STOP: diff --git a/src/org/traccar/protocol/Ivt401ProtocolDecoder.java b/src/org/traccar/protocol/Ivt401ProtocolDecoder.java index 6e11a763c..d2f1d3d69 100644 --- a/src/org/traccar/protocol/Ivt401ProtocolDecoder.java +++ b/src/org/traccar/protocol/Ivt401ProtocolDecoder.java @@ -58,6 +58,23 @@ public class Ivt401ProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+),") // tilt .number("(d+),") // trip .number("(d+),") // odometer + .groupBegin() + .number("([01]),") // overspeed + .number("[01],") // input 2 misuse + .number("[01],") // immobilizer + .number("[01],") // temperature alert + .number("[0-2]+,") // geofence + .number("([0-3]),") // harsh driving + .number("[01],") // reconnect + .number("([01]),") // low battery + .number("([01]),") // power disconnected + .number("[01],") // gps failure + .number("([01]),") // towing + .number("[01],") // server unreachable + .number("[128],") // sleep mode + .expression("([^,]+)?,") // driver id + .number("d+,") // sms count + .groupEnd("?") .any() .compile(); @@ -138,6 +155,27 @@ public class Ivt401ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, parser.nextLong()); + if (parser.hasNext(6)) { + position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_OVERSPEED : null); + switch (parser.nextInt()) { + case 1: + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 2: + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 3: + position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + break; + default: + break; + } + position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_LOW_BATTERY : null); + position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_POWER_CUT : null); + position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_TOW : null); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + } + return position; } diff --git a/src/org/traccar/protocol/KhdProtocolDecoder.java b/src/org/traccar/protocol/KhdProtocolDecoder.java index d1b5413e5..2f29a16f8 100644 --- a/src/org/traccar/protocol/KhdProtocolDecoder.java +++ b/src/org/traccar/protocol/KhdProtocolDecoder.java @@ -36,17 +36,10 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { private String readSerialNumber(ChannelBuffer buf) { int b1 = buf.readUnsignedByte(); - int b2 = buf.readUnsignedByte(); - if (b2 > 0x80) { - b2 -= 0x80; - } - int b3 = buf.readUnsignedByte(); - if (b3 > 0x80) { - b3 -= 0x80; - } + int b2 = buf.readUnsignedByte() - 0x80; + int b3 = buf.readUnsignedByte() - 0x80; int b4 = buf.readUnsignedByte(); - String serialNumber = String.format("%02d%02d%02d%02d", b1, b2, b3, b4); - return String.valueOf(Long.parseLong(serialNumber)); + return String.format("%02d%02d%02d%02d", b1, b2, b3, b4); } public static final int MSG_LOGIN = 0xB1; diff --git a/src/org/traccar/protocol/KhdProtocolEncoder.java b/src/org/traccar/protocol/KhdProtocolEncoder.java index 618e43dad..cb26c757a 100644 --- a/src/org/traccar/protocol/KhdProtocolEncoder.java +++ b/src/org/traccar/protocol/KhdProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 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. @@ -27,7 +27,7 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder { public static final int MSG_CUT_OIL = 0x39; public static final int MSG_RESUME_OIL = 0x38; - private ChannelBuffer encodeCommand(int command) { + private ChannelBuffer encodeCommand(int command, String uniqueId) { ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); @@ -37,7 +37,12 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder { buf.writeByte(command); buf.writeShort(6); // size - buf.writeInt(0); // terminal id + uniqueId = "00000000".concat(uniqueId); + uniqueId = uniqueId.substring(uniqueId.length() - 8); + buf.writeByte(Integer.parseInt(uniqueId.substring(0, 2))); + buf.writeByte(Integer.parseInt(uniqueId.substring(2, 4)) + 0x80); + buf.writeByte(Integer.parseInt(uniqueId.substring(4, 6)) + 0x80); + buf.writeByte(Integer.parseInt(uniqueId.substring(6, 8))); buf.writeByte(Checksum.xor(buf.toByteBuffer())); buf.writeByte(0x0D); // ending @@ -48,11 +53,13 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder { @Override protected Object encodeCommand(Command command) { + String uniqueId = getUniqueId(command.getDeviceId()); + switch (command.getType()) { case Command.TYPE_ENGINE_STOP: - return encodeCommand(MSG_CUT_OIL); + return encodeCommand(MSG_CUT_OIL, uniqueId); case Command.TYPE_ENGINE_RESUME: - return encodeCommand(MSG_RESUME_OIL); + return encodeCommand(MSG_RESUME_OIL, uniqueId); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/LaipacProtocolDecoder.java b/src/org/traccar/protocol/LaipacProtocolDecoder.java index 99189d012..bfaff9ec4 100644 --- a/src/org/traccar/protocol/LaipacProtocolDecoder.java +++ b/src/org/traccar/protocol/LaipacProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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,8 @@ import org.traccar.helper.Checksum; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; +import org.traccar.model.CellTower; +import org.traccar.model.Network; import org.traccar.model.Position; import java.net.SocketAddress; @@ -45,11 +47,45 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { .number("(d+.d+),") // speed .number("(d+.d+),") // course .number("(dd)(dd)(dd),") // date (ddmmyy) - .expression("(.),") // type - .expression("[^*]+").text("*") + .expression("([abZXTSMHFE86430]),") // event code + .expression("([\\d.]+),") // battery voltage + .number("(d+),") // current mileage + .number("(d),") // gps status + .number("(d+),") // adc1 + .number("(d+)") // adc2 + .number(",(xxxx)") // lac + .number("(xxxx),") // cid + .number("(ddd)") // mcc + .number("(ddd)") // mnc + .optional(4) + .text("*") .number("(xx)") // checksum .compile(); + private String decodeAlarm(String event) { + switch (event) { + case "Z": + return Position.ALARM_LOW_BATTERY; + case "X": + return Position.ALARM_GEOFENCE_ENTER; + case "T": + return Position.ALARM_TAMPERING; + case "H": + return Position.ALARM_POWER_OFF; + case "8": + return Position.ALARM_SHOCK; + case "7": + case "4": + return Position.ALARM_GEOFENCE_EXIT; + case "6": + return Position.ALARM_OVERSPEED; + case "3": + return Position.ALARM_SOS; + default: + return null; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -79,6 +115,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { String status = parser.next(); position.setValid(status.toUpperCase().equals("A")); + position.set(Position.KEY_STATUS, status); position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); @@ -88,25 +125,37 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); position.setTime(dateBuilder.getDate()); - String type = parser.next(); + String event = parser.next(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + position.set(Position.KEY_BATTERY, Double.parseDouble(parser.next().replaceAll("\\.", "")) * 0.001); + position.set(Position.KEY_ODOMETER, parser.nextDouble()); + position.set(Position.KEY_GPS, parser.nextInt()); + position.set(Position.PREFIX_ADC + 1, parser.nextDouble() * 0.001); + position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001); + + Integer lac = parser.nextHexInt(); + Integer cid = parser.nextHexInt(); + Integer mcc = parser.nextInt(); + Integer mnc = parser.nextInt(); + if (lac != null && cid != null && mcc != null && mnc != null) { + position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid))); + } + String checksum = parser.next(); if (channel != null) { - - if (Character.isLowerCase(status.charAt(0))) { - String response = "$EAVACK," + type + "," + checksum; - response += Checksum.nmea(response); + if (event.equals("3")) { + channel.write("$AVCFG,00000000,d*31\r\n"); + } else if (event.equals("X") || event.equals("4")) { + channel.write("$AVCFG,00000000,x*2D\r\n"); + } else if (event.equals("Z")) { + channel.write("$AVCFG,00000000,z*2F\r\n"); + } else if (Character.isLowerCase(status.charAt(0))) { + String response = "$EAVACK," + event + "," + checksum; + response += Checksum.nmea(response) + "\r\n"; channel.write(response); } - - if (type.equals("S") || type.equals("T")) { - channel.write("$AVCFG,00000000,t*21"); - } else if (type.equals("3")) { - channel.write("$AVCFG,00000000,d*31"); - } else if (type.equals("X") || type.equals("4")) { - channel.write("$AVCFG,00000000,x*2D"); - } - } return position; diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java index 91618b534..14d39e0cc 100644 --- a/src/org/traccar/protocol/MegastekProtocolDecoder.java +++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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. @@ -250,7 +250,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // mcc .number("(d+),") // mnc .number("(xxxx),") // lac - .number("(xxxx),") // cid + .number("(x+),") // cid .number("(d+)?,") // gsm .expression("([01]+)?,") // input .expression("([01]+)?,") // output @@ -268,7 +268,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { .number("(d+)?,") // rfid .expression("[^,]*,") .number("(d+)?,") // battery - .expression("([^,]*);") // alert + .expression("([^,]*)") // alert .any() .compile(); diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java index 2e0a1e84c..340d947e3 100644 --- a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java +++ b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java @@ -19,10 +19,10 @@ import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.traccar.BaseProtocolEncoder; import org.traccar.helper.Checksum; +import org.traccar.helper.DataConverter; import org.traccar.helper.Log; import org.traccar.model.Command; -import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; import java.util.TimeZone; @@ -44,7 +44,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { buf.writeShort(2 + 2 + 7 + 2 + content.readableBytes() + 2 + 2); // message length - buf.writeBytes(DatatypeConverter.parseHexBinary((getUniqueId(deviceId) + "FFFFFFFFFFFFFF").substring(0, 14))); + buf.writeBytes(DataConverter.parseHex((getUniqueId(deviceId) + "FFFFFFFFFFFFFF").substring(0, 14))); buf.writeShort(type); diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java index 3a1885d6f..6fee0fd9f 100644 --- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -256,7 +256,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { if (values.length > 5 && !values[5].isEmpty()) { String[] data = values[5].split("\\|"); - boolean started = data[0].charAt(0) == '0'; + boolean started = data[0].charAt(1) == '0'; position.set("taximeterOn", started); position.set("taximeterStart", data[1]); if (data.length > 2) { diff --git a/src/org/traccar/protocol/OpenGtsProtocol.java b/src/org/traccar/protocol/OpenGtsProtocol.java new file mode 100644 index 000000000..a0246ba1b --- /dev/null +++ b/src/org/traccar/protocol/OpenGtsProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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.http.HttpRequestDecoder; +import org.jboss.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class OpenGtsProtocol extends BaseProtocol { + + public OpenGtsProtocol() { + super("opengts"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("httpEncoder", new HttpResponseEncoder()); + pipeline.addLast("httpDecoder", new HttpRequestDecoder()); + pipeline.addLast("objectDecoder", new OpenGtsProtocolDecoder(OpenGtsProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/OpenGtsProtocolDecoder.java b/src/org/traccar/protocol/OpenGtsProtocolDecoder.java new file mode 100644 index 000000000..ba8f434d8 --- /dev/null +++ b/src/org/traccar/protocol/OpenGtsProtocolDecoder.java @@ -0,0 +1,115 @@ +/* + * Copyright 2018 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.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.QueryStringDecoder; +import org.traccar.BaseHttpProtocolDecoder; +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.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class OpenGtsProtocolDecoder extends BaseHttpProtocolDecoder { + + private static final Pattern PATTERN = new PatternBuilder() + .text("$GPRMC,") + .number("(dd)(dd)(dd),") // time (hhmmss) + .expression("([AV]),") // validity + .number("(d+)(dd.d+),") // latitude + .expression("([NS]),") + .number("(d+)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+),") // speed + .number("(d+.d+),") // course + .number("(dd)(dd)(dd),") // date (ddmmyy) + .any() + .compile(); + + public OpenGtsProtocolDecoder(OpenGtsProtocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + HttpRequest request = (HttpRequest) msg; + QueryStringDecoder decoder = new QueryStringDecoder(request.getUri()); + Map<String, List<String>> params = decoder.getParameters(); + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + for (Map.Entry<String, List<String>> entry : params.entrySet()) { + String value = entry.getValue().get(0); + switch (entry.getKey()) { + case "id": + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + position.setDeviceId(deviceSession.getDeviceId()); + break; + case "gprmc": + Parser parser = new Parser(PATTERN, value); + if (!parser.matches()) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble(0)); + position.setCourse(parser.nextDouble(0)); + + dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + position.setTime(dateBuilder.getDate()); + break; + case "alt": + position.setAltitude(Double.parseDouble(value)); + break; + case "batt": + position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value)); + break; + default: + break; + } + } + + if (position.getDeviceId() != 0) { + sendResponse(channel, HttpResponseStatus.OK); + return position; + } else { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + } + +} diff --git a/src/org/traccar/protocol/Pt502FrameDecoder.java b/src/org/traccar/protocol/Pt502FrameDecoder.java index 252c8dd02..4d3e9b4d5 100644 --- a/src/org/traccar/protocol/Pt502FrameDecoder.java +++ b/src/org/traccar/protocol/Pt502FrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2018 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. @@ -20,6 +20,8 @@ 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; + public class Pt502FrameDecoder extends FrameDecoder { private static final int BINARY_HEADER = 5; @@ -28,26 +30,41 @@ public class Pt502FrameDecoder extends FrameDecoder { protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { - if (buf.readableBytes() < BINARY_HEADER) { + if (buf.readableBytes() < 10) { return null; } - if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf) { - buf.skipBytes(BINARY_HEADER); - } + if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf + && buf.toString(buf.readerIndex() + BINARY_HEADER, 4, StandardCharsets.US_ASCII).equals("$PHD")) { - int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r'); - if (index < 0) { - index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\n'); - } + int length = buf.getUnsignedShort(buf.readerIndex() + 3); + if (buf.readableBytes() >= length) { + buf.skipBytes(BINARY_HEADER); + ChannelBuffer result = buf.readBytes(length - BINARY_HEADER - 2); + buf.skipBytes(2); // line break + return result; + } + + } else { - if (index > 0) { - ChannelBuffer result = buf.readBytes(index - buf.readerIndex()); - while (buf.readable() - && (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n')) { - buf.skipBytes(1); + if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf) { + buf.skipBytes(BINARY_HEADER); } - return result; + + int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r'); + if (index < 0) { + index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\n'); + } + + if (index > 0) { + ChannelBuffer result = buf.readBytes(index - buf.readerIndex()); + while (buf.readable() + && (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n')) { + buf.skipBytes(1); + } + return result; + } + } return null; diff --git a/src/org/traccar/protocol/Pt502Protocol.java b/src/org/traccar/protocol/Pt502Protocol.java index 0116422c2..ba820a6a1 100644 --- a/src/org/traccar/protocol/Pt502Protocol.java +++ b/src/org/traccar/protocol/Pt502Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,6 @@ public class Pt502Protocol extends BaseProtocol { protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new Pt502FrameDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Pt502ProtocolEncoder()); pipeline.addLast("objectDecoder", new Pt502ProtocolDecoder(Pt502Protocol.this)); } diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java index bc7647744..76e7ee1bf 100644 --- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -1,5 +1,5 @@ /*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
* Copyright 2012 Luis Parada (luis.parada@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,11 @@ */
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.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -25,13 +28,14 @@ import org.traccar.helper.PatternBuilder; import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
private static final int MAX_CHUNK_SIZE = 960;
- private byte[] photo;
+ private ChannelBuffer photo;
public Pt502ProtocolDecoder(Pt502Protocol protocol) {
super(protocol);
@@ -85,25 +89,15 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { }
}
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private Position decodePosition(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN, (String) msg);
+ Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
Position position = new Position(getProtocolName());
-
- String type = parser.next();
-
- if (type.startsWith("PHO") && channel != null) {
- photo = new byte[Integer.parseInt(type.substring(3))];
- channel.write("#PHD0," + Math.min(photo.length, MAX_CHUNK_SIZE) + "\r\n");
- }
-
- position.set(Position.KEY_ALARM, decodeAlarm(type));
+ position.set(Position.KEY_ALARM, decodeAlarm(parser.next()));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -146,4 +140,71 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { return position;
}
+ private void requestPhotoFragment(Channel channel) {
+ if (channel != null) {
+ int offset = photo.writerIndex();
+ int size = Math.min(photo.writableBytes(), MAX_CHUNK_SIZE);
+ channel.write("#PHD" + offset + "," + size + "\r\n");
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ int typeEndIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
+ String type = buf.toString(buf.readerIndex(), typeEndIndex - buf.readerIndex(), StandardCharsets.US_ASCII);
+
+ if (type.startsWith("$PHD")) {
+
+ int dataIndex = buf.indexOf(typeEndIndex + 1, buf.writerIndex(), (byte) ',') + 1;
+ buf.readerIndex(dataIndex);
+
+ if (photo != null) {
+
+ photo.writeBytes(buf.readBytes(buf.readableBytes()));
+
+ if (photo.writableBytes() > 0) {
+
+ requestPhotoFragment(channel);
+
+ } else {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+
+ photo = null;
+
+ return position;
+
+ }
+
+ }
+
+ } else {
+
+ if (type.startsWith("$PHO")) {
+ int size = Integer.parseInt(type.split("-")[0].substring(4));
+ if (size > 0) {
+ photo = ChannelBuffers.buffer(size);
+ requestPhotoFragment(channel);
+ }
+ }
+
+ return decodePosition(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+
+ }
+
+ return null;
+ }
+
}
diff --git a/src/org/traccar/protocol/Pt60Protocol.java b/src/org/traccar/protocol/Pt60Protocol.java new file mode 100644 index 000000000..857790efd --- /dev/null +++ b/src/org/traccar/protocol/Pt60Protocol.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 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.CharacterDelimiterFrameDecoder; +import org.traccar.TrackerServer; + +import java.util.List; + +public class Pt60Protocol extends BaseProtocol { + + public Pt60Protocol() { + super("pt60"); + } + + @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(1024, "@R#@")); + pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new Pt60ProtocolDecoder(Pt60Protocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/Pt60ProtocolDecoder.java b/src/org/traccar/protocol/Pt60ProtocolDecoder.java new file mode 100644 index 000000000..c87c22c5f --- /dev/null +++ b/src/org/traccar/protocol/Pt60ProtocolDecoder.java @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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.model.Position; + +import java.net.SocketAddress; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.regex.Pattern; + +public class Pt60ProtocolDecoder extends BaseProtocolDecoder { + + public Pt60ProtocolDecoder(Pt60Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("@G#@,") // header + .number("Vdd,") // protocol version + .number("d,") // type + .number("(d+),") // imei + .number("(d+),") // imsi + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+);") // latitude + .number("(-?d+.d+),") // longitude + .compile(); + + private void sendResponse(Channel channel) { + if (channel != null) { + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + channel.write("@G#@,V01,38," + dateFormat.format(new Date()) + ",@R#@"); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + sendResponse(channel); + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next(), parser.next()); + if (deviceSession == null) { + return null; + } + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(parser.nextDateTime()); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + + return position; + } + +} diff --git a/src/org/traccar/protocol/RoboTrackFrameDecoder.java b/src/org/traccar/protocol/RoboTrackFrameDecoder.java new file mode 100644 index 000000000..af215103c --- /dev/null +++ b/src/org/traccar/protocol/RoboTrackFrameDecoder.java @@ -0,0 +1,57 @@ +/* + * Copyright 2018 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 RoboTrackFrameDecoder extends FrameDecoder { + + private int messageLength(ChannelBuffer buf) { + switch ((int) buf.getByte(buf.readerIndex())) { + case RoboTrackProtocolDecoder.MSG_ID: + return 69; + case RoboTrackProtocolDecoder.MSG_ACK: + return 3; + case RoboTrackProtocolDecoder.MSG_GPS: + case RoboTrackProtocolDecoder.MSG_GSM: + case RoboTrackProtocolDecoder.MSG_IMAGE_START: + return 24; + case RoboTrackProtocolDecoder.MSG_IMAGE_DATA: + return 8 + buf.getUnsignedShort(buf.readerIndex() + 1); + case RoboTrackProtocolDecoder.MSG_IMAGE_END: + return 6; + default: + return Integer.MAX_VALUE; + } + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + int length = messageLength(buf); + + if (buf.readableBytes() >= length) { + return buf.readBytes(length); + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/RoboTrackProtocol.java b/src/org/traccar/protocol/RoboTrackProtocol.java new file mode 100644 index 000000000..382cb1c2f --- /dev/null +++ b/src/org/traccar/protocol/RoboTrackProtocol.java @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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.TrackerServer; + +import java.nio.ByteOrder; +import java.util.List; + +public class RoboTrackProtocol extends BaseProtocol { + + public RoboTrackProtocol() { + super("robotrack"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new RoboTrackFrameDecoder()); + pipeline.addLast("objectDecoder", new RoboTrackProtocolDecoder(RoboTrackProtocol.this)); + } + }; + server.setEndianness(ByteOrder.LITTLE_ENDIAN); + serverList.add(server); + } + +} diff --git a/src/org/traccar/protocol/RoboTrackProtocolDecoder.java b/src/org/traccar/protocol/RoboTrackProtocolDecoder.java new file mode 100644 index 000000000..4f27fb08e --- /dev/null +++ b/src/org/traccar/protocol/RoboTrackProtocolDecoder.java @@ -0,0 +1,130 @@ +/* + * Copyright 2018 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.BitUtil; +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.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public class RoboTrackProtocolDecoder extends BaseProtocolDecoder { + + public RoboTrackProtocolDecoder(RoboTrackProtocol protocol) { + super(protocol); + } + + public static final int MSG_ID = 0x00; + public static final int MSG_ACK = 0x80; + public static final int MSG_GPS = 0x03; + public static final int MSG_GSM = 0x04; + public static final int MSG_IMAGE_START = 0x06; + public static final int MSG_IMAGE_DATA = 0x07; + public static final int MSG_IMAGE_END = 0x08; + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + int type = buf.readUnsignedByte(); + + if (type == MSG_ID) { + + buf.skipBytes(16); // name + + String imei = buf.readBytes(15).toString(StandardCharsets.US_ASCII); + + if (getDeviceSession(channel, remoteAddress, imei) != null && channel != null) { + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0); + response.writeByte(MSG_ACK); + response.writeByte(0x01); // success + response.writeByte(0x66); // checksum + channel.write(response); + } + + } else if (type == MSG_GPS || type == MSG_GSM) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000)); + + if (type == MSG_GPS) { + + position.setValid(true); + position.setFixTime(position.getDeviceTime()); + position.setLatitude(buf.readInt() * 0.000001); + position.setLongitude(buf.readInt() * 0.000001); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readByte())); + + } else { + + getLastLocation(position, position.getDeviceTime()); + + position.setNetwork(new Network(CellTower.from( + buf.readUnsignedShort(), buf.readUnsignedShort(), + buf.readUnsignedShort(), buf.readUnsignedShort()))); + + buf.readUnsignedByte(); // reserved + + } + + int value = buf.readUnsignedByte(); + + position.set(Position.KEY_SATELLITES, BitUtil.to(value, 4)); + position.set(Position.KEY_RSSI, BitUtil.between(value, 4, 7)); + position.set(Position.KEY_MOTION, BitUtil.check(value, 7)); + + value = buf.readUnsignedByte(); + + position.set(Position.KEY_CHARGE, BitUtil.check(value, 0)); + + for (int i = 1; i <= 4; i++) { + position.set(Position.PREFIX_IN + i, BitUtil.check(value, i)); + } + + position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(value, 5) * 100 / 7); + position.set(Position.KEY_DEVICE_TEMP, buf.readByte()); + + for (int i = 1; i <= 3; i++) { + position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort()); + } + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/org/traccar/protocol/RuptelaProtocolDecoder.java index 87ee8246a..7b11cc5c3 100644 --- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java +++ b/src/org/traccar/protocol/RuptelaProtocolDecoder.java @@ -20,10 +20,10 @@ import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.xml.bind.DatatypeConverter; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -196,7 +196,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { } if (channel != null) { - channel.write(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary("0002640113bc"))); + channel.write(ChannelBuffers.wrappedBuffer(DataConverter.parseHex("0002640113bc"))); } return positions; @@ -229,7 +229,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { } if (channel != null) { - channel.write(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary("00026d01c4a4"))); + channel.write(ChannelBuffers.wrappedBuffer(DataConverter.parseHex("00026d01c4a4"))); } return positions; diff --git a/src/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/org/traccar/protocol/SigfoxProtocolDecoder.java index 65ab2cc55..b454e00fa 100644 --- a/src/org/traccar/protocol/SigfoxProtocolDecoder.java +++ b/src/org/traccar/protocol/SigfoxProtocolDecoder.java @@ -22,12 +22,12 @@ import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import javax.json.Json; import javax.json.JsonObject; -import javax.xml.bind.DatatypeConverter; import java.io.StringReader; import java.net.SocketAddress; import java.net.URLDecoder; @@ -61,7 +61,7 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder { position.setTime(new Date(json.getInt("time") * 1000L)); ChannelBuffer buf = ChannelBuffers.wrappedBuffer( - ByteOrder.LITTLE_ENDIAN, DatatypeConverter.parseHexBinary(json.getString("data"))); + ByteOrder.LITTLE_ENDIAN, DataConverter.parseHex(json.getString("data"))); int type = buf.readUnsignedByte() >> 4; if (type == 0) { diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java index 58e670307..b739e699b 100644 --- a/src/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java @@ -21,6 +21,8 @@ import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; import org.traccar.model.Position; import java.net.SocketAddress; @@ -156,7 +158,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } } - private Position decode235( + private Position decode2356( Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException { int index = 0; @@ -175,7 +177,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); position.set(Position.KEY_TYPE, type); - if (protocol.equals("ST300") || protocol.equals("ST500")) { + if (protocol.equals("ST300") || protocol.equals("ST500") || protocol.equals("ST600")) { index += 1; // model } @@ -186,7 +188,12 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setTime(dateFormat.parse(values[index++] + values[index++])); if (!protocol.equals("ST500")) { - index += 1; // cell + int cid = Integer.parseInt(values[index++], 16); + if (protocol.equals("ST600")) { + position.setNetwork(new Network(CellTower.from( + Integer.parseInt(values[index++]), Integer.parseInt(values[index++]), + Integer.parseInt(values[index++], 16), cid, Integer.parseInt(values[index++])))); + } } position.setLatitude(Double.parseDouble(values[index++])); @@ -369,7 +376,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } else if (values[0].equals("ST910")) { return decode9(channel, remoteAddress, values); } else { - return decode235(channel, remoteAddress, values[0].substring(0, 5), values); + return decode2356(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 dbc467993..be3cb5f67 100644 --- a/src/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/org/traccar/protocol/T55ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2018 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. @@ -250,9 +250,11 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } else if (sentence.startsWith("$PCPTI")) { getDeviceSession(channel, remoteAddress, sentence.substring(7, sentence.indexOf(",", 7))); } else if (sentence.startsWith("IMEI")) { - getDeviceSession(channel, remoteAddress, sentence.substring(5, sentence.length())); + getDeviceSession(channel, remoteAddress, sentence.substring(5)); + } else if (sentence.startsWith("$IMEI")) { + getDeviceSession(channel, remoteAddress, sentence.substring(6)); } else if (sentence.startsWith("$GPFID")) { - deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(7, sentence.length())); + deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(7)); if (deviceSession != null && position != null) { Position position = this.position; position.setDeviceId(deviceSession.getDeviceId()); diff --git a/src/org/traccar/protocol/T800xProtocolEncoder.java b/src/org/traccar/protocol/T800xProtocolEncoder.java index 6ed5dbccd..038a5e51a 100644 --- a/src/org/traccar/protocol/T800xProtocolEncoder.java +++ b/src/org/traccar/protocol/T800xProtocolEncoder.java @@ -18,10 +18,10 @@ package org.traccar.protocol; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.traccar.BaseProtocolEncoder; +import org.traccar.helper.DataConverter; import org.traccar.helper.Log; import org.traccar.model.Command; -import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; public class T800xProtocolEncoder extends BaseProtocolEncoder { @@ -39,7 +39,7 @@ public class T800xProtocolEncoder extends BaseProtocolEncoder { buf.writeByte(T800xProtocolDecoder.MSG_COMMAND); buf.writeShort(7 + 8 + 1 + content.length()); buf.writeShort(1); // serial number - buf.writeBytes(DatatypeConverter.parseHexBinary("0" + getUniqueId(command.getDeviceId()))); + buf.writeBytes(DataConverter.parseHex("0" + getUniqueId(command.getDeviceId()))); buf.writeByte(MODE_SETTING); buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII)); diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/org/traccar/protocol/TaipProtocolDecoder.java index 9c98799fb..a7aa9dd96 100644 --- a/src/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/org/traccar/protocol/TaipProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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. @@ -47,7 +47,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .groupEnd("?") .number("(d{5})") // seconds .or() - .expression("(?:RGP|RCQ|RBR)") // type + .expression("(?:RGP|RCQ|RCV|RBR)") // type .number("(dd)?") // event .number("(dd)(dd)(dd)") // date (mmddyy) .number("(dd)(dd)(dd)") // time (hhmmss) @@ -62,12 +62,33 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .number("(ddd)") // speed .number("(ddd)") // course .groupBegin() + .number("([023])") // fix mode + .number("xx") // data age + .number("(xx)") // input + .number("(dd)") // event + .number("(dd)") // hdop + .or() + .groupBegin() .number("(xx)") // input .number("(xx)") // satellites .number("(ddd)") // battery .number("(x{8})") // odometer .number("[01]") // gps power + .groupBegin() + .number("([023])") // fix mode + .number("(dd)") // pdop + .number("dd") // satellites + .number("xxxx") // data age + .number("[01]") // modem power + .number("[0-5]") // gsm status + .number("(dd)") // rssi + .number("([-+]dddd)") // temperature 1 + .number("xx") // seconds from last + .number("([-+]dddd)") // temperature 2 + .number("xx") // seconds from last + .groupEnd("?") .groupEnd("?") + .groupEnd() .any() .compile(); @@ -103,6 +124,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); + Boolean valid = null; Integer event = null; if (parser.hasNext(3)) { @@ -116,27 +138,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { 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)) { position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); } @@ -154,13 +155,51 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.setCourse(parser.nextDouble(0)); if (parser.hasNext(4)) { + valid = parser.nextInt() > 0; + int input = parser.nextHexInt(); + position.set(Position.KEY_IGNITION, BitUtil.check(input, 7)); + position.set(Position.KEY_INPUT, input); + event = parser.nextInt(); + position.set(Position.KEY_HDOP, parser.nextInt()); + } + + if (parser.hasNext(4)) { position.set(Position.KEY_INPUT, parser.nextHexInt(0)); position.set(Position.KEY_SATELLITES, parser.nextHexInt(0)); position.set(Position.KEY_BATTERY, parser.nextInt(0)); position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0)); } - position.setValid(true); + if (parser.hasNext(4)) { + valid = parser.nextInt() > 0; + position.set(Position.KEY_PDOP, parser.nextInt()); + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextInt() * 0.01); + position.set(Position.PREFIX_TEMP + 2, parser.nextInt() * 0.01); + } + + position.setValid(valid != null ? valid : true); + + if (event != null) { + position.set(Position.KEY_EVENT, event); + 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: + break; + } + } String[] attributes = null; beginIndex = sentence.indexOf(';'); @@ -230,14 +269,13 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { if (deviceSession != null) { if (channel != null) { if (messageIndex != null) { - String response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";*"; + String response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*"; response += String.format("%02X", Checksum.xor(response)) + "<"; channel.write(response, remoteAddress); } else { channel.write(uniqueId, remoteAddress); } } - return position; } diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java index 9e249247a..d2069e6c9 100644 --- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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. @@ -133,7 +133,10 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1); break; case 78: - position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", readValue(buf, length, false))); + long driverUniqueId = readValue(buf, length, false); + if (driverUniqueId != 0) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId)); + } break; case 80: position.set("workMode", readValue(buf, length, false)); diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java index 1c5130a6c..5a2c203d2 100644 --- a/src/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/org/traccar/protocol/TotemProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2018 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,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.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -131,7 +132,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN4 = new PatternBuilder() .text("$$") // header .number("dddd") // length - .expression("A[ABC]") // type + .number("(xx)") // type .number("(d+)|") // imei .number("(x{8})") // status .number("(dd)(dd)(dd)") // date (yymmdd) @@ -163,30 +164,16 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private String decodeAlarm(Short value) { + private String decodeAlarm123(int value) { switch (value) { case 0x01: return Position.ALARM_SOS; - case 0x02: - return Position.ALARM_OVERSPEED; - case 0x04: - return Position.ALARM_GEOFENCE_EXIT; - case 0x05: - return Position.ALARM_GEOFENCE_ENTER; - case 0x06: - return Position.ALARM_TOW; case 0x10: return Position.ALARM_LOW_BATTERY; case 0x11: return Position.ALARM_OVERSPEED; - case 0x12: - return Position.ALARM_LOW_POWER; - case 0x13: - return Position.ALARM_LOW_BATTERY; case 0x30: return Position.ALARM_PARKING; - case 0x40: - return Position.ALARM_SHOCK; case 0x42: return Position.ALARM_GEOFENCE_EXIT; case 0x43: @@ -196,10 +183,31 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } + private String decodeAlarm4(int value) { + switch (value) { + case 0x01: + return Position.ALARM_SOS; + case 0x02: + return Position.ALARM_OVERSPEED; + case 0x04: + return Position.ALARM_GEOFENCE_EXIT; + case 0x05: + return Position.ALARM_GEOFENCE_ENTER; + case 0x40: + return Position.ALARM_SHOCK; + case 0x42: + return Position.ALARM_ACCELERATION; + case 0x43: + return Position.ALARM_BRAKING; + default: + return null; + } + } + private boolean decode12(Position position, Parser parser, Pattern pattern) { if (parser.hasNext()) { - position.set(Position.KEY_ALARM, decodeAlarm(Short.parseShort(parser.next(), 16))); + position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); } DateBuilder dateBuilder = new DateBuilder(); int year = 0, month = 0, day = 0; @@ -235,12 +243,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HDOP, parser.nextDouble()); } - position.set(Position.PREFIX_IO + 1, parser.next()); + int io = parser.nextBinInt(); if (pattern == PATTERN1) { + for (int i = 1; i <= 4; i++) { + position.set(Position.PREFIX_IN + i, BitUtil.check(io, 3 + i)); + } position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.01); } else { + position.set(Position.KEY_ANTENNA, BitUtil.check(io, 0)); + position.set(Position.KEY_CHARGE, BitUtil.check(io, 1)); + for (int i = 1; i <= 6; i++) { + position.set(Position.PREFIX_IN + i, BitUtil.check(io, 1 + i)); + } position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.1); } + for (int i = 1; i <= 4; i++) { + position.set(Position.PREFIX_OUT + i, BitUtil.check(io, 7 + i)); + } position.set(Position.KEY_POWER, parser.nextDouble(0)); position.set(Position.PREFIX_ADC + 1, parser.next()); @@ -259,11 +278,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { private boolean decode3(Position position, Parser parser) { if (parser.hasNext()) { - short alarm = Short.parseShort(parser.next(), 16); - position.set(Position.KEY_ALARM, decodeAlarm(alarm)); - if (alarm >= 0x21 && alarm <= 0x28) { - position.set(Position.PREFIX_IN + ((alarm - 0x21) / 2 + 1), alarm % 2 > 0); - } + position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); } position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); @@ -294,7 +309,26 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { private boolean decode4(Position position, Parser parser) { - position.set(Position.KEY_STATUS, parser.next()); + long status = parser.nextHexLong(); + + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 1) ? Position.ALARM_SOS : null); + position.set(Position.KEY_IGNITION, BitUtil.check(status, 32 - 2)); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 3) ? Position.ALARM_OVERSPEED : null); + position.set(Position.KEY_CHARGE, BitUtil.check(status, 32 - 4)); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 5) ? Position.ALARM_GEOFENCE_EXIT : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 6) ? Position.ALARM_GEOFENCE_ENTER : null); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 32 - 9)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 32 - 10)); + position.set(Position.PREFIX_OUT + 3, BitUtil.check(status, 32 - 11)); + position.set(Position.PREFIX_OUT + 4, BitUtil.check(status, 32 - 12)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(status, 32 - 13)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(status, 32 - 14)); + position.set(Position.PREFIX_IN + 4, BitUtil.check(status, 32 - 15)); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 16) ? Position.ALARM_SHOCK : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 18) ? Position.ALARM_LOW_BATTERY : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 22) ? Position.ALARM_JAMMING : null); + + position.setValid(BitUtil.check(status, 32 - 20)); position.setTime(parser.nextDateTime()); @@ -318,7 +352,6 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HDOP, parser.nextDouble(0)); position.set(Position.KEY_ODOMETER, parser.nextInt(0) * 1000); - position.setValid(true); position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); @@ -349,6 +382,10 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); + if (pattern == PATTERN4) { + position.set(Position.KEY_ALARM, decodeAlarm4(parser.nextHexInt())); + } + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; diff --git a/src/org/traccar/protocol/WatchFrameDecoder.java b/src/org/traccar/protocol/WatchFrameDecoder.java index 826a8b4d0..0009ef30f 100644 --- a/src/org/traccar/protocol/WatchFrameDecoder.java +++ b/src/org/traccar/protocol/WatchFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2018 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. @@ -25,47 +25,66 @@ import java.nio.charset.StandardCharsets; public class WatchFrameDecoder extends FrameDecoder { - public static final int MESSAGE_HEADER = 20; - @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { - if (buf.readableBytes() >= MESSAGE_HEADER) { - ChannelBuffer lengthBuffer = ChannelBuffers.dynamicBuffer(); - buf.getBytes(buf.readerIndex() + MESSAGE_HEADER - 4 - 1, lengthBuffer, 4); - int length = Integer.parseInt(lengthBuffer.toString(StandardCharsets.US_ASCII), 16) + MESSAGE_HEADER + 1; - if (buf.readableBytes() >= length) { - ChannelBuffer frame = ChannelBuffers.dynamicBuffer(); - int endIndex = buf.readerIndex() + length; - while (buf.readerIndex() < endIndex) { - byte b = buf.readByte(); - if (b == 0x7D) { - switch (buf.readByte()) { - case 0x01: - frame.writeByte(0x7D); - break; - case 0x02: - frame.writeByte(0x5B); - break; - case 0x03: - frame.writeByte(0x5D); - break; - case 0x04: - frame.writeByte(0x2C); - break; - case 0x05: - frame.writeByte(0x2A); - break; - default: - throw new IllegalArgumentException(); - } - } else { - frame.writeByte(b); + int idIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*') + 1; + if (idIndex <= 0) { + return null; + } + + int lengthIndex = buf.indexOf(idIndex, buf.writerIndex(), (byte) '*') + 1; + if (lengthIndex <= 0) { + return null; + } + + int payloadIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) '*'); + if (payloadIndex < 0) { + return null; + } + + if (payloadIndex + 5 < buf.writerIndex() && buf.getByte(payloadIndex + 5) == '*' + && buf.toString(payloadIndex + 1, 4, StandardCharsets.US_ASCII).matches("\\p{XDigit}+")) { + lengthIndex = payloadIndex + 1; + payloadIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) '*'); + if (payloadIndex < 0) { + return null; + } + } + + int length = Integer.parseInt( + buf.toString(lengthIndex, payloadIndex - lengthIndex, StandardCharsets.US_ASCII), 16); + if (buf.readableBytes() >= payloadIndex + 1 + length + 1) { + ChannelBuffer frame = ChannelBuffers.dynamicBuffer(); + int endIndex = buf.readerIndex() + payloadIndex + 1 + length + 1; + while (buf.readerIndex() < endIndex) { + byte b = buf.readByte(); + if (b == 0x7D) { + switch (buf.readByte()) { + case 0x01: + frame.writeByte(0x7D); + break; + case 0x02: + frame.writeByte(0x5B); + break; + case 0x03: + frame.writeByte(0x5D); + break; + case 0x04: + frame.writeByte(0x2C); + break; + case 0x05: + frame.writeByte(0x2A); + break; + default: + throw new IllegalArgumentException(); } + } else { + frame.writeByte(b); } - return frame; } + return frame; } return null; diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java index fe62874b5..b58c3b6a1 100644 --- a/src/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/org/traccar/protocol/WatchProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 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. @@ -48,7 +48,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .expression("([NS]),") .number(" *(-?d+.d+),") // longitude .expression("([EW])?,") - .number("(d+.d+),") // speed + .number("(d+.?d*),") // speed .number("(d+.?d*),") // course .number("(d+.?d*),") // altitude .number("(d+),") // satellites @@ -60,10 +60,15 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .expression("(.*)") // cell and wifi .compile(); - private void sendResponse(Channel channel, String manufacturer, String id, String content) { + private void sendResponse(Channel channel, String id, String index, String content) { if (channel != null) { - channel.write(String.format( - "[%s*%s*%04x*%s]", manufacturer, id, content.length(), content)); + if (index != null) { + channel.write(String.format( + "[%s*%s*%s*%04x*%s]", manufacturer, id, index, content.length(), content)); + } else { + channel.write(String.format( + "[%s*%s*%04x*%s]", manufacturer, id, content.length(), content)); + } } } @@ -92,8 +97,38 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { return null; } - private void decodeTail(Position position, String data) { - String[] values = data.split(","); + private Position decodePosition(DeviceSession deviceSession, String data) { + + Parser parser = new Parser(PATTERN_POSITION, data); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); + position.setCourse(parser.nextDouble(0)); + position.setAltitude(parser.nextDouble(0)); + + position.set(Position.KEY_SATELLITES, parser.nextInt(0)); + position.set(Position.KEY_RSSI, parser.nextInt(0)); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + + position.set(Position.KEY_STEPS, parser.nextInt(0)); + + int status = parser.nextHexInt(0); + position.set(Position.KEY_ALARM, decodeAlarm(status)); + if (BitUtil.check(status, 4)) { + position.set(Position.KEY_MOTION, true); + } + + String[] values = parser.next().split(","); int index = 0; Network network = new Network(); @@ -122,6 +157,19 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { position.setNetwork(network); } + + return position; + } + + private boolean hasIndex; + private String manufacturer; + + public boolean getHasIndex() { + return hasIndex; + } + + public String getManufacturer() { + return manufacturer; } @Override @@ -131,22 +179,34 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { ChannelBuffer buf = (ChannelBuffer) msg; buf.skipBytes(1); // header - String manufacturer = buf.readBytes(2).toString(StandardCharsets.US_ASCII); + manufacturer = buf.readBytes(2).toString(StandardCharsets.US_ASCII); buf.skipBytes(1); // delimiter - String id = buf.readBytes(10).toString(StandardCharsets.US_ASCII); + int idIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*'); + String id = buf.readBytes(idIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); if (deviceSession == null) { return null; } buf.skipBytes(1); // delimiter + + String index = null; + int contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*'); + if (contentIndex + 5 < buf.writerIndex() && buf.getByte(contentIndex + 5) == '*' + && buf.toString(contentIndex + 1, 4, StandardCharsets.US_ASCII).matches("\\p{XDigit}+")) { + int indexLength = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*') - buf.readerIndex(); + hasIndex = true; + index = buf.readBytes(indexLength).toString(StandardCharsets.US_ASCII); + buf.skipBytes(1); // delimiter + } + buf.skipBytes(4); // length buf.skipBytes(1); // delimiter buf.writerIndex(buf.writerIndex() - 1); // ignore ending - int contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ','); + contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ','); if (contentIndex < 0) { contentIndex = buf.writerIndex(); } @@ -157,9 +217,13 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { buf.readerIndex(contentIndex + 1); } - if (type.equals("LK")) { + if (type.equals("INIT")) { + + sendResponse(channel, id, index, "INIT,1"); + + } else if (type.equals("LK")) { - sendResponse(channel, manufacturer, id, "LK"); + sendResponse(channel, id, index, "LK"); if (buf.readable()) { String[] values = buf.toString(StandardCharsets.US_ASCII).split(","); @@ -178,48 +242,22 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { } else if (type.equals("UD") || type.equals("UD2") || type.equals("UD3") || type.equals("AL") || type.equals("WT")) { - if (type.equals("AL")) { - sendResponse(channel, manufacturer, id, "AL"); - } - - Parser parser = new Parser(PATTERN_POSITION, buf.toString(StandardCharsets.US_ASCII)); - if (!parser.matches()) { - return null; - } - - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); - - position.setValid(parser.next().equals("A")); - position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); - position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); - - position.set(Position.KEY_SATELLITES, parser.nextInt(0)); - position.set(Position.KEY_RSSI, parser.nextInt(0)); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); + Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII)); - position.set(Position.KEY_STEPS, parser.nextInt(0)); - - int status = parser.nextHexInt(0); - position.set(Position.KEY_ALARM, decodeAlarm(status)); - if (BitUtil.check(status, 4)) { - position.set(Position.KEY_MOTION, true); + if (type.equals("AL")) { + if (position != null) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + sendResponse(channel, id, index, "AL"); } - decodeTail(position, parser.next()); - return position; } else if (type.equals("TKQ")) { - sendResponse(channel, manufacturer, id, "TKQ"); + sendResponse(channel, id, index, "TKQ"); - } else if (type.equals("PULSE") || type.equals("heart")) { + } else if (type.equals("PULSE") || type.equals("heart") || type.equals("bphrt")) { if (buf.readable()) { @@ -228,10 +266,14 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, new Date()); - position.setValid(false); - String pulse = buf.toString(StandardCharsets.US_ASCII); - position.set("pulse", pulse); - position.set(Position.KEY_RESULT, pulse); + String[] values = buf.toString(StandardCharsets.US_ASCII).split(","); + int valueIndex = 0; + + if (type.equals("bphrt")) { + position.set("pressureHigh", values[valueIndex++]); + position.set("pressureLow", values[valueIndex++]); + } + position.set("pulse", values[valueIndex]); return position; diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java index d2d3b52d1..4c87f3abd 100644 --- a/src/org/traccar/protocol/WatchProtocolEncoder.java +++ b/src/org/traccar/protocol/WatchProtocolEncoder.java @@ -15,11 +15,12 @@ */ package org.traccar.protocol; +import org.jboss.netty.channel.Channel; import org.traccar.StringProtocolEncoder; +import org.traccar.helper.DataConverter; import org.traccar.helper.Log; import org.traccar.model.Command; -import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -41,12 +42,27 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin return null; } + protected String formatCommand(Channel channel, Command command, String format, String... keys) { + + boolean hasIndex = false; + String manufacturer = "CS"; + if (channel != null) { + WatchProtocolDecoder decoder = channel.getPipeline().get(WatchProtocolDecoder.class); + if (decoder != null) { + hasIndex = decoder.getHasIndex(); + manufacturer = decoder.getManufacturer(); + } + } - @Override - protected String formatCommand(Command command, String format, String... keys) { String content = formatCommand(command, format, this, keys); - return String.format("[CS*%s*%04x*%s]", - getUniqueId(command.getDeviceId()), content.length(), content); + + if (hasIndex) { + return String.format("[%s*%s*0001*%04x*%s]", + manufacturer, getUniqueId(command.getDeviceId()), content.length(), content); + } else { + return String.format("[%s*%s*%04x*%s]", + manufacturer, getUniqueId(command.getDeviceId()), content.length(), content); + } } private int getEnableFlag(Command command) { @@ -68,7 +84,7 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin } private String getBinaryData(Command command) { - byte[] data = DatatypeConverter.parseHexBinary(command.getString(Command.KEY_DATA)); + byte[] data = DataConverter.parseHex(command.getString(Command.KEY_DATA)); int encodedLength = data.length; for (byte b : data) { @@ -96,37 +112,37 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin } @Override - protected Object encodeCommand(Command command) { + protected Object encodeCommand(Channel channel, Command command) { switch (command.getType()) { case Command.TYPE_CUSTOM: - return formatCommand(command, command.getString(Command.KEY_DATA)); + return formatCommand(channel, command, command.getString(Command.KEY_DATA)); case Command.TYPE_POSITION_SINGLE: - return formatCommand(command, "RG"); + return formatCommand(channel, command, "RG"); case Command.TYPE_SOS_NUMBER: - return formatCommand(command, "SOS{%s},{%s}", Command.KEY_INDEX, Command.KEY_PHONE); + return formatCommand(channel, command, "SOS{%s},{%s}", Command.KEY_INDEX, Command.KEY_PHONE); case Command.TYPE_ALARM_SOS: - return formatCommand(command, "SOSSMS," + getEnableFlag(command)); + return formatCommand(channel, command, "SOSSMS," + getEnableFlag(command)); case Command.TYPE_ALARM_BATTERY: - return formatCommand(command, "LOWBAT," + getEnableFlag(command)); + return formatCommand(channel, command, "LOWBAT," + getEnableFlag(command)); case Command.TYPE_REBOOT_DEVICE: - return formatCommand(command, "RESET"); + return formatCommand(channel, command, "RESET"); case Command.TYPE_ALARM_REMOVE: - return formatCommand(command, "REMOVE," + getEnableFlag(command)); + return formatCommand(channel, command, "REMOVE," + getEnableFlag(command)); case Command.TYPE_SILENCE_TIME: - return formatCommand(command, "SILENCETIME,{%s}", Command.KEY_DATA); + return formatCommand(channel, command, "SILENCETIME,{%s}", Command.KEY_DATA); case Command.TYPE_ALARM_CLOCK: - return formatCommand(command, "REMIND,{%s}", Command.KEY_DATA); + return formatCommand(channel, command, "REMIND,{%s}", Command.KEY_DATA); case Command.TYPE_SET_PHONEBOOK: - return formatCommand(command, "PHB,{%s}", Command.KEY_DATA); + return formatCommand(channel, command, "PHB,{%s}", Command.KEY_DATA); case Command.TYPE_VOICE_MESSAGE: - return formatCommand(command, "TK," + getBinaryData(command)); + return formatCommand(channel, command, "TK," + getBinaryData(command)); case Command.TYPE_POSITION_PERIODIC: - return formatCommand(command, "UPLOAD,{%s}", Command.KEY_FREQUENCY); + return formatCommand(channel, command, "UPLOAD,{%s}", Command.KEY_FREQUENCY); case Command.TYPE_SET_TIMEZONE: - return formatCommand(command, "LZ,,{%s}", Command.KEY_TIMEZONE); + return formatCommand(channel, command, "LZ,,{%s}", Command.KEY_TIMEZONE); case Command.TYPE_SET_INDICATOR: - return formatCommand(command, "FLOWER,{%s}", Command.KEY_DATA); + return formatCommand(channel, command, "FLOWER,{%s}", Command.KEY_DATA); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/XirgoProtocolDecoder.java b/src/org/traccar/protocol/XirgoProtocolDecoder.java index 10dd298fd..461503af1 100644 --- a/src/org/traccar/protocol/XirgoProtocolDecoder.java +++ b/src/org/traccar/protocol/XirgoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2018 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. @@ -95,6 +95,71 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private void decodeEvent(Position position, int event) { + + position.set(Position.KEY_EVENT, event); + + switch (event) { + case 4001: + case 4003: + case 6011: + case 6013: + position.set(Position.KEY_IGNITION, true); + break; + case 4002: + case 4004: + case 6012: + case 6014: + position.set(Position.KEY_IGNITION, false); + break; + case 4005: + position.set(Position.KEY_CHARGE, false); + break; + case 6002: + position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); + break; + case 6006: + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 6007: + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 6008: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + case 6009: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case 6010: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED); + break; + case 6016: + position.set(Position.KEY_ALARM, Position.ALARM_IDLE); + break; + case 6017: + position.set(Position.KEY_ALARM, Position.ALARM_TOW); + break; + case 6030: + case 6071: + position.set(Position.KEY_MOTION, true); + break; + case 6031: + position.set(Position.KEY_MOTION, false); + break; + case 6032: + position.set(Position.KEY_ALARM, Position.ALARM_PARKING); + break; + case 6090: + position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); + break; + case 6091: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + break; + default: + break; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -133,7 +198,7 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(deviceSession.getDeviceId()); - position.set(Position.KEY_EVENT, parser.next()); + decodeEvent(position, parser.nextInt()); position.setTime(parser.nextDateTime()); diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java index 72be473c1..1be943e98 100644 --- a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -20,10 +20,10 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.xml.bind.DatatypeConverter; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -95,7 +95,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+\\] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)"); Matcher matcher = pattern.matcher(configString); while (matcher.find()) { - formats.put(Short.parseShort(matcher.group(1), 16), DatatypeConverter.parseHexBinary(matcher.group(2))); + formats.put(Short.parseShort(matcher.group(1), 16), DataConverter.parseHex(matcher.group(2))); } } |