From 6fc48063460a673815b182e0a8f3adf4db0fd08a Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 21 Oct 2015 15:18:55 +1300 Subject: Refactor GL200 protocol decoder --- src/org/traccar/BaseProtocolEncoder.java | 1 - src/org/traccar/helper/PatternBuilder.java | 22 +- src/org/traccar/protocol/Gl200ProtocolDecoder.java | 236 +++++++++------------ 3 files changed, 126 insertions(+), 133 deletions(-) diff --git a/src/org/traccar/BaseProtocolEncoder.java b/src/org/traccar/BaseProtocolEncoder.java index d53607e68..050697801 100644 --- a/src/org/traccar/BaseProtocolEncoder.java +++ b/src/org/traccar/BaseProtocolEncoder.java @@ -20,7 +20,6 @@ import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; -import org.traccar.model.Position; public abstract class BaseProtocolEncoder extends OneToOneEncoder { diff --git a/src/org/traccar/helper/PatternBuilder.java b/src/org/traccar/helper/PatternBuilder.java index a0e24e017..b68089b53 100644 --- a/src/org/traccar/helper/PatternBuilder.java +++ b/src/org/traccar/helper/PatternBuilder.java @@ -31,6 +31,11 @@ public class PatternBuilder { return this; } + // GRouP + public PatternBuilder grp(String s) { + return xpr("(?:").xpr(s).xpr(")"); + } + // OPtional eXpression public PatternBuilder opx(String s) { return xpr("(?:").xpr(s).xpr(")?"); @@ -42,12 +47,21 @@ public class PatternBuilder { return this; } + // OPtional Text + public PatternBuilder opt(String s) { + return xpr("(?:").txt(s).xpr(")?"); + } + // NUMber public PatternBuilder num(String s) { s = s.replace("dddd", "d{4}").replace("ddd", "d{3}").replace("dd", "d{2}"); s = s.replace("xxxx", "x{4}").replace("xxx", "x{3}").replace("xx", "x{2}"); - pattern.append(s.replace("d", "\\d").replace("x", "\\p{XDigit}").replaceAll("([\\.\\|])", "\\\\$1")); + s = s.replace("d", "\\d").replace("x", "\\p{XDigit}").replaceAll("([\\.])", "\\\\$1"); + s = s.replaceAll("\\|$", "\\\\|"); // special case for delimiter + + pattern.append(s); + return this; } @@ -70,6 +84,12 @@ public class PatternBuilder { return not(s).txt(s); } + // BINary + public PatternBuilder bin(String s) { + pattern.append(s.replaceAll("(\\p{XDigit}{2})", "\\\\$1")); + return this; + } + public PatternBuilder groupBegin() { return xpr("(?:"); } diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java index 396b0315c..20db06f9f 100644 --- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -16,13 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -33,158 +33,132 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN_HEARTBEAT = Pattern.compile( - "\\+ACK:GTHBD," + - "([0-9A-Z]{2}\\p{XDigit}{4})," + - ".*," + - "(\\p{XDigit}{4})\\$?"); - - private static final Pattern PATTERN = Pattern.compile( - "(?:(?:\\+(?:RESP|BUFF):)|" + - "(?:\\x00?\\x04,\\p{XDigit}{4},[01],))" + - "GT...," + - "(?:[0-9A-Z]{2}\\p{XDigit}{4})?," + // Protocol version - "([^,]+)," + // IMEI - - "(?:[0-9A-Z]{17}," + // VIN - "[^,]{0,20}," + // Device name - "[01]," + // Report type - "\\p{XDigit}{1,8}," + // Report mask - "[0-9A-Z]{17}," + // VIN - "[01]," + // ODB connect - "\\d{1,5}," + // ODB voltage - "\\p{XDigit}{8}," + // Support PIDs - "(\\d{1,5})," + // Engine RPM - "(\\d{1,3})," + // Speed - "(-?\\d{1,3})," + // Coolant temp - "(\\d+\\.?\\d*|Inf|NaN)?," + // Fuel consumption - "(\\d{1,5})," + // DTCs cleared distance - "\\d{1,5}," + - "([01])," + // ODB connect - "(\\d{1,3})," + // Number of DTCs - "(\\p{XDigit}*)," + // DTCs - "(\\d{1,3})," + // Throttle - "\\d{1,3}," + // Engine load - "(\\d{1,3})?," + // Fuel level - "(\\d+)|.*)," + // Odometer - - "(\\d*)," + // GPS accuracy - "(\\d+.\\d)?," + // Speed - "(\\d+)?," + // Course - "(-?\\d+\\.\\d)?," + // Altitude - "(-?\\d+\\.\\d+)," + // Longitude - "(-?\\d+\\.\\d+)," + // Latitude - "(\\d{4})(\\d{2})(\\d{2})" + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "(\\d{4})?," + // MCC - "(\\d{4})?," + // MNC - "(\\p{XDigit}{4}|\\p{XDigit}{8})?," + // LAC - "(\\p{XDigit}{4})?," + // Cell - "(?:(\\d+\\.\\d)?," + // Odometer - "(\\d{1,3})?,)?" + // Battery - ".*," + - "(\\p{XDigit}{4})\\$?"); + private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder() + .txt("+ACK:GTHBD,") + .num("([0-9A-Z]{2}xxxx),") + .any().txt(",") + .num("(xxxx)") + .opt("$") + .compile(); + + private static final Pattern PATTERN = new PatternBuilder() + .groupBegin() + .txt("+").grp("RESP|BUFF").txt(":") + .or() + .bin("00?04,") + .num("xxxx,") + .xpr("[01],") + .groupEnd(false) + .xpr("GT...,") + .opn("[0-9A-Z]{2}xxxx").txt(",") // protocol version + .xpr("([^,]+),") // imei + + .groupBegin() + .xpr("[0-9A-Z]{17},") // vin + .xpr("[^,]{0,20},") // device name + .xpr("[01],") // report type + .num("x{1,8},") // report mask + .xpr("[0-9A-Z]{17},") // vin + .num("[01],") // obd connect + .num("d{1,5},") // obd voltage + .num("x{8},") // support pids + .num("(d{1,5}),") // engine rpm + .num("(d{1,3}),") // speed + .num("(-?d{1,3}),") // coolant temp + .num("(d+.?d*|Inf|NaN)?,") // fuel consumption + .num("(d{1,5}),") // dtcs cleared distance + .num("d{1,5},") + .xpr("([01]),") // obd connect + .num("(d{1,3}),") // number of dtcs + .num("(x*),") // dtcs + .num("(d{1,3}),") // throttle + .num("d{1,3},") // engine load + .num("(d{1,3})?,") // fuel level + .num("(d+)") // odometer + .or().any() + .groupEnd(false).txt(",") + + .num("(d*),") // gps accuracy + .num("(d+.d)?,") // speed + .num("(d+)?,") // course + .num("(-?d+.d)?,") // altitude + .num("(-?d+.d+),") // longitude + .num("(-?d+.d+),") // latitude + .num("(dddd)(dd)(dd)") // date + .num("(dd)(dd)(dd),") // time + .num("(dddd)?,") // mcc + .num("(dddd)?,") // mnc + .num("(xxxx|x{8})?,") // loc + .num("(xxxx)?,") // cell + .groupBegin() + .num("(d+.d)?,") // odometer + .num("(d{1,3})?,") // battery + .groupEnd(true) + .any().txt(",") + .num("(xxxx)\\$?") + .opt("$") + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Handle heartbeat - Matcher parser = PATTERN_HEARTBEAT.matcher(sentence); + Parser parser = new Parser(PATTERN_HEARTBEAT, (String) msg); if (parser.matches()) { if (channel != null) { - channel.write("+SACK:GTHBD," + parser.group(1) + "," + parser.group(2) + "$", remoteAddress); + channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress); } return null; } - // Parse message - parser = PATTERN.matcher(sentence); + parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(parser.group(index++), channel, remoteAddress)) { + if (!identify(parser.next(), channel, remoteAddress)) { return null; } position.setDeviceId(getDeviceId()); // OBD - position.set(Event.KEY_RPM, parser.group(index++)); - position.set(Event.KEY_OBD_SPEED, parser.group(index++)); - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); - position.set("fuel-consumption", parser.group(index++)); - position.set("dtcs-cleared-distance", parser.group(index++)); - position.set("odb-connect", parser.group(index++)); - position.set("dtcs-number", parser.group(index++)); - position.set("dtcs-codes", parser.group(index++)); - position.set("throttle-position", parser.group(index++)); - position.set(Event.KEY_FUEL, parser.group(index++)); - position.set(Event.KEY_OBD_ODOMETER, parser.group(index++)); - - // Validity - position.setValid(Integer.parseInt(parser.group(index++)) < 20); - - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(speed))); - } - - // Course - String course = parser.group(index++); - if (speed != null) { - position.setCourse(Double.parseDouble(course)); - } - - // Altitude - String altitude = parser.group(index++); - if (altitude != null) { - position.setAltitude(Double.parseDouble(altitude)); - } - - // Coordinates - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setLatitude(Double.parseDouble(parser.group(index++))); - - // Date - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - - // Time - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Cell information - position.set(Event.KEY_MCC, parser.group(index++)); - position.set(Event.KEY_MNC, parser.group(index++)); - position.set(Event.KEY_LAC, parser.group(index++)); - position.set(Event.KEY_CELL, parser.group(index++)); - - // Other - String odometer = parser.group(index++); - if (odometer != null && Double.parseDouble(odometer) != 0) { - position.set(Event.KEY_ODOMETER, odometer); - } - position.set(Event.KEY_BATTERY, parser.group(index++)); + position.set(Event.KEY_RPM, parser.next()); + position.set(Event.KEY_OBD_SPEED, parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); + position.set("fuel-consumption", parser.next()); + position.set("dtcs-cleared-distance", parser.next()); + position.set("odb-connect", parser.next()); + position.set("dtcs-number", parser.next()); + position.set("dtcs-codes", parser.next()); + position.set("throttle-position", parser.next()); + position.set(Event.KEY_FUEL, parser.next()); + position.set(Event.KEY_OBD_ODOMETER, parser.next()); + + position.setValid(parser.nextInt() < 20); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.set(Event.KEY_MCC, parser.next()); + position.set(Event.KEY_MNC, parser.next()); + position.set(Event.KEY_LAC, parser.next()); + position.set(Event.KEY_CELL, parser.next()); + + position.set(Event.KEY_ODOMETER, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) { - channel.write("+SACK:" + parser.group(index++) + "$", remoteAddress); + channel.write("+SACK:" + parser.next() + "$", remoteAddress); } return position; -- cgit v1.2.3