aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Muratov <binakot@gmail.com>2017-07-13 08:30:35 +0300
committerIvan Muratov <binakot@gmail.com>2017-07-13 08:30:35 +0300
commit6e70a873886b366980423e448808b60c624e4047 (patch)
tree905021251f4174ab2eb4d01ff2587c86e1b8314a
parent2e0ed3ccfde8f779111de2b722a94224faa65c99 (diff)
downloadtrackermap-server-6e70a873886b366980423e448808b60c624e4047.tar.gz
trackermap-server-6e70a873886b366980423e448808b60c624e4047.tar.bz2
trackermap-server-6e70a873886b366980423e448808b60c624e4047.zip
Basic ARNAVI4 binary protocol implementation is done.
Supported 2 types of HEADER packets (v1, v2). Supported 3 types of PACKAGE DATA records: latitude, longitude and additional data (speed, satellites, altitude, course).
-rw-r--r--src/org/traccar/protocol/Arnavi4FrameDecoder.java51
-rw-r--r--src/org/traccar/protocol/Arnavi4Protocol.java14
-rw-r--r--src/org/traccar/protocol/Arnavi4ProtocolDecoder.java188
-rw-r--r--test/org/traccar/protocol/Arnavi4FrameDecoderTest.java37
-rw-r--r--test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java23
5 files changed, 249 insertions, 64 deletions
diff --git a/src/org/traccar/protocol/Arnavi4FrameDecoder.java b/src/org/traccar/protocol/Arnavi4FrameDecoder.java
new file mode 100644
index 000000000..eaf829cc3
--- /dev/null
+++ b/src/org/traccar/protocol/Arnavi4FrameDecoder.java
@@ -0,0 +1,51 @@
+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;
+
+/**
+ * Created by Ivan Muratov @binakot on 12.07.2017.
+ */
+public class Arnavi4FrameDecoder extends FrameDecoder {
+
+ static final byte HEADER_START_SIGN = (byte)0xFF;
+ static final byte HEADER_VERSION_1 = 0x22;
+ static final byte HEADER_VERSION_2 = 0x23;
+ static final int HEADER_LENGTH = 10;
+
+ static final byte PACKAGE_START_SIGN = 0x5B;
+ static final byte PACKAGE_END_SIGN = 0x5D;
+ static final int PACKAGE_MIN_PARCEL_NUMBER = 0x01;
+ static final int PACKAGE_MAX_PARCEL_NUMBER = 0xFB;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx,
+ Channel channel,
+ ChannelBuffer buf) throws Exception {
+
+ if (buf.readableBytes() == 0) {
+ return null;
+ }
+
+ byte[] bytes = new byte[buf.readableBytes()];
+ buf.getBytes(0, bytes);
+
+ if (bytes[0] == HEADER_START_SIGN
+ && bytes.length == HEADER_LENGTH
+ && (bytes[1] == HEADER_VERSION_1 || bytes[1] == HEADER_VERSION_2)) {
+ return buf.readBytes(HEADER_LENGTH);
+ }
+
+ int parcelNumber = bytes[1] & 0xFF;
+ if (bytes[0] == PACKAGE_START_SIGN && bytes[bytes.length - 1] == PACKAGE_END_SIGN
+ && parcelNumber >= PACKAGE_MIN_PARCEL_NUMBER && parcelNumber <= PACKAGE_MAX_PARCEL_NUMBER) {
+ return buf.readBytes(bytes.length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Arnavi4Protocol.java b/src/org/traccar/protocol/Arnavi4Protocol.java
index 622051fbd..227397980 100644
--- a/src/org/traccar/protocol/Arnavi4Protocol.java
+++ b/src/org/traccar/protocol/Arnavi4Protocol.java
@@ -2,12 +2,10 @@ package org.traccar.protocol;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.TrackerServer;
+import java.nio.ByteOrder;
import java.util.List;
/**
@@ -21,15 +19,15 @@ public class Arnavi4Protocol 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 LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("frameDecoder", new Arnavi4FrameDecoder());
pipeline.addLast("objectDecoder", new Arnavi4ProtocolDecoder(Arnavi4Protocol.this));
}
- });
+ };
+ server.setEndianness(ByteOrder.LITTLE_ENDIAN);
+ serverList.add(server);
}
}
diff --git a/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java b/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java
index dbedff9a1..caa8dd28f 100644
--- a/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java
@@ -1,87 +1,175 @@
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.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.util.regex.Pattern;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.traccar.protocol.Arnavi4FrameDecoder.*;
/**
* Created by Ivan Muratov @binakot on 11.07.2017.
*/
public class Arnavi4ProtocolDecoder extends BaseProtocolDecoder {
+ private static final byte RECORD_PING = 0x00;
+ private static final byte RECORD_DATA = 0x01;
+ private static final byte RECORD_TEXT = 0x03;
+ private static final byte RECORD_FILE = 0x04;
+ private static final byte RECORD_BINARY = 0x06;
+
+ private static final byte TAG_LATITUDE = 3;
+ private static final byte TAG_LONGITUDE = 4;
+ private static final byte TAG_COORD_PARAMS = 5;
+
public Arnavi4ProtocolDecoder(Arnavi4Protocol protocol) {
super(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$AV,")
- .number("Vd,") // type
- .number("(d+),") // device id
- .number("(d+),") // index
- .number("(d+),") // power
- .number("(d+),") // battery
- .number("-?d+,")
- .expression("[01],") // movement
- .expression("([01]),") // ignition
- .number("(d+),") // input
- .number("d+,d+,") // input 1
- .number("d+,d+,").optional() // input 2
- .expression("[01],") // fix type
- .number("(d+),") // satellites
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(dd)(dd.d+)([NS]),") // latitude
- .number("(ddd)(dd.d+)([EW]),") // longitude
- .number("(d+.d+),") // speed
- .number("(d+.d+),") // course
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .any()
- .compile();
+ private static int modulo256Checksum(byte[] bytes) {
+ int sum = 0;
+ for (byte b : bytes) {
+ sum = (sum + b) & 0xFF;
+ }
+ return sum;
+ }
+
+ private Position decodePosition(DeviceSession deviceSession, ChannelBuffer buf, long timestamp) {
+
+ final Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(timestamp));
+
+ while (buf.readableBytes() > 0) {
+ short tagId = buf.readUnsignedByte();
+ int tagValue = buf.readInt();
+ switch (tagId) {
+ case TAG_LATITUDE:
+ position.setLatitude(Float.intBitsToFloat(tagValue));
+ position.setValid(true);
+ break;
+
+ case TAG_LONGITUDE:
+ position.setLongitude(Float.intBitsToFloat(tagValue));
+ position.setValid(true);
+ break;
+
+ case TAG_COORD_PARAMS:
+ position.setSpeed((tagValue >> 24) * 1.852);
+ position.set(Position.KEY_SATELLITES, (tagValue >> 16 & 0x0F) + (tagValue >> 20 & 0x0F));
+ position.setAltitude((tagValue >> 8 & 0xFF) * 10.0);
+ position.setCourse((tagValue & 0xFF) * 2.0);
+ break;
+
+ default:
+ break; // Skip other tags
+ }
+ }
+
+ return position;
+ }
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
- if (!parser.matches()) {
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ byte startSign = buf.readByte();
+
+ if (startSign == HEADER_START_SIGN) {
+
+ byte version = buf.readByte();
+
+ String imei = String.valueOf(buf.readLong());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+
+ if (deviceSession != null && channel != null) {
+
+ final ChannelBuffer response;
+
+ if (version == HEADER_VERSION_1) {
+ response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 4);
+ response.writeBytes(new byte[]{0x7B, 0x00, 0x00, 0x7D});
+
+ } else if (version == HEADER_VERSION_2) {
+ response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 9);
+ response.writeBytes(new byte[]{0x7B, 0x04, 0x00});
+ byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) (System.currentTimeMillis() / 1000)).array();
+ response.writeByte(modulo256Checksum(timestampBytes));
+ response.writeBytes(timestampBytes);
+ response.writeByte(0x7D);
+
+ } else {
+ throw new IllegalArgumentException("unsupported header version");
+ }
+
+ channel.write(response);
+ }
+
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
- position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_INDEX, parser.nextInt(0));
- position.set(Position.KEY_POWER, parser.nextInt(0) * 0.01);
- position.set(Position.KEY_BATTERY, parser.nextInt(0) * 0.01);
- position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
- position.set(Position.KEY_INPUT, parser.nextInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
+ if (startSign == PACKAGE_START_SIGN) {
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ List<Position> positions = new LinkedList<>();
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ int parcelNumber = buf.readUnsignedByte();
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
+ byte recordStartSign = buf.readByte();
+ while (recordStartSign != PACKAGE_END_SIGN) {
+ switch (recordStartSign) {
+ case RECORD_PING:
+ case RECORD_DATA:
+ case RECORD_TEXT:
+ case RECORD_FILE:
+ case RECORD_BINARY: {
+ int length = buf.readUnsignedShort();
+ long timestamp = buf.readUnsignedInt() * 1000;
+ ChannelBuffer recordBuf = buf.readBytes(length);
- return position;
+ if (recordStartSign == RECORD_DATA) {
+ positions.add(decodePosition(deviceSession, recordBuf, timestamp));
+ }
+
+ buf.readUnsignedByte(); // crc
+
+ break;
+ }
+
+ default:
+ throw new IllegalArgumentException("unsupported record type");
+ }
+
+ recordStartSign = buf.readByte();
+ }
+
+ if (channel != null) {
+ final ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 4);
+ response.writeBytes(new byte[]{0x7B, 0x00, (byte) parcelNumber, 0x7D});
+ channel.write(response);
+ }
+
+ return positions;
+ }
+
+ return null;
}
}
diff --git a/test/org/traccar/protocol/Arnavi4FrameDecoderTest.java b/test/org/traccar/protocol/Arnavi4FrameDecoderTest.java
new file mode 100644
index 000000000..08abd3835
--- /dev/null
+++ b/test/org/traccar/protocol/Arnavi4FrameDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import java.nio.ByteOrder;
+
+/**
+ * Created by Ivan Muratov @binakot on 13.07.2017.
+ */
+public class Arnavi4FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Arnavi4FrameDecoder decoder = new Arnavi4FrameDecoder();
+
+ Assert.assertEquals( // Valid HEADER v1 packet with IMEI
+ binary(ByteOrder.LITTLE_ENDIAN, "ff22f30c45f5c90f0300"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "ff22f30c45f5c90f0300")));
+
+ Assert.assertEquals( // Valid HEADER v2 packet with IMEI
+ binary(ByteOrder.LITTLE_ENDIAN, "ff23f30c45f5c90f0300"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "ff23f30c45f5c90f0300")));
+
+ Assert.assertEquals( // Valid PACKAGE packet with one DATA packet
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ Assert.assertEquals( // Valid PACKAGE packet with two DATA packet
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ }
+
+} \ No newline at end of file
diff --git a/test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java b/test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java
index b199fe565..2395572a1 100644
--- a/test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java
@@ -3,7 +3,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-import static org.junit.Assert.*;
+import java.nio.ByteOrder;
/**
* Created by Ivan Muratov @binakot on 11.07.2017.
@@ -13,14 +13,25 @@ public class Arnavi4ProtocolDecoderTest extends ProtocolTest {
@Test
public void testDecode() throws Exception {
- Arnavi4ProtocolDecoder decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
+ Arnavi4ProtocolDecoder decoder;
- verifyPosition(decoder, text(
- "$AV,V2,32768,12487,2277,203,-1,0,0,193,0,0,1,13,200741,5950.6773N,03029.1043E,0.0,0.0,121012,*6E"));
+ decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
- verifyPosition(decoder, text(
- "$AV,V3,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
+ verifyNull(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid HEADER v1 packet with IMEI
+ "ff22f30c45f5c90f0300"));
+ verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid PACKAGE packet with one DATA packet
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+
+ decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
+
+ verifyNull(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid HEADER v2 packet with IMEI
+ "ff23f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid PACKAGE packet with two DATA packet
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
}
}