From 57fba5c08cb03fffb360a8a4a3ae530ad94e7679 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 14 Sep 2013 22:38:40 +1200 Subject: Add third totem format --- src/org/traccar/ServerManager.java | 2 +- src/org/traccar/protocol/TotemFrameDecoder.java | 51 ++++ src/org/traccar/protocol/TotemProtocolDecoder.java | 264 +++++++++++++++------ .../traccar/protocol/TotemProtocolDecoderTest.java | 3 + 4 files changed, 245 insertions(+), 75 deletions(-) create mode 100644 src/org/traccar/protocol/TotemFrameDecoder.java diff --git a/src/org/traccar/ServerManager.java b/src/org/traccar/ServerManager.java index a997cb06f..25ef3177a 100644 --- a/src/org/traccar/ServerManager.java +++ b/src/org/traccar/ServerManager.java @@ -945,7 +945,7 @@ public class ServerManager { serverList.add(new TrackerServer(this, new ServerBootstrap(), protocol) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); + pipeline.addLast("frameDecoder", new TotemFrameDecoder()); pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TotemProtocolDecoder(ServerManager.this)); } diff --git a/src/org/traccar/protocol/TotemFrameDecoder.java b/src/org/traccar/protocol/TotemFrameDecoder.java new file mode 100644 index 000000000..84a14dac6 --- /dev/null +++ b/src/org/traccar/protocol/TotemFrameDecoder.java @@ -0,0 +1,51 @@ +/* + * 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; + +public class TotemFrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, + Channel channel, + ChannelBuffer buf) throws Exception { + + // Check minimum length + if (buf.readableBytes() < 10) { + return null; + } + + // Trim end line + if (buf.getUnsignedShort(buf.readerIndex()) == 0x0d0a) { + buf.skipBytes(2); + } + + // Read message + int length = Integer.parseInt(buf.toString(2, 2, Charset.defaultCharset()), 16) + 1; + if (length >= buf.readableBytes()) { + return buf.readBytes(length); + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java index 55698893c..58e975730 100644 --- a/src/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/org/traccar/protocol/TotemProtocolDecoder.java @@ -90,18 +90,69 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { "\\d+\\|" + // Serial Number "\\p{XDigit}{4}"); // Checksum + static private Pattern patternThird = Pattern.compile( + "\\$\\$" + // Header + "\\p{XDigit}{2}" + // Length + "(\\d+)\\|" + // IMEI + ".." + // Alarm Type + "(\\d{2})(\\d{2})(\\d{2})" + // Date (YYMMDD) + "(\\d{2})(\\d{2})(\\d{2})" + // Time (HHMMSS) + "(\\p{XDigit}{4})" + // IO Status + "[01]" + // Charging + "(\\d{2})" + // Battery + "(\\d{2})" + // External Power + "(\\d{4})" + // ADC 1 + "(\\d{4})" + // ADC 2 + "(\\d{3})" + // Temperature 1 + "(\\d{3})" + // Temperature 2 + "(\\p{XDigit}{8})" + // Location Code + "([AV])" + // Validity + "(\\d{2})" + // Satellites + "(\\d{3})" + // Course + "(\\d{3})" + // Speed + "(\\d{2}\\.\\d)" + // PDOP + "(\\d{7})" + // Milage + "(\\d{2})(\\d{2}\\.\\d{4})" + // Latitude (DDMM.MMMM) + "([NS])" + + "(\\d{3})(\\d{2}\\.\\d{4})" + // Longitude (DDDMM.MMMM) + "([EW])" + + "\\d{4}" + // Serial Number + "\\p{XDigit}{4}"); // Checksum + + private enum MessageFormat { + first, + second, + third + } + @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { String sentence = (String) msg; - - boolean first = sentence.contains("$GPRMC"); + + // Determine format + MessageFormat format = MessageFormat.third; + if (sentence.contains("$GPRMC")) { + format = MessageFormat.first; + } else { + int index = sentence.indexOf('|'); + if (index != -1 && sentence.indexOf('|', index + 1) != -1) { + format = MessageFormat.second; + } + } // Parse message - Matcher parser = first ? patternFirst.matcher(sentence) : patternSecond.matcher(sentence); - if (!parser.matches()) { + Matcher parser = null; + if (format == MessageFormat.first) { + parser = patternFirst.matcher(sentence); + } else if (format == MessageFormat.second) { + parser = patternSecond.matcher(sentence); + } else if (format == MessageFormat.third) { + parser = patternThird.matcher(sentence); + } + if (parser == null || !parser.matches()) { return null; } @@ -119,88 +170,153 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { Log.warning("Unknown device - " + imei); return null; } + + if (format == MessageFormat.first || format == MessageFormat.second) { - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - int year = 0; - if (!first) { - time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); - year = Integer.valueOf(parser.group(index++)); - time.set(Calendar.YEAR, 2000 + year); - } - time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++))); - time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++))); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false); - - // Latitude - Double latitude = Double.valueOf(parser.group(index++)); - latitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.valueOf(parser.group(index++)); - longitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.valueOf(speed)); - } else { - position.setSpeed(0.0); - } + // Time + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + time.clear(); + int year = 0; + if (format == MessageFormat.second) { + time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); + time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); + year = Integer.valueOf(parser.group(index++)); + time.set(Calendar.YEAR, 2000 + year); + } + time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++))); + time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++))); + time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++))); - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.valueOf(course)); - } else { - position.setCourse(0.0); - } + // Validity + position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false); - // Date - if (first) { - time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); + // Latitude + Double latitude = Double.valueOf(parser.group(index++)); + latitude += Double.valueOf(parser.group(index++)) / 60; + if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; + position.setLatitude(latitude); + + // Longitude + Double longitude = Double.valueOf(parser.group(index++)); + longitude += Double.valueOf(parser.group(index++)) / 60; + if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; + position.setLongitude(longitude); + + // Speed + String speed = parser.group(index++); + if (speed != null) { + position.setSpeed(Double.valueOf(speed)); + } else { + position.setSpeed(0.0); + } + + // Course + String course = parser.group(index++); + if (course != null) { + position.setCourse(Double.valueOf(course)); + } else { + position.setCourse(0.0); + } + + // Date + if (format == MessageFormat.first) { + time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); + time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); + year = Integer.valueOf(parser.group(index++)); + time.set(Calendar.YEAR, 2000 + year); + } + if (year == 0) { + return null; // ignore invalid data + } + position.setTime(time.getTime()); + + // Altitude + position.setAltitude(0.0); + + // Accuracy + extendedInfo.set("hdop", parser.group(index++)); + + // IO Status + extendedInfo.set("io", parser.group(index++)); + + // Power + extendedInfo.set("battery", parser.group(index++)); + position.setPower(Double.valueOf(parser.group(index++))); + + // ADC + extendedInfo.set("adc", parser.group(index++)); + + // Location Code + extendedInfo.set("lac", parser.group(index++)); + + // Temperature + extendedInfo.set("temperature", parser.group(index++)); + + // Milage + extendedInfo.set("milage", parser.group(index++)); + + } else if (format == MessageFormat.third) { + + // Time + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + time.clear(); + time.set(Calendar.YEAR, 2000 + Integer.valueOf(parser.group(index++))); time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); - year = Integer.valueOf(parser.group(index++)); - time.set(Calendar.YEAR, 2000 + year); - } - if (year == 0) { - return null; // ignore invalid data - } - position.setTime(time.getTime()); + time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); + time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++))); + time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++))); + time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++))); + position.setTime(time.getTime()); + + // IO Status + extendedInfo.set("io", parser.group(index++)); + + // Power + extendedInfo.set("battery", Double.valueOf(parser.group(index++)) / 10); + position.setPower(Double.valueOf(parser.group(index++))); - // Altitude - position.setAltitude(0.0); + // ADC + extendedInfo.set("adc1", parser.group(index++)); + extendedInfo.set("adc2", parser.group(index++)); - // Accuracy - extendedInfo.set("hdop", parser.group(index++)); + // Temperature + extendedInfo.set("temperature1", parser.group(index++)); + extendedInfo.set("temperature2", parser.group(index++)); - // IO Status - extendedInfo.set("io", parser.group(index++)); + // Location Code + extendedInfo.set("lac", parser.group(index++)); - // Power - extendedInfo.set("battery", parser.group(index++)); - position.setPower(Double.valueOf(parser.group(index++))); + // Validity + position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false); - // ADC - extendedInfo.set("adc", parser.group(index++)); + // Satellites + extendedInfo.set("satellites", parser.group(index++)); - // Location Code - extendedInfo.set("lac", parser.group(index++)); + // Course + position.setCourse(Double.valueOf(parser.group(index++))); - // Temperature - extendedInfo.set("temperature", parser.group(index++)); + // Speed + position.setSpeed(Double.valueOf(parser.group(index++))); - // Milage - extendedInfo.set("milage", parser.group(index++)); + // PDOP + extendedInfo.set("pdop", parser.group(index++)); + + // Milage + extendedInfo.set("milage", parser.group(index++)); + + // Latitude + Double latitude = Double.valueOf(parser.group(index++)); + latitude += Double.valueOf(parser.group(index++)) / 60; + if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; + position.setLatitude(latitude); + + // Longitude + Double longitude = Double.valueOf(parser.group(index++)); + longitude += Double.valueOf(parser.group(index++)) / 60; + if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; + position.setLongitude(longitude); + + } // Extended info position.setExtendedInfo(extendedInfo.toString()); diff --git a/test/org/traccar/protocol/TotemProtocolDecoderTest.java b/test/org/traccar/protocol/TotemProtocolDecoderTest.java index f241088d2..9b5a1d349 100644 --- a/test/org/traccar/protocol/TotemProtocolDecoderTest.java +++ b/test/org/traccar/protocol/TotemProtocolDecoderTest.java @@ -24,6 +24,9 @@ public class TotemProtocolDecoderTest { assertNotNull(decoder.decode(null, null, "$$8B862170017861566|AA180613080657|A|2237.1901|N|11402.1369|E|1.579|178|8.70|100000001000|13811|00000000|253162F5|00000000|0.0000|0014|2B16")); + assertNotNull(decoder.decode(null, null, + "$$72862170017856731|3913090911165280000370000000000000000019BD508A0400000003.400000093644.9817N01012.9944E00506F2E")); + } } -- cgit v1.2.3