From 957740cfb5d74098971a8d4186eda4f9383774d8 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 25 Oct 2015 11:06:34 +1300 Subject: Refactor Totem protocol decoder --- src/org/traccar/helper/DateBuilder.java | 19 -- src/org/traccar/helper/PatternBuilder.java | 11 +- src/org/traccar/protocol/TotemProtocolDecoder.java | 224 +++++++-------------- test/org/traccar/helper/PatternBuilderTest.java | 3 + .../traccar/protocol/TotemProtocolDecoderTest.java | 9 +- 5 files changed, 90 insertions(+), 176 deletions(-) diff --git a/src/org/traccar/helper/DateBuilder.java b/src/org/traccar/helper/DateBuilder.java index 094dfff16..77c6821aa 100644 --- a/src/org/traccar/helper/DateBuilder.java +++ b/src/org/traccar/helper/DateBuilder.java @@ -60,16 +60,6 @@ public class DateBuilder { return setDate(year, month, day); } - @Deprecated - public DateBuilder setDate(String year, String month, String day) { - return setDate(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day)); - } - - @Deprecated - public DateBuilder setDateReverse(String day, String month, String year) { - return setDate(year, month, day); - } - public DateBuilder setCurrentDate() { Calendar now = Calendar.getInstance(calendar.getTimeZone()); return setYear(now.get(Calendar.YEAR)).setMonth(now.get(Calendar.MONTH)).setDay(now.get(Calendar.DAY_OF_MONTH)); @@ -104,19 +94,10 @@ public class DateBuilder { return setHour(hour).setMinute(minute).setSecond(second); } - @Deprecated - public DateBuilder setTime(String hour, String minute, String second) { - return setTime(Integer.parseInt(hour), Integer.parseInt(minute), Integer.parseInt(second)); - } - public DateBuilder setTime(int hour, int minute, int second, int millis) { return setHour(hour).setMinute(minute).setSecond(second).setMillis(millis); } - public DateBuilder setDateTime(int year, int month, int day, int hour, int minute, int second) { - return setDate(year, month, day).setTime(hour, minute, second); - } - public Date getDate() { return calendar.getTime(); } diff --git a/src/org/traccar/helper/PatternBuilder.java b/src/org/traccar/helper/PatternBuilder.java index 5d9d3afa8..4ce4295dd 100644 --- a/src/org/traccar/helper/PatternBuilder.java +++ b/src/org/traccar/helper/PatternBuilder.java @@ -33,6 +33,8 @@ public class PatternBuilder { } public PatternBuilder expression(String s) { + s = s.replaceAll("\\|$", "\\\\|"); // special case for delimiter + fragments.add(s); return this; } @@ -63,6 +65,11 @@ public class PatternBuilder { return this; } + public PatternBuilder or() { + fragments.add("|"); + return this; + } + public PatternBuilder groupBegin() { return expression("(?:"); } @@ -75,10 +82,6 @@ public class PatternBuilder { return expression(")" + s); } - public PatternBuilder or() { - return expression("|"); - } - public Pattern compile() { return Pattern.compile(toString(), Pattern.DOTALL); } diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java index 60285d360..556a9fa1e 100644 --- a/src/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/org/traccar/protocol/TotemProtocolDecoder.java @@ -22,6 +22,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -65,32 +67,33 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private static final Pattern PATTERN2 = Pattern.compile( - "\\$\\$" + // Header - "\\p{XDigit}{2}" + // Length - "(\\d+)\\|" + // IMEI - "(..)" + // Alarm Type - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - "(\\d{2})(\\d{2})(\\d{2})\\|" + // Time (HHMMSS) - "([AV])\\|" + // Validity - "(\\d+)(\\d{2}\\.\\d+)\\|" + // Latitude (DDMM.MMMM) - "([NS])\\|" + - "(\\d+)(\\d{2}\\.\\d+)\\|" + // Longitude (DDDMM.MMMM) - "([EW])\\|" + - "(\\d+\\.\\d+)?\\|" + // Speed - "(\\d+)?\\|" + // Course - "(\\d+\\.\\d+)\\|" + // HDOP - "(\\d+)\\|" + // IO Status - "\\d" + // Charged - "(\\d{2})" + // Battery - "(\\d{2})\\|" + // External Power - "(\\d+)\\|" + // ADC - "(\\p{XDigit}{8})\\|" + // Location Code - "(\\d+)\\|" + // Temperature - "(\\d+.\\d+)\\|" + // Odometer - "\\d+\\|" + // Serial Number - "\\p{XDigit}{4}" + // Checksum - "\r?\n?"); + private static final Pattern PATTERN2 = new PatternBuilder() + .text("$$") // header + .number("xx") // length + .number("(d+)|") // imei + .expression("(..)") // alarm type + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd)|") // time + .expression("([AV])|") // validity + .number("(d+)(dd.d+)|") // latitude + .expression("([NS])|") + .number("(d+)(dd.d+)|") // longitude + .expression("([EW])|") + .number("(d+.d+)?|") // speed + .number("(d+)?|") // course + .number("(d+.d+)|") // hdop + .number("(d+)|") // io status + .number("d") // charged + .number("(dd)") // battery + .number("(dd)|") // external power + .number("(d+)|") // adc + .number("(x{8})|") // location code + .number("(d+)|") // temperature + .number("(d+.d+)|") // odometer + .number("d+|") // serial number + .number("xxxx") // checksum + .any() + .compile(); private static final Pattern PATTERN3 = new PatternBuilder() .text("$$") // header @@ -138,164 +141,85 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } - // Parse message - Matcher parser = pattern.matcher(sentence); + Parser parser = new Parser(pattern, sentence); 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)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Alarm type - position.set(Event.KEY_ALARM, parser.group(index++)); + position.set(Event.KEY_ALARM, parser.next()); if (pattern == PATTERN1 || pattern == PATTERN2) { - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); + DateBuilder dateBuilder = new DateBuilder(); int year = 0; if (pattern == PATTERN2) { - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - year = Integer.parseInt(parser.group(index++)); - time.set(Calendar.YEAR, 2000 + year); - } - 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++))); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(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.parseDouble(speed)); + dateBuilder.setDay(parser.nextInt()).setMonth(parser.nextInt()); + year = parser.nextInt(); + dateBuilder.setYear(year); } + dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // Date if (pattern == PATTERN1) { - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - year = Integer.parseInt(parser.group(index++)); - time.set(Calendar.YEAR, 2000 + year); + dateBuilder.setDay(parser.nextInt()).setMonth(parser.nextInt()); + year = parser.nextInt(); + dateBuilder.setYear(year); } if (year == 0) { return null; // ignore invalid data } - position.setTime(time.getTime()); - - // Accuracy - position.set(Event.KEY_HDOP, parser.group(index++)); - - // IO Status - position.set(Event.PREFIX_IO + 1, parser.group(index++)); - - // Power - position.set(Event.KEY_BATTERY, parser.group(index++)); - position.set(Event.KEY_POWER, Double.parseDouble(parser.group(index++))); - - // ADC - position.set(Event.PREFIX_ADC + 1, parser.group(index++)); - - // Location Code - position.set(Event.KEY_LAC, parser.group(index++)); + position.setTime(dateBuilder.getDate()); - // Temperature - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); - - // Odometer - position.set(Event.KEY_ODOMETER, parser.group(index++)); + position.set(Event.KEY_HDOP, parser.next()); + position.set(Event.PREFIX_IO + 1, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_POWER, parser.nextDouble()); + position.set(Event.PREFIX_ADC + 1, parser.next()); + position.set(Event.KEY_LAC, parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); + position.set(Event.KEY_ODOMETER, parser.next()); } else if (pattern == PATTERN3) { - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + 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.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()); - - // IO Status - position.set(Event.PREFIX_IO + 1, parser.group(index++)); - - // Power - position.set(Event.KEY_BATTERY, Double.parseDouble(parser.group(index++)) / 10); - position.set(Event.KEY_POWER, Double.parseDouble(parser.group(index++))); - - // ADC - position.set(Event.PREFIX_ADC + 1, parser.group(index++)); - position.set(Event.PREFIX_ADC + 2, parser.group(index++)); - - // Temperature - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); - position.set(Event.PREFIX_TEMP + 2, parser.group(index++)); - - // Location Code - position.set(Event.KEY_LAC, parser.group(index++)); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Satellites - position.set(Event.KEY_SATELLITES, parser.group(index++)); + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.set(Event.PREFIX_IO + 1, parser.next()); + position.set(Event.KEY_BATTERY, parser.nextDouble() / 10); + position.set(Event.KEY_POWER, parser.nextDouble()); + position.set(Event.PREFIX_ADC + 1, parser.next()); + position.set(Event.PREFIX_ADC + 2, parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); + position.set(Event.PREFIX_TEMP + 2, parser.next()); + position.set(Event.KEY_LAC, parser.next()); - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); + position.setValid(parser.next().equals("A")); + position.set(Event.KEY_SATELLITES, parser.next()); - // PDOP - position.set("pdop", parser.group(index++)); + position.setCourse(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); - // Odometer - position.set(Event.KEY_ODOMETER, parser.group(index++)); + position.set("pdop", parser.next()); - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + position.set(Event.KEY_ODOMETER, parser.next()); - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); } diff --git a/test/org/traccar/helper/PatternBuilderTest.java b/test/org/traccar/helper/PatternBuilderTest.java index 427161758..10621693e 100644 --- a/test/org/traccar/helper/PatternBuilderTest.java +++ b/test/org/traccar/helper/PatternBuilderTest.java @@ -10,6 +10,9 @@ public class PatternBuilderTest { Assert.assertEquals("\\$GPRMC", new PatternBuilder().text("$GPRMC").toString()); Assert.assertEquals("(\\d{2}\\.\\p{XDigit}+)", new PatternBuilder().number("(dd.x+)").toString()); Assert.assertEquals("a(?:bc)?", new PatternBuilder().text("a").text("b").text("c").optional(2).toString()); + Assert.assertEquals("a|b", new PatternBuilder().expression("a|b").toString()); + Assert.assertEquals("ab\\|", new PatternBuilder().expression("ab|").toString()); + Assert.assertEquals("|", new PatternBuilder().or().toString()); } } diff --git a/test/org/traccar/protocol/TotemProtocolDecoderTest.java b/test/org/traccar/protocol/TotemProtocolDecoderTest.java index 11bf03c85..373fbd6d1 100644 --- a/test/org/traccar/protocol/TotemProtocolDecoderTest.java +++ b/test/org/traccar/protocol/TotemProtocolDecoderTest.java @@ -14,10 +14,12 @@ public class TotemProtocolDecoderTest extends ProtocolDecoderTest { // $$0108AA864244026063437|1A0000001401010101014111000027BA0E57003100000000.000000000000.0000N00000.0000E048156 verifyPosition(decoder, text( - "$$BE863771024392112|AA$GPRMC,044704.000,A,1439.3334,N,12059.1417,E,0.00,0.00,200815,,,A*67|01.7|00.8|01.4|000000000000|20150820044704|14291265|00000000|4EECBF8B31|0000|0.0000|0002|00000|56E7")); + "$$BE863771024392112|AA$GPRMC,044704.000,A,1439.3334,N,12059.1417,E,0.00,0.00,200815,,,A*67|01.7|00.8|01.4|000000000000|20150820044704|14291265|00000000|4EECBF8B31|0000|0.0000|0002|00000|56E7"), + position("2015-08-20 04:47:04.000", true, 14.65556, 120.98570)); verifyPosition(decoder, text( - "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324")); + "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324"), + position("2015-03-14 05:10:02.000", true, 4.65437, 101.14907)); verifyPosition(decoder, text( "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324\r")); @@ -32,7 +34,8 @@ public class TotemProtocolDecoderTest extends ProtocolDecoderTest { "$$B2359772032984289|AA$GPRMC,104446.000,A,5011.3944,N,01439.6637,E,0.00,,290212,,,A*7D|01.8|00.9|01.5|000000100000|20120229104446|14151221|00050000|046D085E|0000|0.0000|1170|29A7")); verifyPosition(decoder, text( - "$$8B862170017861566|AA180613080657|A|2237.1901|N|11402.1369|E|1.579|178|8.70|100000001000|13811|00000000|253162F5|00000000|0.0000|0014|2B16")); + "$$8B862170017861566|AA180613080657|A|2237.1901|N|11402.1369|E|1.579|178|8.70|100000001000|13811|00000000|253162F5|00000000|0.0000|0014|2B16"), + position("2013-06-18 08:06:57.000", true, 22.61984, 114.03562)); verifyPosition(decoder, text( "$$72862170017856731|3913090911165280000370000000000000000019BD508A0400000003.400000093644.9817N01012.9944E00506F2E")); -- cgit v1.2.3