diff options
author | Anton Tananaev <anton.tananaev@gmail.com> | 2012-12-16 19:00:49 +1300 |
---|---|---|
committer | Anton Tananaev <anton.tananaev@gmail.com> | 2012-12-16 19:00:49 +1300 |
commit | 1cde955eb9e506ec3a4cf4b3829d88b798d45175 (patch) | |
tree | 024843b594496132f8ef23c9e91a2da21e3645a2 | |
parent | c486ac49ac3cbbb0cefa6f5e6cc4c6b38e444a7a (diff) | |
download | trackermap-server-1cde955eb9e506ec3a4cf4b3829d88b798d45175.tar.gz trackermap-server-1cde955eb9e506ec3a4cf4b3829d88b798d45175.tar.bz2 trackermap-server-1cde955eb9e506ec3a4cf4b3829d88b798d45175.zip |
Added skypatrol protocol (fix #82)
-rw-r--r-- | src/org/traccar/protocol/SkypatrolProtocolDecoder.java | 247 | ||||
-rw-r--r-- | test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java | 15 |
2 files changed, 174 insertions, 88 deletions
diff --git a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java b/src/org/traccar/protocol/SkypatrolProtocolDecoder.java index d5adf2aaf..3f54fd0bf 100644 --- a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java +++ b/src/org/traccar/protocol/SkypatrolProtocolDecoder.java @@ -15,10 +15,10 @@ */ package org.traccar.protocol; +import java.nio.charset.Charset; import java.util.Calendar; import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.traccar.GenericProtocolDecoder; @@ -32,119 +32,208 @@ import org.traccar.model.Position; public class SkypatrolProtocolDecoder extends GenericProtocolDecoder { /** - * Device ID - */ - private Long deviceId; - - /** * Initialize */ public SkypatrolProtocolDecoder(DataManager dataManager) { super(dataManager); } - /** - * Regular expressions pattern - */ - static private Pattern pattern = Pattern.compile( - "\\$GPRMC," + - "(\\d{2})(\\d{2})(\\d{2})\\.(\\d+)," + // Time (HHMMSS.SSS) - "([AV])," + // Validity - "(\\d{2})(\\d{2}\\.\\d+)," + // Latitude (DDMM.MMMM) - "([NS])," + - "(\\d{3})(\\d{2}\\.\\d+)," + // Longitude (DDDMM.MMMM) - "([EW])," + - "(\\d+\\.\\d{2})?," + // Speed - "(\\d+\\.\\d{2})?," + // Course - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - ".+"); // Other (Checksumm) - + private static boolean checkBit(long mask, int bit) { + long checkMask = 1 << bit; + return (mask & checkMask) == checkMask; + } + + private static double convertCoordinate(long coordinate) { + int sign = 1; + if (coordinate > 0x7fffffffl) { + sign = -1; + coordinate = 0xffffffffl - coordinate; + } + + double degrees = coordinate / 1000000; + degrees += (coordinate % 1000000) / 600000.0; + + return sign * degrees; + } + /** * Decode message */ + @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { - String sentence = (String) msg; + ChannelBuffer buf = (ChannelBuffer) msg; - // Detect device ID - if (sentence.contains("$PGID")) { - String imei = sentence.substring(6, 6 + 15); - try { - deviceId = getDataManager().getDeviceByImei(imei).getId(); - } catch(Exception error) { - Log.warning("Unknown device - " + imei); - } + // Read header + int apiNumber = buf.readUnsignedShort(); + int commandType = buf.readUnsignedByte(); + int messageType = buf.getUnsignedByte(buf.readerIndex()) >> 4; + boolean needAck = (buf.readUnsignedByte() & 0xf) == 1; + long mask = 0; + if (buf.readUnsignedByte() == 4) { + mask = buf.readUnsignedInt(); } - // Parse message - else if (sentence.contains("$GPRMC") && deviceId != null) { - - // Send response - if (channel != null) { - channel.write("OK1\r\n"); + // Binary position report + if (apiNumber == 5 && + commandType == 2 && + messageType == 1 && + checkBit(mask, 0)) { + + // Create new position + Position position = new Position(); + StringBuilder extendedInfo = new StringBuilder("<protocol>skypatrol</protocol>"); + + // Status code + if (checkBit(mask, 1)) { + extendedInfo.append("<status>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</status>"); } - - // Parse message - Matcher parser = pattern.matcher(sentence); - if (!parser.matches()) { + + // Device id + String id = null; + if (checkBit(mask, 23)) { + id = buf.toString(buf.readerIndex(), 8, Charset.defaultCharset()).trim(); + buf.skipBytes(8); + } else if (checkBit(mask, 2)) { + id = buf.toString(buf.readerIndex(), 22, Charset.defaultCharset()).trim(); + buf.skipBytes(22); + } else { + Log.warning("No device id field"); return null; } + try { + position.setDeviceId(getDataManager().getDeviceByImei(id).getId()); + } catch(Exception error) { + Log.warning("Unknown device - " + id); + return null; + } + + // IO data + if (checkBit(mask, 3)) { + buf.readUnsignedShort(); + } + + // ADC 1 + if (checkBit(mask, 4)) { + buf.readUnsignedShort(); + } - // Create new position - Position position = new Position(); - position.setDeviceId(deviceId); - - Integer index = 1; + // ADC 2 + if (checkBit(mask, 5)) { + buf.readUnsignedShort(); + } - // Time + // Function category + if (checkBit(mask, 7)) { + buf.readUnsignedByte(); + } + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); time.clear(); - time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++))); - time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++))); - index += 1; // Skip milliseconds - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false); + // Date + if (checkBit(mask, 8)) { + time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); + time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); + time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); + } + + // GPS status + if (checkBit(mask, 9)) { + position.setValid(buf.readUnsignedByte() == 1); + } // Latitude - Double latitude = Double.valueOf(parser.group(index++)); - latitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + if (checkBit(mask, 10)) { + position.setLatitude(convertCoordinate(buf.readUnsignedInt())); + } // Longitude - Double lonlitude = Double.valueOf(parser.group(index++)); - lonlitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) lonlitude = -lonlitude; - position.setLongitude(lonlitude); - + if (checkBit(mask, 11)) { + position.setLongitude(convertCoordinate(buf.readUnsignedInt())); + } + // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.valueOf(speed)); - } else { - position.setSpeed(0.0); + if (checkBit(mask, 12)) { + position.setSpeed(buf.readUnsignedShort() / 10.0); } // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.valueOf(course)); - } else { - position.setCourse(0.0); + if (checkBit(mask, 13)) { + position.setCourse(buf.readUnsignedShort() / 10.0); } - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.valueOf(parser.group(index++))); + // Time + if (checkBit(mask, 14)) { + time.set(Calendar.HOUR, buf.readUnsignedByte()); + time.set(Calendar.MINUTE, buf.readUnsignedByte()); + time.set(Calendar.SECOND, buf.readUnsignedByte()); + } + position.setTime(time.getTime()); - + // Altitude - position.setAltitude(0.0); + if (checkBit(mask, 15)) { + buf.skipBytes(3); + } + + // Satellites + if (checkBit(mask, 16)) { + extendedInfo.append("<satellites>"); + extendedInfo.append(buf.readUnsignedByte()); + extendedInfo.append("</satellites>"); + } + + // Battery percentage + if (checkBit(mask, 17)) { + buf.readUnsignedShort(); + } + + // Trip milage + if (checkBit(mask, 20)) { + extendedInfo.append("<trip>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</trip>"); + } + + // Milage + if (checkBit(mask, 21)) { + extendedInfo.append("<milage>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</milage>"); + } + + // Time of message generation + if (checkBit(mask, 22)) { + buf.skipBytes(6); + } + + // Battery level + if (checkBit(mask, 24)) { + position.setPower(buf.readUnsignedShort() / 1000.0); + } + + // GPS overspeed + if (checkBit(mask, 25)) { + buf.skipBytes(18); + } + + // Cell information + if (checkBit(mask, 26)) { + buf.skipBytes(54); + } + + // Sequence number + if (checkBit(mask, 28)) { + position.setId((long) buf.readUnsignedShort()); + } + + // Extended info + position.setExtendedInfo(extendedInfo.toString()); return position; } diff --git a/test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java index 467893fdb..3024094d4 100644 --- a/test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java +++ b/test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java @@ -1,23 +1,20 @@ package org.traccar.protocol; +import org.jboss.netty.buffer.ChannelBufferFactory; +import org.jboss.netty.buffer.HeapChannelBufferFactory; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import org.junit.Test; public class SkypatrolProtocolDecoderTest { - + @Test public void testDecode() throws Exception { SkypatrolProtocolDecoder decoder = new SkypatrolProtocolDecoder(new TestDataManager()); + ChannelBufferFactory factory = new HeapChannelBufferFactory(); - /*assertNull(decoder.decode(null, null, "$PGID,359853000144328*0F")); - - assertNotNull(decoder.decode(null, null, - "$GPRMC,094907.000,A,6000.5332,N,03020.5192,E,1.17,60.26,091111,,*33")); - - assertNotNull(decoder.decode(null, null, - "$GPRMC,115528.000,A,6000.5432,N,03020.4948,E,,,091111,,*06"));*/ + byte[] buf1 = {0x00,0x05,0x02,0x10,0x04,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,0x00,0x00,0x00,0x0D,0x31,0x31,0x34,0x37,0x37,0x35,0x38,0x33,0x00,(byte)0xCB,0x00,0x00,0x00,0x00,0x0E,0x11,0x07,0x0C,0x01,0x01,(byte)0x84,(byte)0xD0,0x32,(byte)0xFB,0x38,0x41,0x37,0x00,0x00,0x00,0x00,0x16,0x07,0x2B,0x00,0x00,0x17,0x05,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x4E,0x0C,0x07,0x11,0x16,0x07,0x2C,0x10,0x59,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x03,0x10,0x02,0x60,(byte)0xB7,0x36,0x3B,0x63,0x06,(byte)0xC1,0x1A,0x00,(byte)0xB7,0x36,0x37,(byte)0xF2,0x06,(byte)0xBF,0x19,(byte)0xB7,0x36,0x37,(byte)0xF1,0x06,(byte)0xB5,0x0E,(byte)0xB7,0x36,0x38,(byte)0xB1,0x06,(byte)0xBB,0x0B,(byte)0xB7,0x36,0x3B,0x61,0x06,(byte)0xB8,0x0A,(byte)0xB7,0x36,0x37,(byte)0xF3,0x06,(byte)0xB7,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C}; + assertNotNull(decoder.decode(null, null, factory.getBuffer(buf1, 0, buf1.length))); } |