aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--setup/default.xml1
-rw-r--r--src/org/traccar/helper/Checksum.java8
-rw-r--r--src/org/traccar/protocol/Arnavi4FrameDecoder.java51
-rw-r--r--src/org/traccar/protocol/Arnavi4Protocol.java33
-rw-r--r--src/org/traccar/protocol/Arnavi4ProtocolDecoder.java168
-rw-r--r--test/org/traccar/helper/ChecksumTest.java8
-rw-r--r--test/org/traccar/protocol/Arnavi4FrameDecoderTest.java37
-rw-r--r--test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java37
8 files changed, 343 insertions, 0 deletions
diff --git a/setup/default.xml b/setup/default.xml
index 2fc6c3cf0..223b9a585 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -579,5 +579,6 @@
<entry key='vt200.port'>5143</entry>
<entry key='owntracks.port'>5144</entry>
<entry key='vtfms.port'>5145</entry>
+ <entry key='arnavi4.port'>5146</entry>
</properties>
diff --git a/src/org/traccar/helper/Checksum.java b/src/org/traccar/helper/Checksum.java
index 43ba6a689..7a0151d36 100644
--- a/src/org/traccar/helper/Checksum.java
+++ b/src/org/traccar/helper/Checksum.java
@@ -243,4 +243,12 @@ public final class Checksum {
return (10 - (checksum % 10)) % 10;
}
+ public static int modulo256(byte... bytes) {
+ int sum = 0;
+ for (byte b : bytes) {
+ sum = (sum + b) & 0xFF;
+ }
+ return sum;
+ }
+
}
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
new file mode 100644
index 000000000..227397980
--- /dev/null
+++ b/src/org/traccar/protocol/Arnavi4Protocol.java
@@ -0,0 +1,33 @@
+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;
+
+/**
+ * Created by Ivan Muratov @binakot on 11.07.2017.
+ */
+public class Arnavi4Protocol extends BaseProtocol {
+
+ public Arnavi4Protocol() {
+ super("arnavi4");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ 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
new file mode 100644
index 000000000..46caebb71
--- /dev/null
+++ b/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java
@@ -0,0 +1,168 @@
+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.Checksum;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+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 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 {
+
+ 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(Checksum.modulo256(timestampBytes));
+ response.writeBytes(timestampBytes);
+ response.writeByte(0x7D);
+
+ } else {
+ throw new IllegalArgumentException("unsupported header version");
+ }
+
+ channel.write(response);
+ }
+
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (startSign == PACKAGE_START_SIGN) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int parcelNumber = buf.readUnsignedByte();
+
+ 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);
+
+ 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/helper/ChecksumTest.java b/test/org/traccar/helper/ChecksumTest.java
index c37eda88d..737b65c62 100644
--- a/test/org/traccar/helper/ChecksumTest.java
+++ b/test/org/traccar/helper/ChecksumTest.java
@@ -4,6 +4,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.junit.Assert;
import org.junit.Test;
+import org.traccar.protocol.Arnavi4ProtocolDecoder;
import java.nio.charset.StandardCharsets;
@@ -28,4 +29,11 @@ public class ChecksumTest {
Assert.assertEquals(0, Checksum.luhn(63070019470771L));
}
+ @Test
+ public void testModulo256() {
+ Assert.assertEquals(0x00, Checksum.modulo256((byte)0x00));
+ Assert.assertEquals(0x00, Checksum.modulo256((byte)0x00, (byte)0x00, (byte)0x00));
+ Assert.assertEquals(0x06, Checksum.modulo256((byte)0x01, (byte)0x02, (byte)0x03));
+ }
+
}
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
new file mode 100644
index 000000000..2395572a1
--- /dev/null
+++ b/test/org/traccar/protocol/Arnavi4ProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import java.nio.ByteOrder;
+
+/**
+ * Created by Ivan Muratov @binakot on 11.07.2017.
+ */
+public class Arnavi4ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Arnavi4ProtocolDecoder decoder;
+
+ decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
+
+ 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));
+ }
+
+}