aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2013-08-21 22:49:34 +1200
committerAnton Tananaev <anton.tananaev@gmail.com>2013-08-21 22:49:34 +1200
commit0fa340eb62473efa698ddaed783340535f4b2ee0 (patch)
treef426e32bab14ee1844e760038ca698e74811b77f
parentba4cb97520ef9bbdd9be90b0c4307073469739c7 (diff)
downloadtrackermap-server-0fa340eb62473efa698ddaed783340535f4b2ee0.tar.gz
trackermap-server-0fa340eb62473efa698ddaed783340535f4b2ee0.tar.bz2
trackermap-server-0fa340eb62473efa698ddaed783340535f4b2ee0.zip
Support TianQIN protocol
-rw-r--r--src/org/traccar/ServerManager.java5
-rw-r--r--src/org/traccar/protocol/H02FrameDecoder.java56
-rw-r--r--src/org/traccar/protocol/H02ProtocolDecoder.java113
-rw-r--r--test/org/traccar/protocol/H02ProtocolDecoderTest.java65
4 files changed, 208 insertions, 31 deletions
diff --git a/src/org/traccar/ServerManager.java b/src/org/traccar/ServerManager.java
index 95f8c7b58..d149e3ea8 100644
--- a/src/org/traccar/ServerManager.java
+++ b/src/org/traccar/ServerManager.java
@@ -401,10 +401,7 @@ public class ServerManager {
serverList.add(new TrackerServer(this, new ServerBootstrap(), protocol) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- byte delimiter[] = { (byte) '#' };
- pipeline.addLast("frameDecoder",
- new DelimiterBasedFrameDecoder(1024, ChannelBuffers.wrappedBuffer(delimiter)));
- pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("frameDecoder", new H02FrameDecoder());
pipeline.addLast("objectDecoder", new H02ProtocolDecoder(ServerManager.this));
}
});
diff --git a/src/org/traccar/protocol/H02FrameDecoder.java b/src/org/traccar/protocol/H02FrameDecoder.java
new file mode 100644
index 000000000..ef28e9986
--- /dev/null
+++ b/src/org/traccar/protocol/H02FrameDecoder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import java.nio.charset.Charset;
+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;
+import org.traccar.helper.ChannelBufferTools;
+
+public class H02FrameDecoder extends FrameDecoder {
+
+ private static final int MESSAGE_LENGTH = 32;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx,
+ Channel channel,
+ ChannelBuffer buf) throws Exception {
+
+ String marker = buf.toString(buf.readerIndex(), 1, Charset.defaultCharset());
+ if (marker.equals("*")) {
+
+ // Return text message
+ Integer index = ChannelBufferTools.find(buf, buf.readerIndex(), buf.readableBytes(), "#");
+ if (index != null) {
+ return buf.readBytes(index + 1 - buf.readerIndex());
+ }
+
+ } else if (marker.equals("$")) {
+
+ // Return binary message
+ if (buf.readableBytes() >= MESSAGE_LENGTH) {
+ return buf.readBytes(MESSAGE_LENGTH);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java
index b997d5e28..7bf589bef 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/H02ProtocolDecoder.java
@@ -15,14 +15,17 @@
*/
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.BaseProtocolDecoder;
import org.traccar.ServerManager;
+import org.traccar.helper.ChannelBufferTools;
import org.traccar.helper.Log;
import org.traccar.model.ExtendedInfoFormatter;
import org.traccar.model.Position;
@@ -32,14 +35,83 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
public H02ProtocolDecoder(ServerManager serverManager) {
super(serverManager);
}
+
+ private static double readCoordinate(ChannelBuffer buf, boolean lon) {
+
+ int degrees = ChannelBufferTools.readHexInteger(buf, 2);
+ if (lon) {
+ degrees = degrees * 10 + (buf.getByte(buf.readerIndex()) >> 4);
+ }
+
+ double result = 0;
+ if (lon) {
+ result = buf.readUnsignedByte() & 0x0f;
+ }
+ result = result * 10 + ChannelBufferTools.readHexInteger(buf, lon ? 5 : 6) * 0.0001;
+
+ result /= 60;
+ result += degrees;
+
+ return result;
+ }
+
+ private Position decodeBinary(ChannelBuffer buf) {
+
+ // Create new position
+ Position position = new Position();
+ ExtendedInfoFormatter extendedInfo = new ExtendedInfoFormatter("h02");
+
+ buf.readByte(); // marker
+
+ // Identification
+ String id = String.valueOf(
+ (buf.readUnsignedInt() << 8) + buf.readUnsignedByte());
+ try {
+ position.setDeviceId(getDataManager().getDeviceByImei(id).getId());
+ } catch(Exception error) {
+ Log.warning("Unknown device - " + id);
+ return null;
+ }
+
+ // Time
+ Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ time.clear();
+ time.set(Calendar.HOUR, ChannelBufferTools.readHexInteger(buf, 2));
+ time.set(Calendar.MINUTE, ChannelBufferTools.readHexInteger(buf, 2));
+ time.set(Calendar.SECOND, ChannelBufferTools.readHexInteger(buf, 2));
+ time.set(Calendar.DAY_OF_MONTH, ChannelBufferTools.readHexInteger(buf, 2));
+ time.set(Calendar.MONTH, ChannelBufferTools.readHexInteger(buf, 2) - 1);
+ time.set(Calendar.YEAR, 2000 + ChannelBufferTools.readHexInteger(buf, 2));
+ position.setTime(time.getTime());
+
+ // Location
+ double latitude = readCoordinate(buf, false);
+ int x = buf.readByte(); // reserved
+ double longitude = readCoordinate(buf, true);
+ int flags = buf.readUnsignedByte() & 0x0f;
+ position.setValid((flags & 0x02) != 0);
+ if ((flags & 0x04) == 0) latitude = -latitude;
+ if ((flags & 0x08) == 0) longitude = -longitude;
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ position.setAltitude(0.0);
+
+ // Speed and course
+ position.setSpeed((double) ChannelBufferTools.readHexInteger(buf, 3));
+ position.setCourse((buf.readUnsignedByte() & 0x0f) * 100.0 + ChannelBufferTools.readHexInteger(buf, 2));
+
+ // Status
+ extendedInfo.set("status", ChannelBufferTools.readHexString(buf, 8));
+
+ position.setExtendedInfo(extendedInfo.toString());
+ return position;
+ }
- /**
- * Regular expressions pattern
- */
static private Pattern pattern = Pattern.compile(
- "\\*HQ," +
+ "\\*..," + // Manufacturer
"(\\d+)," + // IMEI
"V\\d," + // Version?
+ ".*" +
"(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS)
"([AV])," + // Validity
"(\\d+)(\\d{2}.\\d{4})," + // Latitude (DDMM.MMMM)
@@ -49,15 +121,12 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
"(\\d+.\\d+)," + // Speed
"(\\d+.\\d+)?," + // Course
"(\\d{2})(\\d{2})(\\d{2})," + // Date (DDMMYY)
+ "(\\p{XDigit}{8})" + // Status
".*");
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, Object msg)
- throws Exception {
+
+ private Position decodeText(String sentence) {
// Parse message
- String sentence = (String) msg;
Matcher parser = pattern.matcher(sentence);
if (!parser.matches()) {
return null;
@@ -119,11 +188,31 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1);
time.set(Calendar.YEAR, 2000 + Integer.valueOf(parser.group(index++)));
position.setTime(time.getTime());
+
+ // Status
+ extendedInfo.set("status", parser.group(index++));
- // Extended info
position.setExtendedInfo(extendedInfo.toString());
-
return position;
}
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, Object msg)
+ throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+ String marker = buf.toString(0, 1, Charset.defaultCharset());
+
+ // TODO X mode?
+
+ if (marker.equals("*")) {
+ return decodeText(buf.toString(Charset.defaultCharset()));
+ } else if (marker.equals("$")) {
+ return decodeBinary(buf);
+ }
+
+ return null;
+ }
+
}
diff --git a/test/org/traccar/protocol/H02ProtocolDecoderTest.java b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
index c01cb9212..8c998c655 100644
--- a/test/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -1,8 +1,11 @@
package org.traccar.protocol;
+import java.nio.charset.Charset;
+import org.jboss.netty.buffer.ChannelBuffers;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.Test;
+import org.traccar.helper.ChannelBufferTools;
public class H02ProtocolDecoderTest {
@@ -12,27 +15,59 @@ public class H02ProtocolDecoderTest {
H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
decoder.setDataManager(new TestDataManager());
- assertNull(decoder.decode(null, null,
- "*HQ,353588020068342,V1,000000,V,0.0000,0,0.0000,0,0.00,0.00,000000,ffffffff,000106,000002,000203,004c87,16"));
+ assertNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,353588020068342,V1,000000,V,0.0000,0,0.0000,0,0.00,0.00,000000,ffffffff,000106,000002,000203,004c87,16#", Charset.defaultCharset())));
- assertNotNull(decoder.decode(null, null,
- "*HQ,3800008786,V1,062507,V,3048.2437,N,03058.5617,E,000.00,000,250413,FFFFFBFF"));
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,3800008786,V1,062507,V,3048.2437,N,03058.5617,E,000.00,000,250413,FFFFFBFF#", Charset.defaultCharset())));
- assertNotNull(decoder.decode(null, null,
- "*HQ,123456789012345,V1,155850,A,5214.5346,N,2117.4683,E,0.00,270.90,131012,ffffffff,000000,000000,000000,000000"));
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,123456789012345,V1,155850,A,5214.5346,N,2117.4683,E,0.00,270.90,131012,ffffffff,000000,000000,000000,000000#", Charset.defaultCharset())));
- assertNotNull(decoder.decode(null, null,
- "*HQ,353588010001689,V1,221116,A,1548.8220,S,4753.1679,W,0.00,0.00,300413,ffffffff,0002d4,000004,0001cd,000047"));
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,353588010001689,V1,221116,A,1548.8220,S,4753.1679,W,0.00,0.00,300413,ffffffff,0002d4,000004,0001cd,000047#", Charset.defaultCharset())));
- assertNotNull(decoder.decode(null, null,
- "*HQ,354188045498669,V1,195200,A,701.8915,S,3450.3399,W,0.00,205.70,050213,ffffffff,000243,000000,000000"));
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,354188045498669,V1,195200,A,701.8915,S,3450.3399,W,0.00,205.70,050213,ffffffff,000243,000000,000000#", Charset.defaultCharset())));
- assertNotNull(decoder.decode(null, null,
- "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF"));
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#", Charset.defaultCharset())));
- // binary?
- // 2427051711092133391406135002584900014337822e000000ffffffffff0000
- // 2427051711092134091406135002584900014337822e000000ffffffffff0000
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V1,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S17,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S14,100,10,1,3,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S20,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S20,DONE,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,F7FFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,R8,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S23,165.165.33.250:8800,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S24,thit.gd,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#", Charset.defaultCharset())));
+
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.copiedBuffer(
+ "*TH,2020916012,V4,S1,OK,pass_word,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFD#", Charset.defaultCharset())));
+
+ int[] buf1 = {0x24,0x27,0x05,0x17,0x11,0x09,0x21,0x33,0x39,0x14,0x06,0x13,0x50,0x02,0x58,0x49,0x00,0x01,0x43,0x37,0x82,0x2e,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00};
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ChannelBufferTools.convertArray(buf1))));
+
+ int[] buf2 = {0x24,0x27,0x05,0x17,0x11,0x09,0x21,0x34,0x09,0x14,0x06,0x13,0x50,0x02,0x58,0x49,0x00,0x01,0x43,0x37,0x82,0x2e,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00};
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ChannelBufferTools.convertArray(buf2))));
+
+ int[] buf3 = {0x24,0x10,0x30,0x73,0x10,0x01,0x05,0x03,0x16,0x22,0x09,0x02,0x22,0x12,0x87,0x45,0x00,0x11,0x34,0x66,0x57,0x4C,0x01,0x40,0x28,0xff,0xff,0xfb,0xff,0xff,0x00,0x00};
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ChannelBufferTools.convertArray(buf3))));
}