aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
committerAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
commit59416923dcb3a756eaf532cc4259f2f6625c0762 (patch)
tree9082dae6616deac8fda432b7bfd80e4a52b6d9dc /src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
parent79a129dd6327d932133d6b9a50190d3f4927bff9 (diff)
downloadtrackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.gz
trackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.bz2
trackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.zip
Convert project to gradle
Diffstat (limited to 'src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java')
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java1266
1 files changed, 1266 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
new file mode 100644
index 000000000..aeb57a116
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -0,0 +1,1266 @@
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
+
+ private boolean ignoreFixTime;
+
+ public Gl200TextProtocolDecoder(Protocol protocol) {
+ super(protocol);
+
+ ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
+ }
+
+ private static final Pattern PATTERN_ACK = new PatternBuilder()
+ .text("+ACK:GT")
+ .expression("...,") // type
+ .number("([0-9A-Z]{2}xxxx),") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any().text(",")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_INF = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTINF,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17},)?") // vin
+ .expression("(?:[^,]+)?,") // device name
+ .number("(xx),") // state
+ .expression("(?:[0-9Ff]{20})?,") // iccid
+ .number("(d{1,2}),") // rssi
+ .number("d{1,2},")
+ .expression("[01],") // external power
+ .number("([d.]+)?,") // odometer or external power
+ .number("d*,") // backup battery or lightness
+ .number("(d+.d+),") // battery
+ .expression("([01]),") // charging
+ .number("(?:d),") // led
+ .number("(?:d)?,") // gps on need
+ .number("(?:d)?,") // gps antenna type
+ .number("(?:d)?,").optional() // gps antenna state
+ .number("d{14},") // last fix time
+ .groupBegin()
+ .number("(d+),") // battery percentage
+ .number("[d.]*,") // flash type / power
+ .number("(-?[d.]+)?,,,") // temperature
+ .or()
+ .expression("(?:[01])?,").optional() // pin15 mode
+ .number("(d+)?,") // adc1
+ .number("(d+)?,").optional() // adc2
+ .number("(xx)?,") // digital input
+ .number("(xx)?,") // digital output
+ .number("[-+]dddd,") // timezone
+ .expression("[01],") // daylight saving
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_VER = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTVER,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .expression("([^,]*),") // device type
+ .number("(xxxx),") // firmware version
+ .number("(xxxx),") // hardware version
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+)?,") // mcc
+ .number("(d+)?,") // mnc
+ .groupBegin()
+ .number("(d+),") // lac
+ .number("(d+),") // cid
+ .or()
+ .number("(x+)?,") // lac
+ .number("(x+)?,") // cid
+ .groupEnd()
+ .number("(?:d+|(d+.d))?,") // odometer
+ .compile();
+
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("+RESP:GTOBD,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .expression("[^,]{0,20},") // device name
+ .expression("[01],") // report type
+ .number("x{1,8},") // report mask
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .number("[01],") // obd connect
+ .number("(?:d{1,5})?,") // obd voltage
+ .number("(?:x{8})?,") // support pids
+ .number("(d{1,5})?,") // engine rpm
+ .number("(d{1,3})?,") // speed
+ .number("(-?d{1,3})?,") // coolant temp
+ .number("(d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d{1,5})?,") // dtcs cleared distance
+ .number("(?:d{1,5})?,")
+ .expression("([01])?,") // obd connect
+ .number("(d{1,3})?,") // number of dtcs
+ .number("(x*),") // dtcs
+ .number("(d{1,3})?,") // throttle
+ .number("(?:d{1,3})?,") // engine load
+ .number("(d{1,3})?,") // fuel level
+ .expression("(?:[0-9A],)?") // obd protocol
+ .number("(d+),") // odometer
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_FRI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:([0-9A-Z]{17}),)?") // vin
+ .expression("[^,]*,") // device name
+ .number("(d+)?,") // power
+ .number("d{1,2},").optional() // report type
+ .number("d{1,2},").optional() // count
+ .number(",").optional() // reserved
+ .number("(d+),").optional() // battery
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .groupBegin()
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,") // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .number("(d+)?,") // rpm
+ .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d+)?,") // fuel level
+ .or()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .groupEnd()
+ .any()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_ERI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTERI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(x{8}),") // mask
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,").optional() // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .expression("(.*)") // additional data
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IGN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d+,") // ignition off duration
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_LSW = new PatternBuilder()
+ .text("+RESP:").expression("GT[LT]SW,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("[01],") // type
+ .number("([01]),") // state
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IDA = new PatternBuilder()
+ .text("+RESP:GTIDA,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,,") // device name
+ .number("([^,]+),") // rfid
+ .expression("[01],") // report type
+ .number("1,") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d+.d),") // odometer
+ .text(",,,,")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_WIF = new PatternBuilder()
+ .text("+RESP:GTWIF,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(d+),") // count
+ .number("((?:x{12},-?d+,,,,)+),,,,") // wifi
+ .number("(d{1,3}),") // battery
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_GSM = new PatternBuilder()
+ .text("+RESP:GTGSM,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:STR|CTN|NMR|RTL),") // fix type
+ .expression("(.*)") // cells
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_PNA = new PatternBuilder()
+ .text("+RESP:GT").expression("P[NF]A,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d*,")
+ .number("(x{1,2}),") // report type
+ .number("d{1,2},") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_BASIC = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF)").text(":")
+ .expression("GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+),").optional(4) // cell
+ .any()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_ACK, sentence);
+ if (parser.matches()) {
+ String protocolVersion = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ if (type.equals("HBD")) {
+ if (channel != null) {
+ parser.skip(6);
+ channel.writeAndFlush(new NetworkMessage(
+ "+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress));
+ }
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, parser.nextDateTime());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, "Command " + type + " accepted");
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
+ if (parser.matches()) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private void decodeDeviceTime(Position position, Parser parser) {
+ if (parser.hasNext(6)) {
+ if (ignoreFixTime) {
+ position.setTime(parser.nextDateTime());
+ } else {
+ position.setDeviceTime(parser.nextDateTime());
+ }
+ }
+ }
+
+ private Long parseHours(String hoursString) {
+ if (hoursString != null) {
+ String[] hours = hoursString.split(":");
+ return (long) (Integer.parseInt(hours[0]) * 3600
+ + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60 : 0)
+ + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000;
+ }
+ return null;
+ }
+
+ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_INF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ switch (parser.nextHexInt()) {
+ case 0x16:
+ case 0x1A:
+ case 0x12:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x11:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x21:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x22:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x41:
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x42:
+ position.set(Position.KEY_MOTION, true);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ parser.next(); // odometer or external power
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 1);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ position.set(Position.PREFIX_TEMP + 1, parser.next());
+
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+
+ position.set(Position.KEY_INPUT, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.next());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ position.set(Position.KEY_INDEX, parser.nextHexInt());
+
+ return position;
+ }
+
+ private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_VER, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set("deviceType", parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.nextHexInt());
+ position.set(Position.KEY_VERSION_HW, parser.nextHexInt());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ return position;
+ }
+
+ private void skipLocation(Parser parser) {
+ parser.skip(19);
+ }
+
+ private void decodeLocation(Position position, Parser parser) {
+ Integer hdop = parser.nextInt();
+ position.setValid(hdop == null || hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(8)) {
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+ position.setTime(parser.nextDateTime());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt())));
+ }
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt())));
+ }
+ }
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ }
+
+ private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_OBD, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
+ position.set("dtcsClearedDistance", parser.nextInt());
+ if (parser.hasNext()) {
+ position.set("odbConnect", parser.nextInt() == 1);
+ }
+ position.set("dtcsNumber", parser.nextInt());
+ position.set("dtcsCodes", parser.next());
+ position.set(Position.KEY_THROTTLE, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt() * 1000);
+ }
+
+ decodeLocation(position, parser);
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_OBD_ODOMETER, (int) (parser.nextDouble() * 1000));
+ }
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
+ Position position = new Position(getProtocolName());
+
+ int index = 0;
+ String[] values = sentence.split(",");
+
+ index += 1; // header
+ index += 1; // protocol version
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ index += 1; // device name
+ index += 1; // report type
+ index += 1; // canbus state
+ long reportMask = Long.parseLong(values[index++], 16);
+ long reportMaskExt = 0;
+
+ if (BitUtil.check(reportMask, 0)) {
+ position.set(Position.KEY_VIN, values[index++]);
+ }
+ if (BitUtil.check(reportMask, 1)) {
+ position.set(Position.KEY_IGNITION, Integer.parseInt(values[index++]) > 0);
+ }
+ if (BitUtil.check(reportMask, 2)) {
+ position.set(Position.KEY_OBD_ODOMETER, values[index++]);
+ }
+ if (BitUtil.check(reportMask, 3) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 5) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_RPM, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 4) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1])));
+ }
+ if (BitUtil.check(reportMask, 6) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 7) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index - 1].substring(1)));
+ }
+ if (BitUtil.check(reportMask, 8) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index - 1].substring(1)));
+ }
+ if (BitUtil.check(reportMask, 9) && !values[index++].isEmpty()) {
+ position.set("range", Long.parseLong(values[index - 1]) * 100);
+ }
+ if (BitUtil.check(reportMask, 10) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1])));
+ }
+ if (BitUtil.check(reportMask, 12)) {
+ position.set("drivingHours", Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 13)) {
+ position.set("idleHours", Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) {
+ position.set("idleFuelConsumption", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 15) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) {
+ position.set("tachographInfo", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) {
+ position.set("indicators", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) {
+ position.set("lights", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) {
+ position.set("doors", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) {
+ position.set("vehicleOverspeed", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) {
+ position.set("engineOverspeed", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 29)) {
+ reportMaskExt = Long.parseLong(values[index++], 16);
+ }
+ if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) {
+ position.set("adBlueLevel", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 1) && !values[index++].isEmpty()) {
+ position.set("axleWeight1", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 2) && !values[index++].isEmpty()) {
+ position.set("axleWeight3", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 3) && !values[index++].isEmpty()) {
+ position.set("axleWeight4", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 4)) {
+ index += 1; // tachograph overspeed
+ }
+ if (BitUtil.check(reportMaskExt, 5)) {
+ index += 1; // tachograph motion
+ }
+ if (BitUtil.check(reportMaskExt, 6)) {
+ index += 1; // tachograph direction
+ }
+ if (BitUtil.check(reportMaskExt, 7) && !values[index++].isEmpty()) {
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[index - 1]) * 0.001);
+ }
+ if (BitUtil.check(reportMaskExt, 8)) {
+ index += 1; // pedal breaking factor
+ }
+ if (BitUtil.check(reportMaskExt, 9)) {
+ index += 1; // engine breaking factor
+ }
+ if (BitUtil.check(reportMaskExt, 10)) {
+ index += 1; // total accelerator kick-downs
+ }
+ if (BitUtil.check(reportMaskExt, 11)) {
+ index += 1; // total effective engine speed
+ }
+ if (BitUtil.check(reportMaskExt, 12)) {
+ index += 1; // total cruise control time
+ }
+ if (BitUtil.check(reportMaskExt, 13)) {
+ index += 1; // total accelerator kick-down time
+ }
+ if (BitUtil.check(reportMaskExt, 14)) {
+ index += 1; // total brake application
+ }
+ if (BitUtil.check(reportMaskExt, 15) && !values[index++].isEmpty()) {
+ position.set("driver1Card", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 16) && !values[index++].isEmpty()) {
+ position.set("driver2Card", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 17) && !values[index++].isEmpty()) {
+ position.set("driver1Name", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 18) && !values[index++].isEmpty()) {
+ position.set("driver2Name", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 19) && !values[index++].isEmpty()) {
+ position.set("registration", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 20)) {
+ index += 1; // expansion information
+ }
+ if (BitUtil.check(reportMaskExt, 21)) {
+ index += 1; // rapid brakings
+ }
+ if (BitUtil.check(reportMaskExt, 22)) {
+ index += 1; // rapid accelerations
+ }
+ if (BitUtil.check(reportMaskExt, 23)) {
+ index += 1; // engine torque
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ if (BitUtil.check(reportMask, 30)) {
+ while (values[index].isEmpty()) {
+ index += 1;
+ }
+ position.setValid(Integer.parseInt(values[index++]) > 0);
+ if (!values[index].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+ position.setAltitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setTime(dateFormat.parse(values[index++]));
+ } else {
+ index += 6; // no location
+ getLastLocation(position, null);
+ }
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (BitUtil.check(reportMask, 31)) {
+ index += 4; // cell
+ index += 1; // reserved
+ }
+
+ if (ignoreFixTime) {
+ position.setTime(dateFormat.parse(values[index]));
+ } else {
+ position.setDeviceTime(dateFormat.parse(values[index]));
+ }
+
+ return position;
+ }
+
+ private void decodeStatus(Position position, Parser parser) {
+ if (parser.hasNext(3)) {
+ int ignition = parser.nextHexInt();
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ }
+ }
+
+ private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_FRI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ String vin = parser.next();
+ Integer power = parser.nextInt();
+ Integer battery = parser.nextInt();
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_VIN, vin);
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ skipLocation(parser);
+
+ if (power != null && power > 10) {
+ position.set(Position.KEY_POWER, power * 0.001); // only on some devices
+ }
+ if (battery != null) {
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ }
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ decodeStatus(position, parser);
+
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_ERI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ long mask = parser.nextHexLong();
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ Integer power = parser.nextInt();
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ skipLocation(parser);
+
+ if (power != null) {
+ position.set(Position.KEY_POWER, power * 0.001);
+ }
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ decodeStatus(position, parser);
+
+ int index = 0;
+ String[] data = parser.next().split(",");
+
+ index += 1; // device type
+
+ if (BitUtil.check(mask, 0)) {
+ index += 1; // digital fuel sensor data
+ }
+
+ if (BitUtil.check(mask, 1)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // id
+ index += 1; // type
+ if (!data[index++].isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
+ }
+ }
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ index += 1; // can data
+ }
+
+ if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // type
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
+ }
+ if (BitUtil.check(mask, 4)) {
+ index += 1; // volume
+ }
+ }
+ }
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IGN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_IGNITION, sentence.contains("IGN"));
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeLsw(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_LSW, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.PREFIX_IN + (sentence.contains("LSW") ? 1 : 2), parser.nextInt() == 1);
+
+ decodeLocation(position, parser);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IDA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_WIF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ parser.nextInt(); // count
+ Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
+ while (matcher.find()) {
+ String mac = matcher.group(1).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2))));
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ return position;
+ }
+
+ private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_GSM, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ String[] data = parser.next().split(",");
+ for (int i = 0; i < 6; i++) {
+ if (!data[i * 6].isEmpty()) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]),
+ Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16),
+ Integer.parseInt(data[i * 6 + 4])));
+ }
+ }
+
+ position.setNetwork(network);
+
+ return position;
+ }
+
+ private Object decodePna(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_PNA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_ALARM, sentence.contains("PNA") ? Position.ALARM_POWER_ON : Position.ALARM_POWER_OFF);
+
+ return position;
+ }
+
+ private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int reportType = parser.nextHexInt();
+ if (type.equals("NMR")) {
+ position.set(Position.KEY_MOTION, reportType == 1);
+ } else if (type.equals("SOS")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ } else if (type.equals("DIS")) {
+ position.set(Position.PREFIX_IN + reportType / 0x10, reportType % 0x10 == 1);
+ } else if (type.equals("IGL")) {
+ position.set(Position.KEY_IGNITION, reportType % 0x10 == 1);
+ }
+
+ decodeLocation(position, parser);
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+
+ decodeDeviceTime(position, parser);
+
+ if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
+ channel.writeAndFlush(new NetworkMessage("+SACK:" + parser.next() + "$", remoteAddress));
+ }
+
+ return position;
+ }
+
+ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_BASIC, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ if (parser.hasNext()) {
+ int hdop = parser.nextInt();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+ }
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(2)) {
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ position.setTime(parser.nextDateTime());
+ }
+
+ if (parser.hasNext(4)) {
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt())));
+ }
+
+ decodeDeviceTime(position, parser);
+
+ switch (type) {
+ case "TOW":
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case "IDL":
+ position.set(Position.KEY_ALARM, Position.ALARM_IDLE);
+ break;
+ case "PNA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
+ break;
+ case "PFA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+ break;
+ case "EPN":
+ case "MPN":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case "EPF":
+ case "MPF":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case "BPL":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case "STT":
+ position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
+ break;
+ case "SWG":
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
+ break;
+ case "TMP":
+ case "TEM":
+ position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
+ break;
+ case "JDR":
+ case "JDS":
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ break;
+ default:
+ break;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII);
+
+ int typeIndex = sentence.indexOf(":GT");
+ if (typeIndex < 0) {
+ return null;
+ }
+
+ Object result;
+ String type = sentence.substring(typeIndex + 3, typeIndex + 6);
+ if (sentence.startsWith("+ACK")) {
+ result = decodeAck(channel, remoteAddress, sentence, type);
+ } else {
+ switch (type) {
+ case "INF":
+ result = decodeInf(channel, remoteAddress, sentence);
+ break;
+ case "OBD":
+ result = decodeObd(channel, remoteAddress, sentence);
+ break;
+ case "CAN":
+ result = decodeCan(channel, remoteAddress, sentence);
+ break;
+ case "FRI":
+ case "GEO":
+ case "STR":
+ result = decodeFri(channel, remoteAddress, sentence);
+ break;
+ case "ERI":
+ result = decodeEri(channel, remoteAddress, sentence);
+ break;
+ case "IGN":
+ case "IGF":
+ result = decodeIgn(channel, remoteAddress, sentence);
+ break;
+ case "LSW":
+ case "TSW":
+ result = decodeLsw(channel, remoteAddress, sentence);
+ break;
+ case "IDA":
+ result = decodeIda(channel, remoteAddress, sentence);
+ break;
+ case "WIF":
+ result = decodeWif(channel, remoteAddress, sentence);
+ break;
+ case "GSM":
+ result = decodeGsm(channel, remoteAddress, sentence);
+ break;
+ case "VER":
+ result = decodeVer(channel, remoteAddress, sentence);
+ break;
+ case "PNA":
+ case "PFA":
+ result = decodePna(channel, remoteAddress, sentence);
+ break;
+ default:
+ result = decodeOther(channel, remoteAddress, sentence, type);
+ break;
+ }
+
+ if (result == null) {
+ result = decodeBasic(channel, remoteAddress, sentence, type);
+ }
+
+ if (result != null) {
+ if (result instanceof Position) {
+ ((Position) result).set(Position.KEY_TYPE, type);
+ } else {
+ for (Position p : (List<Position>) result) {
+ p.set(Position.KEY_TYPE, type);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+}