aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2012-12-16 19:00:49 +1300
committerAnton Tananaev <anton.tananaev@gmail.com>2012-12-16 19:00:49 +1300
commit1cde955eb9e506ec3a4cf4b3829d88b798d45175 (patch)
tree024843b594496132f8ef23c9e91a2da21e3645a2
parentc486ac49ac3cbbb0cefa6f5e6cc4c6b38e444a7a (diff)
downloadtrackermap-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.java247
-rw-r--r--test/org/traccar/protocol/SkypatrolProtocolDecoderTest.java15
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)));
}