diff options
Diffstat (limited to 'src')
23 files changed, 976 insertions, 369 deletions
diff --git a/src/org/traccar/BaseProtocolEncoder.java b/src/org/traccar/BaseProtocolEncoder.java index 2c8a81868..18544fd28 100644 --- a/src/org/traccar/BaseProtocolEncoder.java +++ b/src/org/traccar/BaseProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 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,7 @@ public abstract class BaseProtocolEncoder extends OneToOneEncoder { if (msg instanceof Command) { Command command = (Command) msg; - Object encodedCommand = encodeCommand(command); + Object encodedCommand = encodeCommand(channel, command); // Log command StringBuilder s = new StringBuilder(); @@ -65,6 +65,12 @@ public abstract class BaseProtocolEncoder extends OneToOneEncoder { return msg; } - protected abstract Object encodeCommand(Command command); + protected Object encodeCommand(Channel channel, Command command) { + return encodeCommand(command); + } + + protected Object encodeCommand(Command command) { + return null; + } } diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java index 8e88e15b9..6228f3709 100644 --- a/src/org/traccar/MainEventHandler.java +++ b/src/org/traccar/MainEventHandler.java @@ -60,13 +60,18 @@ public class MainEventHandler extends IdleStateAwareChannelHandler { // Log position StringBuilder s = new StringBuilder(); s.append(formatChannel(e.getChannel())).append(" "); - s.append("id: ").append(uniqueId).append(", "); - s.append("time: ").append( - new SimpleDateFormat(Log.DATE_FORMAT).format(position.getFixTime())).append(", "); - s.append("lat: ").append(String.format("%.5f", position.getLatitude())).append(", "); - s.append("lon: ").append(String.format("%.5f", position.getLongitude())).append(", "); - s.append("speed: ").append(String.format("%.1f", position.getSpeed())).append(", "); - s.append("course: ").append(String.format("%.1f", position.getCourse())); + s.append("id: ").append(uniqueId); + s.append(", time: ").append( + new SimpleDateFormat(Log.DATE_FORMAT).format(position.getFixTime())); + s.append(", lat: ").append(String.format("%.5f", position.getLatitude())); + s.append(", lon: ").append(String.format("%.5f", position.getLongitude())); + if (position.getSpeed() > 0) { + s.append(", speed: ").append(String.format("%.1f", position.getSpeed())); + } + s.append(", course: ").append(String.format("%.1f", position.getCourse())); + if (position.getAccuracy() > 0) { + s.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy())); + } Object cmdResult = position.getAttributes().get(Position.KEY_RESULT); if (cmdResult != null) { s.append(", result: ").append(cmdResult); diff --git a/src/org/traccar/helper/Checksum.java b/src/org/traccar/helper/Checksum.java index 43ba6a689..5aed84bf8 100644 --- a/src/org/traccar/helper/Checksum.java +++ b/src/org/traccar/helper/Checksum.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2015 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. @@ -24,163 +24,109 @@ public final class Checksum { private Checksum() { } - private static final int[] CRC16_CCITT_TABLE_REVERSE = { - 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, - 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, - 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, - 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, - 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, - 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, - 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, - 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, - 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, - 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, - 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, - 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, - 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, - 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, - 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, - 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, - 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, - 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, - 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, - 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, - 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, - 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, - 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, - 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, - 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, - 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, - 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, - 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, - 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, - 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, - 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, - 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 - }; - - private static final int[] CRC16_CCITT_TABLE = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, - 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, - 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, - 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, - 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, - 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, - 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, - 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, - 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, - 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, - 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, - 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, - 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, - 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, - 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, - 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, - 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, - 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, - 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, - 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, - 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, - 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 - }; - - private static final int[] CRC16_IBM_TABLE = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, - }; - - // More info: http://reveng.sourceforge.net/crc-catalogue/16.htm - public static final String CRC16_IBM = "IBM"; - public static final String CRC16_MODBUS = "MODBUS"; - public static final String CRC16_X25 = "X-25"; - public static final String CRC16_CCITT_FALSE = "CCITT-FALSE"; - public static final String CRC16_KERMIT = "KERMIT"; - public static final String CRC16_XMODEM = "XMODEM"; - public static final String CRC16_AUG_CCITT = "AUG-CCITT"; - public static final String CRC16_GENIBUS = "GENIBUS"; - public static final String CRC16_MCRF4XX = "MCRF4XX"; - - private static int crc16Unreflected(ByteBuffer buf, int crcIn, int[] table) { - int crc16 = crcIn; - while (buf.hasRemaining()) { - crc16 = table[((crc16 >> 8) ^ buf.get()) & 0xff] ^ (crc16 << 8); + public static class Algorithm { + + private int poly; + private int init; + private boolean refIn; + private boolean refOut; + private int xorOut; + private int[] table; + + public Algorithm(int bits, int poly, int init, boolean refIn, boolean refOut, int xorOut) { + this.poly = poly; + this.init = init; + this.refIn = refIn; + this.refOut = refOut; + this.xorOut = xorOut; + this.table = bits == 8 ? initTable8() : initTable16(); + } + + private int[] initTable8() { + int[] table = new int[256]; + int crc; + for (int i = 0; i < 256; i++) { + crc = i; + for (int j = 0; j < 8; j++) { + boolean bit = (crc & 0x80) != 0; + crc <<= 1; + if (bit) { + crc ^= poly; + } + } + table[i] = crc & 0xFF; + } + return table; + } + + private int[] initTable16() { + int[] table = new int[256]; + int crc; + for (int i = 0; i < 256; i++) { + crc = i << 8; + for (int j = 0; j < 8; j++) { + boolean bit = (crc & 0x8000) != 0; + crc <<= 1; + if (bit) { + crc ^= poly; + } + } + table[i] = crc & 0xFFFF; + } + return table; + } + + } + + private static int reverse(int value, int bits) { + int result = 0; + for (int i = 0; i < bits; i++) { + result = (result << 1) | (value & 1); + value >>= 1; } - return crc16 & 0xFFFF; + return result; } - private static int crc16Reflected(ByteBuffer buf, int crcIn, int[] table) { - int crc16 = crcIn; + public static int crc8(Algorithm algorithm, ByteBuffer buf) { + int crc = algorithm.init; while (buf.hasRemaining()) { - crc16 = table[(crc16 ^ buf.get()) & 0xff] ^ (crc16 >> 8); + int b = buf.get() & 0xFF; + if (algorithm.refIn) { + b = reverse(b, 8); + } + crc = algorithm.table[(crc & 0xFF) ^ b]; + } + if (algorithm.refOut) { + crc = reverse(crc, 8); } - return crc16 & 0xFFFF; + return (crc ^ algorithm.xorOut) & 0xFF; } - public static int crc16(String type, ByteBuffer buf) { - switch (type) { - case CRC16_IBM: - return crc16Reflected(buf, 0, CRC16_IBM_TABLE); - case CRC16_MODBUS: - return crc16Reflected(buf, 0xFFFF, CRC16_IBM_TABLE); - case CRC16_X25: - return crc16Reflected(buf, 0xFFFF, CRC16_CCITT_TABLE_REVERSE) ^ 0xFFFF; - case CRC16_CCITT_FALSE: - return crc16Unreflected(buf, 0xFFFF, CRC16_CCITT_TABLE); - case CRC16_KERMIT: - return crc16Reflected(buf, 0, CRC16_CCITT_TABLE_REVERSE); - case CRC16_XMODEM: - return crc16Unreflected(buf, 0, CRC16_CCITT_TABLE); - case CRC16_AUG_CCITT: - return crc16Unreflected(buf, 0x1d0f, CRC16_CCITT_TABLE); - case CRC16_GENIBUS: - return crc16Unreflected(buf, 0xFFFF, CRC16_CCITT_TABLE) ^ 0xFFFF; - case CRC16_MCRF4XX: - return crc16Reflected(buf, 0xFFFF, CRC16_CCITT_TABLE_REVERSE); - default: - throw new UnsupportedOperationException(type); + public static int crc16(Algorithm algorithm, ByteBuffer buf) { + int crc = algorithm.init; + while (buf.hasRemaining()) { + int b = buf.get() & 0xFF; + if (algorithm.refIn) { + b = reverse(b, 8); + } + crc = (crc << 8) ^ algorithm.table[((crc >> 8) & 0xFF) ^ b]; + } + if (algorithm.refOut) { + crc = reverse(crc, 16); } + return (crc ^ algorithm.xorOut) & 0xFFFF; } + public static final Algorithm CRC8_EGTS = new Algorithm(8, 0x31, 0xFF, false, false, 0x00); + public static final Algorithm CRC8_ROHC = new Algorithm(8, 0x07, 0xFF, true, true, 0x00); + + public static final Algorithm CRC16_IBM = new Algorithm(16, 0x8005, 0x0000, true, true, 0x0000); + public static final Algorithm CRC16_X25 = new Algorithm(16, 0x1021, 0xFFFF, true, true, 0xFFFF); + public static final Algorithm CRC16_MODBUS = new Algorithm(16, 0x8005, 0xFFFF, true, true, 0x0000); + public static final Algorithm CRC16_CCITT_FALSE = new Algorithm(16, 0x1021, 0xFFFF, false, false, 0x0000); + public static final Algorithm CRC16_KERMIT = new Algorithm(16, 0x1021, 0x0000, true, true, 0x0000); + public static final Algorithm CRC16_XMODEM = new Algorithm(16, 0x1021, 0x0000, false, false, 0x0000); + public static int crc32(ByteBuffer buf) { CRC32 checksum = new CRC32(); while (buf.hasRemaining()) { diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java index 49e3231c3..fca0f16e3 100644 --- a/src/org/traccar/model/Position.java +++ b/src/org/traccar/model/Position.java @@ -58,6 +58,7 @@ public class Position extends Message { public static final String KEY_TYPE = "type"; public static final String KEY_IGNITION = "ignition"; public static final String KEY_FLAGS = "flags"; + public static final String KEY_ANTENNA = "antenna"; public static final String KEY_CHARGE = "charge"; public static final String KEY_IP = "ip"; public static final String KEY_ARCHIVE = "archive"; 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/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/ContinentalProtocolDecoder.java b/src/org/traccar/protocol/ContinentalProtocolDecoder.java index 2138eb39e..726d9e16b 100644 --- a/src/org/traccar/protocol/ContinentalProtocolDecoder.java +++ b/src/org/traccar/protocol/ContinentalProtocolDecoder.java @@ -16,6 +16,7 @@ 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; @@ -36,6 +37,22 @@ public class ContinentalProtocolDecoder extends BaseProtocolDecoder { 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 { @@ -46,11 +63,14 @@ public class ContinentalProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // length buf.readUnsignedByte(); // software version - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedInt())); + 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(); 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/EgtsProtocol.java b/src/org/traccar/protocol/EgtsProtocol.java index 0a57f0061..13ec6c9a7 100644 --- a/src/org/traccar/protocol/EgtsProtocol.java +++ b/src/org/traccar/protocol/EgtsProtocol.java @@ -20,6 +20,7 @@ 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 { @@ -30,13 +31,15 @@ public class EgtsProtocol extends BaseProtocol { @Override public void initTrackerServers(List<TrackerServer> serverList) { - serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + 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 index d05504d81..420701e6c 100644 --- a/src/org/traccar/protocol/EgtsProtocolDecoder.java +++ b/src/org/traccar/protocol/EgtsProtocolDecoder.java @@ -16,13 +16,18 @@ 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; @@ -33,6 +38,10 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { 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; @@ -60,6 +69,44 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { 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 { @@ -68,26 +115,16 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(buf.getUnsignedByte(buf.readerIndex() + 3)); - DeviceSession deviceSession = null; List<Position> positions = new LinkedList<>(); while (buf.readableBytes() > 2) { int length = buf.readUnsignedShort(); - - buf.readUnsignedShort(); // index - + int index = buf.readUnsignedShort(); int recordFlags = buf.readUnsignedByte(); if (BitUtil.check(recordFlags, 0)) { - String deviceId = String.valueOf(buf.readUnsignedInt()); - if (deviceSession == null) { - deviceSession = getDeviceSession(channel, remoteAddress, deviceId); - } - } - - if (deviceSession == null) { - deviceSession = getDeviceSession(channel, remoteAddress); + buf.readUnsignedInt(); // object id } if (BitUtil.check(recordFlags, 1)) { @@ -97,19 +134,61 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedInt(); // time } - buf.readUnsignedByte(); // source service type + int serviceType = buf.readUnsignedByte(); buf.readUnsignedByte(); // recipient service type int recordEnd = buf.readerIndex() + length; Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + 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_POS_DATA) { + 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); @@ -125,7 +204,7 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { } int speed = buf.readUnsignedShort(); - position.setSpeed(BitUtil.to(speed, 14)); + 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); @@ -166,7 +245,9 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { buf.readerIndex(end); } - positions.add(position); + if (serviceType == SERVICE_TELEDATA && deviceSession != null) { + positions.add(position); + } } return positions.isEmpty() ? null : positions; 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/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/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/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/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java index 8da188f60..d6dd933b5 100644 --- a/src/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/org/traccar/protocol/TotemProtocolDecoder.java @@ -243,12 +243,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HDOP, parser.nextDouble()); } - position.set(Position.PREFIX_IO + 1, parser.next()); + String io = parser.next(); if (pattern == PATTERN1) { + for (int i = 1; i <= 4; i++) { + position.set(Position.PREFIX_IN + i, io.charAt(3 + i) == '1'); + } position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.01); } else { + position.set(Position.KEY_ANTENNA, io.charAt(0) == '1'); + position.set(Position.KEY_CHARGE, io.charAt(1) == '1'); + for (int i = 1; i <= 6; i++) { + position.set(Position.PREFIX_IN + i, io.charAt(1 + i) == '1'); + } position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.1); } + for (int i = 1; i <= 4; i++) { + position.set(Position.PREFIX_OUT + i, io.charAt(7 + i) == '1'); + } position.set(Position.KEY_POWER, parser.nextDouble(0)); position.set(Position.PREFIX_ADC + 1, parser.next()); diff --git a/src/org/traccar/protocol/WatchFrameDecoder.java b/src/org/traccar/protocol/WatchFrameDecoder.java index 9adea2843..0009ef30f 100644 --- a/src/org/traccar/protocol/WatchFrameDecoder.java +++ b/src/org/traccar/protocol/WatchFrameDecoder.java @@ -37,11 +37,6 @@ public class WatchFrameDecoder extends FrameDecoder { int lengthIndex = buf.indexOf(idIndex, buf.writerIndex(), (byte) '*') + 1; if (lengthIndex <= 0) { return null; - } else if (lengthIndex - idIndex > 10 + 1) { - lengthIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) '*') + 1; - if (lengthIndex <= 0) { - return null; - } } int payloadIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) '*'); @@ -49,6 +44,15 @@ public class WatchFrameDecoder extends FrameDecoder { 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) { diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java index c57279296..68832cd3e 100644 --- a/src/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/org/traccar/protocol/WatchProtocolDecoder.java @@ -60,7 +60,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .expression("(.*)") // cell and wifi .compile(); - private void sendResponse(Channel channel, String manufacturer, String id, String index, String content) { + private void sendResponse(Channel channel, String id, String index, String content) { if (channel != null) { if (index != null) { channel.write(String.format( @@ -161,6 +161,17 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { return position; } + private boolean hasIndex; + private String manufacturer; + + public boolean getHasIndex() { + return hasIndex; + } + + public String getManufacturer() { + return manufacturer; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -168,11 +179,11 @@ 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 - int idLength = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*') - buf.readerIndex(); - String id = buf.readBytes(idLength).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; @@ -181,8 +192,11 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(1); // delimiter String index = null; - if (idLength > 10) { + 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 } @@ -192,7 +206,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { 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(); } @@ -205,11 +219,11 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { if (type.equals("INIT")) { - sendResponse(channel, manufacturer, id, index, "INIT,1"); + sendResponse(channel, id, index, "INIT,1"); } else if (type.equals("LK")) { - sendResponse(channel, manufacturer, id, index, "LK"); + sendResponse(channel, id, index, "LK"); if (buf.readable()) { String[] values = buf.toString(StandardCharsets.US_ASCII).split(","); @@ -229,14 +243,14 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { || type.equals("AL") || type.equals("WT")) { if (type.equals("AL")) { - sendResponse(channel, manufacturer, id, index, "AL"); + sendResponse(channel, id, index, "AL"); } return decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII)); } else if (type.equals("TKQ")) { - sendResponse(channel, manufacturer, id, index, "TKQ"); + sendResponse(channel, id, index, "TKQ"); } else if (type.equals("PULSE") || type.equals("heart") || type.equals("bphrt")) { diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java index 5206fbf10..4c87f3abd 100644 --- a/src/org/traccar/protocol/WatchProtocolEncoder.java +++ b/src/org/traccar/protocol/WatchProtocolEncoder.java @@ -15,6 +15,7 @@ */ package org.traccar.protocol; +import org.jboss.netty.channel.Channel; import org.traccar.StringProtocolEncoder; import org.traccar.helper.DataConverter; import org.traccar.helper.Log; @@ -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) { @@ -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; |