diff options
Diffstat (limited to 'src/org')
-rw-r--r-- | src/org/traccar/protocol/EelinkProtocolDecoder.java | 2 | ||||
-rw-r--r-- | src/org/traccar/protocol/Jt600ProtocolDecoder.java | 8 | ||||
-rw-r--r-- | src/org/traccar/protocol/Tk103FrameDecoder.java | 75 | ||||
-rw-r--r-- | src/org/traccar/protocol/Tk103Protocol.java | 11 | ||||
-rw-r--r-- | src/org/traccar/protocol/Tk103ProtocolDecoder.java | 173 | ||||
-rw-r--r-- | src/org/traccar/protocol/Tk103ProtocolEncoder.java | 52 | ||||
-rw-r--r-- | src/org/traccar/protocol/TmgProtocolDecoder.java | 15 |
7 files changed, 317 insertions, 19 deletions
diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java index 159a93d9b..98a9f7d6d 100644 --- a/src/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java @@ -190,6 +190,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); position.setCourse(buf.readUnsignedShort()); position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + } else { + getLastLocation(position, position.getDeviceTime()); } if (BitUtil.check(flags, 1)) { diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java index 58835c7d6..e935c1449 100644 --- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -63,8 +63,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { return null; } + int protocolVersion = 0; if (longFormat) { - buf.readUnsignedByte(); // protocol + protocolVersion = buf.readUnsignedByte(); } int version = BitUtil.from(buf.readUnsignedByte(), 4); @@ -122,6 +123,11 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { cellTower.setSignalStrength((int) buf.readUnsignedByte()); position.setNetwork(new Network(cellTower)); + if (protocolVersion == 0x17) { + buf.readUnsignedByte(); // geofence id + buf.skipBytes(3); // reserved + } + } else if (version == 1) { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); diff --git a/src/org/traccar/protocol/Tk103FrameDecoder.java b/src/org/traccar/protocol/Tk103FrameDecoder.java new file mode 100644 index 000000000..390e38906 --- /dev/null +++ b/src/org/traccar/protocol/Tk103FrameDecoder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017 Valerii Vyshniak (val@val.one) + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +public class Tk103FrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + if (buf.readableBytes() < 2) { + return null; + } + + int frameStartIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '('); + if (frameStartIndex == -1) { + buf.clear(); + return null; + } + + int frameEndIndex, freeTextSymbolCounter; + for (frameEndIndex = frameStartIndex, freeTextSymbolCounter = 0;; frameEndIndex++) { + int freeTextIndex = frameEndIndex; + frameEndIndex = buf.indexOf(frameEndIndex, buf.writerIndex(), (byte) ')'); + if (frameEndIndex == -1) { + break; + } + for (;; freeTextIndex++, freeTextSymbolCounter++) { + freeTextIndex = buf.indexOf(freeTextIndex, frameEndIndex, (byte) '$'); + if (freeTextIndex == -1 || freeTextIndex >= frameEndIndex) { + break; + } + } + if (freeTextSymbolCounter % 2 == 0) { + break; + } + } + + if (frameEndIndex == -1) { + while (buf.readableBytes() > 1024) { + int discardUntilIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) '('); + if (discardUntilIndex == -1) { + buf.clear(); + } else { + buf.readerIndex(discardUntilIndex); + } + } + return null; + } + + buf.readerIndex(frameStartIndex); + + return buf.readBytes(frameEndIndex + 1 - frameStartIndex); + } + +} diff --git a/src/org/traccar/protocol/Tk103Protocol.java b/src/org/traccar/protocol/Tk103Protocol.java index 07a68e2d8..6ef9c0a56 100644 --- a/src/org/traccar/protocol/Tk103Protocol.java +++ b/src/org/traccar/protocol/Tk103Protocol.java @@ -22,7 +22,6 @@ import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.handler.codec.string.StringDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; -import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.TrackerServer; import org.traccar.model.Command; @@ -33,6 +32,14 @@ public class Tk103Protocol extends BaseProtocol { public Tk103Protocol() { super("tk103"); setSupportedDataCommands( + Command.TYPE_CUSTOM, + Command.TYPE_GET_DEVICE_STATUS, + Command.TYPE_IDENTIFICATION, + Command.TYPE_MODE_DEEP_SLEEP, + Command.TYPE_MODE_POWER_SAVING, + Command.TYPE_ALARM_SOS, + Command.TYPE_SET_CONNECTION, + Command.TYPE_SOS_NUMBER, Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, Command.TYPE_POSITION_STOP, @@ -48,7 +55,7 @@ public class Tk103Protocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')')); + pipeline.addLast("frameDecoder", new Tk103FrameDecoder()); pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); pipeline.addLast("objectEncoder", new Tk103ProtocolEncoder()); diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java index be3def453..eda29e3f8 100644 --- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java @@ -26,6 +26,7 @@ import org.traccar.helper.PatternBuilder; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.util.regex.Pattern; @@ -40,6 +41,7 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { } private static final Pattern PATTERN = new PatternBuilder() + .text("(").optional() .number("(d+)(,)?") // device id .expression("(.{4}),?") // command .number("(d*)") @@ -63,21 +65,28 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .number("(?:L(x+))?") // odometer .any() .number("([+-]ddd.d)?") // temperature + .groupBegin() + .number("([+-]?d+.d{1,2}),") // altitude + .number("(d+)$") // number of visible satellites + .groupEnd("?") .text(")").optional() .compile(); private static final Pattern PATTERN_BATTERY = new PatternBuilder() + .text("(").optional() .number("(d+),") // device id .text("ZC20,") .number("(dd)(dd)(dd),") // date (ddmmyy) .number("(dd)(dd)(dd),") // time (hhmmss) - .number("d+,") // battery level + .number("(d+),") // battery level .number("(d+),") // battery voltage .number("(d+),") // power voltage .number("d+") // installed + .any() .compile(); private static final Pattern PATTERN_NETWORK = new PatternBuilder() + .text("(").optional() .number("(d{12})") // device id .text("BZ00,") .number("(d+),") // mcc @@ -87,6 +96,31 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_LBSWIFI = new PatternBuilder() + .text("(").optional() + .number("(d+),") // device id + .expression("(.{4}),") // command + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(d+),") // lac + .number("(d+),") // cid + .number("(d+),") // number of wifi macs + .number("((?:(?:xx:){5}(?:xx)\\*[-+]?d+\\*d+,)*)") + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd)") // time (hhmmss) + .any() + .compile(); + + private static final Pattern PATTERN_COMMAND_RESULT = new PatternBuilder() + .text("(").optional() + .number("(d+),") // device id + .expression(".{4},") // command + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .expression("\\$([\\s\\S]*?)(?:\\$|$)") // message + .any() + .compile(); + private String decodeAlarm(int value) { switch (value) { case 1: @@ -112,30 +146,53 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, decodeAlarm(data.charAt(0) - '0')); break; case "ZC11": + case "DW31": + case "DW51": position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT); break; case "ZC12": + case "DW32": + case "DW52": position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); break; case "ZC13": + case "DW33": + case "DW53": position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); break; case "ZC15": + case "DW35": + case "DW55": position.set(Position.KEY_IGNITION, true); break; case "ZC16": + case "DW36": + case "DW56": position.set(Position.KEY_IGNITION, false); break; + case "ZC29": + case "DW42": + case "DW62": + position.set(Position.KEY_IGNITION, true); + break; case "ZC17": + case "DW37": + case "DW57": position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); break; case "ZC25": + case "DW3E": + case "DW5E": position.set(Position.KEY_ALARM, Position.ALARM_SOS); break; case "ZC26": + case "DW3F": + case "DW5F": position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); break; case "ZC27": + case "DW40": + case "DW60": position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); break; default: @@ -143,6 +200,23 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { } } + private Integer decodeBattery(int value) { + switch (value) { + case 6: + return 100; + case 5: + return 80; + case 4: + return 50; + case 3: + return 20; + case 2: + return 10; + default: + return null; + } + } + private Position decodeBattery(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_BATTERY, sentence); if (!parser.matches()) { @@ -160,6 +234,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + int batterylevel = parser.nextInt(0); + if (batterylevel != 255) { + position.set(Position.KEY_BATTERY_LEVEL, decodeBattery(batterylevel)); + } + int battery = parser.nextInt(0); if (battery != 65535) { position.set(Position.KEY_BATTERY, battery * 0.01); @@ -195,20 +274,84 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return position; } - @Override + + private Position decodeLbsWifi(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_LBSWIFI, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeType(position, parser.next(), "0"); + + getLastLocation(position, null); + + Network network = new Network(); + + network.addCellTower(CellTower.from( + parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())); + + int wifiCount = parser.nextInt(); + if (parser.hasNext()) { + String[] wifimacs = parser.next().split(","); + if (wifimacs.length == wifiCount) { + for (int i = 0; i < wifiCount; i++) { + String[] wifiinfo = wifimacs[i].split("\\*"); + network.addWifiAccessPoint(WifiAccessPoint.from( + wifiinfo[0], Integer.parseInt(wifiinfo[1]), Integer.parseInt(wifiinfo[2]))); + } + } + } + + if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { + position.setNetwork(network); + } + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + return position; + } + + private Position decodeCommandResult(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_COMMAND_RESULT, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + position.set(Position.KEY_RESULT, parser.next()); + + return position; + + } + +@Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; - int beginIndex = sentence.indexOf('('); - if (beginIndex != -1) { - sentence = sentence.substring(beginIndex + 1); - } - if (channel != null) { - String id = sentence.substring(0, 12); - String type = sentence.substring(12, 16); + String id = sentence.substring(1, 13); + String type = sentence.substring(13, 17); if (type.equals("BP00")) { channel.write("(" + id + "AP01HSO)"); return null; @@ -221,6 +364,10 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return decodeBattery(channel, remoteAddress, sentence); } else if (sentence.contains("BZ00")) { return decodeNetwork(channel, remoteAddress, sentence); + } else if (sentence.contains("ZC03")) { + return decodeCommandResult(channel, remoteAddress, sentence); + } else if (sentence.contains("DW5")) { + return decodeLbsWifi(channel, remoteAddress, sentence); } Parser parser = new Parser(PATTERN, sentence); @@ -299,6 +446,14 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0)); } + if (parser.hasNext()) { + position.setAltitude(parser.nextDouble(0)); + } + + if (parser.hasNext()) { + position.set(Position.KEY_SATELLITES_VISIBLE, parser.nextInt(0)); + } + return position; } diff --git a/src/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/org/traccar/protocol/Tk103ProtocolEncoder.java index 9e49b6ff1..d7b17eeb7 100644 --- a/src/org/traccar/protocol/Tk103ProtocolEncoder.java +++ b/src/org/traccar/protocol/Tk103ProtocolEncoder.java @@ -16,33 +16,75 @@ */ package org.traccar.protocol; +import org.traccar.Context; import org.traccar.StringProtocolEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; public class Tk103ProtocolEncoder extends StringProtocolEncoder { + private final boolean forceAlternative; + + public Tk103ProtocolEncoder() { + this.forceAlternative = false; + } + + public Tk103ProtocolEncoder(boolean forceAlternative) { + this.forceAlternative = forceAlternative; + } + @Override protected Object encodeCommand(Command command) { + boolean alternative = forceAlternative || Context.getIdentityManager().lookupAttributeBoolean( + command.getDeviceId(), "tk103.alternative", false, true); + + initDevicePassword(command, "123456"); + switch (command.getType()) { case Command.TYPE_GET_VERSION: - return formatCommand(command, "({%s}AP07)", Command.KEY_UNIQUE_ID); + return alternative ? formatCommand(command, "[begin]sms2,*about*,[end]") + : formatCommand(command, "({%s}AP07)", Command.KEY_UNIQUE_ID); case Command.TYPE_REBOOT_DEVICE: - return formatCommand(command, "({%s}AT00)", Command.KEY_UNIQUE_ID); + return alternative ? formatCommand(command, "[begin]sms2,88888888,[end]") + : formatCommand(command, "({%s}AT00)", Command.KEY_UNIQUE_ID); case Command.TYPE_SET_ODOMETER: return formatCommand(command, "({%s}AX01)", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_SINGLE: - return formatCommand(command, "({%s}AP00)", Command.KEY_UNIQUE_ID); + return alternative ? formatCommand(command, "[begin]sms2,*getposl*,[end]") + : formatCommand(command, "({%s}AP00)", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_PERIODIC: - return formatCommand(command, "({%s}AR00%s0000)", Command.KEY_UNIQUE_ID, + return alternative ? formatCommand(command, "[begin]sms2,*routetrack*99*,[end]") + : formatCommand(command, "({%s}AR00%s0000)", Command.KEY_UNIQUE_ID, String.format("%04X", command.getInteger(Command.KEY_FREQUENCY))); case Command.TYPE_POSITION_STOP: - return formatCommand(command, "({%s}AR0000000000)", Command.KEY_UNIQUE_ID); + return alternative ? formatCommand(command, "[begin]sms2,*routetrackoff*,[end]") + : formatCommand(command, "({%s}AR0000000000)", Command.KEY_UNIQUE_ID); case Command.TYPE_ENGINE_STOP: return formatCommand(command, "({%s}AV011)", Command.KEY_UNIQUE_ID); case Command.TYPE_ENGINE_RESUME: return formatCommand(command, "({%s}AV010)", Command.KEY_UNIQUE_ID); + case Command.TYPE_CUSTOM: + return formatCommand(command, "[begin]sms2,{%s},[end]", Command.KEY_DATA); + case Command.TYPE_GET_DEVICE_STATUS: + return formatCommand(command, "[begin]sms2,*status*,[end]"); + case Command.TYPE_IDENTIFICATION: + return formatCommand(command, "[begin]sms2,999999,[end]"); + case Command.TYPE_MODE_DEEP_SLEEP: + return formatCommand(command, command.getBoolean(Command.KEY_ENABLE) + ? "[begin]sms2,*sleep*2*,[end]" : "[begin]sms2,*sleepoff*,[end]"); + case Command.TYPE_MODE_POWER_SAVING: + return formatCommand(command, command.getBoolean(Command.KEY_ENABLE) + ? "[begin]sms2,*sleepv*,[end]" : "[begin]sms2,*sleepoff*,[end]"); + case Command.TYPE_ALARM_SOS: + return formatCommand(command, command.getBoolean(Command.KEY_ENABLE) + ? "[begin]sms2,*soson*,[end]" : "[begin]sms2,*sosoff*,[end]"); + case Command.TYPE_SET_CONNECTION: + return formatCommand(command, "[begin]sms2,*setip*%s*{%s}*,[end]", + command.getString(Command.KEY_SERVER).replace(".", "*"), Command.KEY_PORT); + case Command.TYPE_SOS_NUMBER: + return formatCommand(command, "[begin]sms2,*master*{%s}*{%s}*,[end]", + Command.KEY_DEVICE_PASSWORD, Command.KEY_PHONE); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java index 5e852aff2..4a3055932 100644 --- a/src/org/traccar/protocol/TmgProtocolDecoder.java +++ b/src/org/traccar/protocol/TmgProtocolDecoder.java @@ -72,9 +72,9 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // satellites .number("[^,]*,") // battery level .expression("([01]),") // ignition - .expression("[LH]{4},") // input + .expression("([LH]{4}),") // input .expression("[NT]{4},") // tamper status - .expression("[LH]{2},") // output + .expression("([LH]{2}),") // output .number("(d+.d+),") // adc1 .number("(d+.d+),") // adc1 .number("[^,]*,") // device id @@ -171,6 +171,17 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextInt()); position.set(Position.KEY_SATELLITES, parser.nextInt()); position.set(Position.KEY_IGNITION, parser.nextInt() == 1); + + char[] input = parser.next().toCharArray(); + for (int i = 0; i < input.length; i++) { + position.set(Position.PREFIX_IN + (i + 1), input[i] == 'H'); + } + + char[] output = parser.next().toCharArray(); + for (int i = 0; i < output.length; i++) { + position.set(Position.PREFIX_OUT + (i + 1), output[i] == 'H'); + } + position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); position.set(Position.KEY_ODOMETER, parser.nextInt()); |