aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar
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 /src/org/traccar
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).
Diffstat (limited to 'src/org/traccar')
-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
3 files changed, 195 insertions, 58 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;
}
}