From 0fa340eb62473efa698ddaed783340535f4b2ee0 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 21 Aug 2013 22:49:34 +1200 Subject: Support TianQIN protocol --- src/org/traccar/ServerManager.java | 5 +- src/org/traccar/protocol/H02FrameDecoder.java | 56 +++++++++++ src/org/traccar/protocol/H02ProtocolDecoder.java | 113 ++++++++++++++++++++--- 3 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 src/org/traccar/protocol/H02FrameDecoder.java (limited to 'src') 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; + } + } -- cgit v1.2.3