From 6e70a873886b366980423e448808b60c624e4047 Mon Sep 17 00:00:00 2001 From: Ivan Muratov Date: Thu, 13 Jul 2017 08:30:35 +0300 Subject: 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). --- .../traccar/protocol/Arnavi4ProtocolDecoder.java | 188 +++++++++++++++------ 1 file changed, 138 insertions(+), 50 deletions(-) (limited to 'src/org/traccar/protocol/Arnavi4ProtocolDecoder.java') 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 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; } } -- cgit v1.2.3