diff options
author | Iván Ávalos <avalos@disroot.org> | 2024-06-14 21:08:21 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2024-06-14 21:08:21 -0600 |
commit | 471dc4ca7b6cfd656cc2c04c526fe56ee538991c (patch) | |
tree | 4766fa7209e2eaab65269db456cf0436e6a64a49 /src/main/java/org/traccar/protocol | |
parent | 447c7e15fcec8fc72d0457bb7dbf166cbea84acd (diff) | |
parent | 64528b96da4a742070d5845a876b07ca66ad0be3 (diff) | |
download | trackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.tar.gz trackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.tar.bz2 trackermap-server-471dc4ca7b6cfd656cc2c04c526fe56ee538991c.zip |
Merge tag 'v6.2'
Diffstat (limited to 'src/main/java/org/traccar/protocol')
12 files changed, 524 insertions, 109 deletions
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java index b10ff4c64..ff192807b 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -92,7 +92,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { .number("C(d+);") // coolant temperature .number("L(d+.d);") // fuel level .number("[XY][MH]d+.d+;") - .number("M(d+);") // mileage + .number("Md+.?d*;") // mileage .number("F(d+.d+);") // fuel consumption .number("T(d+);") // engine time .any() @@ -264,7 +264,6 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ENGINE_LOAD, parser.nextDouble()); position.set(Position.KEY_COOLANT_TEMP, parser.nextInt()); position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble()); - position.set(Position.KEY_ODOMETER, parser.nextInt()); position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble()); position.set(Position.KEY_HOURS, parser.nextInt()); diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 775e98401..8edce9346 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -974,6 +974,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (model.startsWith("GV") && !model.startsWith("GV6")) { position.set(Position.PREFIX_ADC + 2, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001); } + if (model.startsWith("GV355CEU")) { + index += 1; // reserved + } position.set(Position.KEY_BATTERY_LEVEL, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1])); if (model.startsWith("GL5")) { diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java index b75e612b8..654071d22 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 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. @@ -27,7 +27,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.config.Keys; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -65,12 +64,6 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { private final XPath xPath; private final XPathExpression messageExpression; - private boolean alternative; - - public void setAlternative(boolean alternative) { - this.alternative = alternative; - } - public GlobalstarProtocolDecoder(Protocol protocol) { super(protocol); try { @@ -89,11 +82,6 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { } } - @Override - protected void init() { - this.alternative = getConfig().getBoolean(Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName())); - } - private void sendResponse(Channel channel, String messageId) throws TransformerException { Document document = documentBuilder.newDocument(); @@ -144,6 +132,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, xPath.evaluate("esn", node)); if (deviceSession != null) { + boolean atlas = "AtlasTrax".equalsIgnoreCase(getDeviceModel(deviceSession)); Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -154,7 +143,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { int flags = buf.readUnsignedByte(); int type; - if (alternative) { + if (atlas) { type = BitUtil.to(flags, 1); position.setValid(true); position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1)); @@ -179,7 +168,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { position.setLongitude(longitude > 180 ? longitude - 360 : longitude); int speed = 0; - if (alternative) { + if (atlas) { speed = buf.readUnsignedByte(); position.setSpeed(UnitsConverter.knotsFromKph(speed)); position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7)); diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 6c0380278..a1a9c3773 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -410,7 +410,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } } - private String decodeAlarm(short value) { + private String decodeAlarm(short value, String model) { + boolean modelLW = model != null && model.toUpperCase().startsWith("LW"); switch (value) { case 0x01: return Position.ALARM_SOS; @@ -427,7 +428,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_OVERSPEED; case 0x0E: case 0x0F: - case 0x19: return Position.ALARM_LOW_BATTERY; case 0x11: return Position.ALARM_POWER_OFF; @@ -438,17 +438,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case 0x14: return Position.ALARM_DOOR; case 0x18: - return Position.ALARM_REMOVING; - case 0x23: - return Position.ALARM_FALL_DOWN; + return modelLW ? Position.ALARM_ACCIDENT : Position.ALARM_REMOVING; + case 0x19: + return modelLW ? Position.ALARM_ACCELERATION : Position.ALARM_LOW_BATTERY; + case 0x1A: case 0x28: return Position.ALARM_BRAKING; - case 0x29: - return Position.ALARM_ACCELERATION; + case 0x1B: case 0x2A: case 0x2B: case 0x2E: return Position.ALARM_CORNERING; + case 0x23: + return Position.ALARM_FALL_DOWN; + case 0x29: + return Position.ALARM_ACCELERATION; case 0x2C: return Position.ALARM_ACCIDENT; case 0x30: @@ -831,7 +835,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { int satellites = BitUtil.between(signal, 10, 15) + BitUtil.between(signal, 5, 10); position.set(Position.KEY_SATELLITES, satellites); position.set(Position.KEY_RSSI, BitUtil.to(signal, 5)); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + position.set(Position.KEY_ALARM, decodeAlarm( + buf.readUnsignedByte(), getDeviceModel(deviceSession))); buf.readUnsignedByte(); // language position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); buf.readUnsignedByte(); // working mode @@ -842,7 +847,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, buf.readUnsignedByte()); short alarmExtension = buf.readUnsignedByte(); if (variant != Variant.VXT01) { - position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension)); + position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension, getDeviceModel(deviceSession))); } } } @@ -881,7 +886,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { decodeStatus(position, buf); position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + position.set(Position.KEY_ALARM, decodeAlarm( + buf.readUnsignedByte(), getDeviceModel(deviceSession))); position.set("oil", buf.readUnsignedShort()); int temperature = buf.readUnsignedByte(); if (BitUtil.check(temperature, 7)) { diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 648c5fb42..d010a8fe0 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -492,6 +492,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01); } break; + case 0x51: + if (length == 16) { + for (int i = 1; i <= 8; i++) { + position.set(Position.PREFIX_TEMP + i, buf.readShort()); + } + } + break; case 0x56: position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 10); buf.readUnsignedByte(); // reserved @@ -668,6 +675,10 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set("fuel2", Double.parseDouble( buf.readCharSequence(6, StandardCharsets.US_ASCII).toString())); break; + case 0x00B2: + position.set(Position.KEY_ICCID, ByteBufUtil.hexDump( + buf.readSlice(10)).replaceAll("f", "")); + break; case 0x00CE: position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); break; diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java index e88b34478..2b234ab21 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java @@ -171,7 +171,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { } long status = buf.readUnsignedInt(); - position.set(Position.KEY_IGNITION, BitUtil.check(status, 7 + 3 * 8)); + position.set(Position.KEY_IGNITION, !BitUtil.check(status, 7 + 3 * 8)); position.set(Position.KEY_STATUS, status); buf.readUnsignedShort(); diff --git a/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java b/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java new file mode 100644 index 000000000..be45346a6 --- /dev/null +++ b/src/main/java/org/traccar/protocol/SnapperFrameDecoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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 io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; + +public class SnapperFrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + byte header = buf.getByte(buf.readerIndex()); + if (header == 'P') { + if (buf.readableBytes() >= 2) { + return buf.readRetainedSlice(2); + } + } else if (buf.readableBytes() >= 16) { + int length = buf.getIntLE(buf.readerIndex() + 12) + 12 + 4 + 9; + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); + } + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/SnapperProtocol.java b/src/main/java/org/traccar/protocol/SnapperProtocol.java new file mode 100644 index 000000000..25a11ed3a --- /dev/null +++ b/src/main/java/org/traccar/protocol/SnapperProtocol.java @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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 jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class SnapperProtocol extends BaseProtocol { + + @Inject + public SnapperProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new SnapperFrameDecoder()); + pipeline.addLast(new SnapperProtocolDecoder(SnapperProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java b/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java new file mode 100644 index 000000000..ef1a4426a --- /dev/null +++ b/src/main/java/org/traccar/protocol/SnapperProtocolDecoder.java @@ -0,0 +1,229 @@ +/* + * Copyright 2024 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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.traccar.BaseProtocolDecoder; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +public class SnapperProtocolDecoder extends BaseProtocolDecoder { + + public SnapperProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_HELLO = 0x01; + public static final int MSG_SENDING_START = 0x02; + public static final int MSG_SENDING_FINISH = 0x03; + public static final int MSG_SEND_EVENTS = 0x21; + public static final int MSG_SEND_TECH_INFO = 0x23; + public static final int MSG_UPDATE_CMS_NUM = 0x24; + public static final int MSG_SEND_SYSTEM_INFO = 0x26; + public static final int MSG_SEND_USER_PHONE_NUMBERS = 0x31; + public static final int MSG_SEND_GPS_DATA = 0x32; + public static final int MSG_SEND_LBS_DATA = 0x33; + public static final int MSG_SEND_SYSTEM_STATUS = 0x34; + public static final int MSG_SEND_TRANSIT_SETTINGS = 0x35; + public static final int MSG_GET_SETTINGS = 0x36; + public static final int MSG_SEND_CONCATENATED_PACKET = 0x37; + public static final int MSG_SEND_DEBUG_INFO = 0x38; + + private void sendResponse( + Channel channel, SocketAddress remoteAddress, int index, int type, String answer) { + + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeByte('K'); + response.writeByte(3); // protocol version + response.writeLongLE(0); // reserved + response.writeShortLE(0); // encryption + response.writeIntLE(answer.length()); + response.writeIntLE(0); // reserved + response.writeShortLE(index); + response.writeByte(Checksum.sum(ByteBuffer.wrap(answer.getBytes(StandardCharsets.US_ASCII)))); + response.writeShortLE(type); + response.writeCharSequence(answer, StandardCharsets.US_ASCII); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + } + + private void decodeEvents(Position position, ByteBuf buf) { + + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + buf.readUnsignedByte(); // info 1 + buf.readUnsignedByte(); // info 2 + buf.readUnsignedIntLE(); // timestamp + buf.readUnsignedByte(); // timezone + } + + private void decodeTechInfo(Position position, ByteBuf buf) { + + for (int i = 0; i < 5; i++) { + buf.readUnsignedByte(); // index + int type = buf.readUnsignedByte(); + switch (type) { + case 0x00: + position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1); + position.set(Position.KEY_DEVICE_TEMP, buf.readByte()); + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + break; + case 0x01: + position.set("interiorTemp", buf.readByte()); + position.set("engineTemp", buf.readByte()); + buf.readUnsignedByte(); // reserved + break; + default: + buf.skipBytes(3); + break; + } + } + } + + private void decodeGpsData(Position position, ByteBuf buf) throws ParseException { + + String content = buf.readCharSequence(buf.readableBytes(), StandardCharsets.US_ASCII).toString(); + JsonObject json = Json.createReader(new StringReader(content)).readObject(); + + int flags = Integer.parseInt(json.getString("f"), 16); + if (!BitUtil.check(flags, 3)) { + return; + } + + position.setValid(BitUtil.check(flags, 1)); + if (!BitUtil.check(flags, 6)) { + position.setLatitude(-position.getLatitude()); + } + if (!BitUtil.check(flags, 7)) { + position.setLongitude(-position.getLongitude()); + } + + DateFormat dateFormat = new SimpleDateFormat("ddMMyyHHmmss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + position.setTime(dateFormat.parse(json.getString("d") + json.getString("t").split("\\.")[0])); + + String lat = json.getString("la"); + position.setLatitude(Integer.parseInt(lat.substring(0, 2)) + Double.parseDouble(lat.substring(2)) / 60); + String lon = json.getString("lo"); + position.setLongitude(Integer.parseInt(lon.substring(0, 3)) + Double.parseDouble(lon.substring(3)) / 60); + + position.setAltitude(Double.parseDouble(json.getString("a"))); + position.setSpeed(Double.parseDouble(json.getString("s"))); + position.setCourse(Double.parseDouble(json.getString("c"))); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(json.getString("sv"))); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + byte header = buf.readByte(); + if (header == 'P') { + if (channel != null) { + ByteBuf response = Unpooled.wrappedBuffer(new byte[] {0x50, 0x4f}); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + return null; + } + + buf.readUnsignedByte(); // protocol version + buf.readUnsignedIntLE(); // system bonus identifier + + String serialNumber = String.valueOf(buf.readUnsignedIntLE()); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serialNumber); + if (deviceSession == null) { + return null; + } + + buf.readUnsignedShortLE(); // encryption + int length = buf.readIntLE(); + buf.readUnsignedByte(); // flags + buf.readUnsignedMediumLE(); // reserved + int index = buf.readUnsignedShortLE(); + buf.readUnsignedByte(); // checksum + int type = buf.readUnsignedShortLE(); + + if (type == MSG_HELLO) { + sendResponse(channel, remoteAddress, index, type, "hello"); + } else { + sendResponse(channel, remoteAddress, index, type, "OK"); + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + switch (type) { + case MSG_SEND_EVENTS: + decodeEvents(position, buf); + getLastLocation(position, null); // TODO read timestamp + return position; + case MSG_SEND_TECH_INFO: + decodeTechInfo(position, buf); + getLastLocation(position, null); + return position; + case MSG_SEND_GPS_DATA: + decodeGpsData(position, buf.readSlice(length)); + return position; + case MSG_SEND_CONCATENATED_PACKET: + int count = buf.readUnsignedShortLE(); + for (int i = 0; i < count; i++) { + int partType = buf.readUnsignedShortLE(); + int partLength = buf.readUnsignedShortLE(); + switch (partType) { + case MSG_SEND_EVENTS: + decodeEvents(position, buf); + break; + case MSG_SEND_TECH_INFO: + decodeTechInfo(position, buf); + break; + case MSG_SEND_GPS_DATA: + decodeGpsData(position, buf.readSlice(partLength)); + break; + default: + buf.skipBytes(partLength); + break; + } + } + if (position.getFixTime() == null) { + getLastLocation(position, null); + } + return position; + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 53c4a5d02..c9d6f16ef 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -295,6 +295,60 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return position; } + private int decodeSerialData(Position position, String[] values, int index) { + + int remaining = Integer.parseInt(values[index++]); + double totalFuel = 0; + while (remaining > 0) { + String attribute = values[index++]; + if (attribute.startsWith("CabAVL")) { + String[] data = attribute.split(","); + double fuel1 = Double.parseDouble(data[2]); + if (fuel1 > 0) { + totalFuel += fuel1; + position.set("fuel1", fuel1); + } + double fuel2 = Double.parseDouble(data[3]); + if (fuel2 > 0) { + totalFuel += fuel2; + position.set("fuel2", fuel2); + } + } else if (attribute.startsWith("GTSL")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]); + } else if (attribute.contains("=")) { + String[] pair = attribute.split("="); + if (pair.length >= 2) { + String value = pair[1].trim(); + if (value.contains(".")) { + value = value.substring(0, value.indexOf('.')); + } + switch (pair[0].charAt(0)) { + case 't': + position.set(Position.PREFIX_TEMP + pair[0].charAt(2), Integer.parseInt(value, 16)); + break; + case 'N': + int fuel = Integer.parseInt(value, 16); + totalFuel += fuel; + position.set("fuel" + pair[0].charAt(2), fuel); + break; + case 'Q': + position.set("drivingQuality", Integer.parseInt(value, 16)); + break; + default: + break; + } + } + } else { + position.set("serial", attribute.trim()); + } + remaining -= attribute.length() + 1; + } + if (totalFuel > 0) { + position.set(Position.KEY_FUEL_LEVEL, totalFuel); + } + return index + 1; // checksum + } + private Position decode2356( Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException { int index = 0; @@ -371,56 +425,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++]))); break; case "UEX": - int remaining = Integer.parseInt(values[index++]); - double totalFuel = 0; - while (remaining > 0) { - String attribute = values[index++]; - if (attribute.startsWith("CabAVL")) { - String[] data = attribute.split(","); - double fuel1 = Double.parseDouble(data[2]); - if (fuel1 > 0) { - totalFuel += fuel1; - position.set("fuel1", fuel1); - } - double fuel2 = Double.parseDouble(data[3]); - if (fuel2 > 0) { - totalFuel += fuel2; - position.set("fuel2", fuel2); - } - } else if (attribute.startsWith("GTSL")) { - position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]); - } else if (attribute.contains("=")) { - String[] pair = attribute.split("="); - if (pair.length >= 2) { - String value = pair[1].trim(); - if (value.contains(".")) { - value = value.substring(0, value.indexOf('.')); - } - switch (pair[0].charAt(0)) { - case 't': - position.set(Position.PREFIX_TEMP + pair[0].charAt(2), Integer.parseInt(value, 16)); - break; - case 'N': - int fuel = Integer.parseInt(value, 16); - totalFuel += fuel; - position.set("fuel" + pair[0].charAt(2), fuel); - break; - case 'Q': - position.set("drivingQuality", Integer.parseInt(value, 16)); - break; - default: - break; - } - } - } else { - position.set("serial", attribute.trim()); - } - remaining -= attribute.length() + 1; - } - if (totalFuel > 0) { - position.set(Position.KEY_FUEL_LEVEL, totalFuel); - } - index += 1; // checksum + index = decodeSerialData(position, values, index); break; default: break; @@ -482,7 +487,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { String type = values[index++]; - if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES")) { + if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES") + && !type.equals("UEX")) { return null; } @@ -601,34 +607,40 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++])); } - if (type.equals("ALT")) { - if (BitUtil.check(mask, 19)) { - int alertId = Integer.parseInt(values[index++]); - position.set(Position.KEY_ALARM, decodeAlert(alertId)); - } - if (BitUtil.check(mask, 20)) { - position.set("alertModifier", values[index++]); - } - if (BitUtil.check(mask, 21)) { - position.set("alertData", values[index++]); - } - } else { - if (BitUtil.check(mask, 19)) { - position.set("mode", Integer.parseInt(values[index++])); - } - if (BitUtil.check(mask, 20)) { - position.set("reason", Integer.parseInt(values[index++])); - } - if (BitUtil.check(mask, 21)) { - position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); - } + switch (type) { + case "ALT": + if (BitUtil.check(mask, 19)) { + int alertId = Integer.parseInt(values[index++]); + position.set(Position.KEY_ALARM, decodeAlert(alertId)); + } + if (BitUtil.check(mask, 20)) { + position.set("alertModifier", values[index++]); + } + if (BitUtil.check(mask, 21)) { + position.set("alertData", values[index++]); + } + break; + case "UEX": + index = decodeSerialData(position, values, index); + break; + default: + if (BitUtil.check(mask, 19)) { + position.set("mode", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 20)) { + position.set("reason", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 21)) { + position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); + } + break; } if (BitUtil.check(mask, 22)) { index += 1; // reserved } - if (BitUtil.check(mask, 23)) { + if (BitUtil.check(mask, 23) && !type.equals("UEX")) { int assignMask = Integer.parseInt(values[index++], 16); for (int i = 0; i <= 30; i++) { if (BitUtil.check(assignMask, i)) { diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index 6197c6c13..de42031d7 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -199,7 +199,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { var fmbXXX = Set.of( "FMB001", "FMB010", "FMB002", "FMB020", "FMB003", "FMB110", "FMB120", "FMB122", "FMB125", "FMB130", "FMB140", "FMU125", "FMB900", "FMB920", "FMB962", "FMB964", "FM3001", "FMB202", "FMB204", "FMB206", - "FMT100", "MTB100", "FMP100", "MSP500"); + "FMT100", "MTB100", "FMP100", "MSP500", "FMC125", "FMM125", "FMU130", "FMC130", "FMM130", "FMB150", + "FMC150", "FMM150"); register(1, null, (p, b) -> p.set(Position.PREFIX_IN + 1, b.readUnsignedByte() > 0)); register(2, null, (p, b) -> p.set(Position.PREFIX_IN + 2, b.readUnsignedByte() > 0)); @@ -231,7 +232,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1)); register(78, null, (p, b) -> { long driverUniqueId = b.readLongLE(); - if (driverUniqueId > 0) { + if (driverUniqueId != 0) { p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId)); } }); @@ -243,9 +244,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { register(85, fmbXXX, (p, b) -> p.set(Position.KEY_RPM, b.readUnsignedShort())); register(87, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_ODOMETER, b.readUnsignedInt())); register(89, fmbXXX, (p, b) -> p.set("fuelLevelPercentage", b.readUnsignedByte())); - register(90, null, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedShort())); register(110, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.1)); register(113, fmbXXX, (p, b) -> p.set(Position.KEY_BATTERY_LEVEL, b.readUnsignedByte())); + register(115, fmbXXX, (p, b) -> p.set("engineTemp", b.readShort() * 0.1)); register(179, null, (p, b) -> p.set(Position.PREFIX_OUT + 1, b.readUnsignedByte() > 0)); register(180, null, (p, b) -> p.set(Position.PREFIX_OUT + 2, b.readUnsignedByte() > 0)); register(181, null, (p, b) -> p.set(Position.KEY_PDOP, b.readUnsignedShort() * 0.1)); @@ -292,6 +293,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } }); register(636, fmbXXX, (p, b) -> p.set("cid4g", b.readUnsignedInt())); + register(662, fmbXXX, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedByte() > 0)); } private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) { diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java index 02744f8ab..1187250f8 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -64,6 +64,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // mnc .number("(d+),") // lac .number("(d+)") // cell + .number(",(dd)").optional() // alarm .groupBegin() .text(",") .expression("(") @@ -77,7 +78,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder() + private static final Pattern PATTERN_CP01 = new PatternBuilder() .expression("[A-Z]{2,3}") .text("CP01,") .number("(ddd)") // gsm @@ -99,7 +100,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private static final Pattern PATTERN_LBS = new PatternBuilder() + private static final Pattern PATTERN_AP02 = new PatternBuilder() .expression("[A-Z]{2,3}") .text("AP02,") .expression("[^,]+,") // language @@ -118,6 +119,19 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .expression("(.*)") // wifi .compile(); + private static final Pattern PATTERN_AP03 = new PatternBuilder() + .expression("[A-Z]{2,3}") + .text("AP03,") + .number("(ddd)") // rssi + .number("(ddd)") // satellites + .number("(ddd)") // battery level + .number("d") // space + .number("xx") // fortification state + .number("dd,") // working mode + .number("(d+),") // steps + .number("d+") // rolls frequency + .compile(); + private Boolean decodeOptionalValue(Parser parser, int activeValue) { int value = parser.nextInt(); if (value != 0) { @@ -183,7 +197,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { if (type.equals("CP01")) { - Parser parser = new Parser(PATTERN_HEARTBEAT, sentence); + Parser parser = new Parser(PATTERN_CP01, sentence); if (!parser.matches()) { return null; } @@ -234,6 +248,20 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())); if (parser.hasNext()) { + switch (parser.nextInt()) { + case 1: + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + break; + case 5: + case 6: + position.set(Position.KEY_ALARM, Position.ALARM_FALL_DOWN); + break; + default: + break; + } + } + + if (parser.hasNext()) { decodeWifi(network, parser.next()); } @@ -243,7 +271,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { } else if (type.equals("AP02")) { - Parser parser = new Parser(PATTERN_LBS, sentence); + Parser parser = new Parser(PATTERN_AP02, sentence); if (!parser.matches()) { return null; } @@ -275,6 +303,61 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type.equals("AP03")) { + + Parser parser = new Parser(PATTERN_AP03, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + position.set(Position.KEY_STEPS, parser.nextInt()); + + return position; + + } else if (type.equals("AP49") || type.equals("APHT") || type.equals("APHP") || type.equals("AP50")) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + String[] values = sentence.split(","); + + switch (type) { + case "AP49": + position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1])); + break; + case "APHT": + position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1])); + position.set("pressureSystolic", Integer.parseInt(values[2])); + position.set("pressureDiastolic", Integer.parseInt(values[3])); + break; + case "APHP": + position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[1])); + position.set("pressureSystolic", Integer.parseInt(values[2])); + position.set("pressureDiastolic", Integer.parseInt(values[3])); + position.set("spo2", Integer.parseInt(values[4])); + position.set("bloodSugar", Double.parseDouble(values[5])); + position.set("temperature", Double.parseDouble(values[6])); + break; + case "AP50": + position.set("temperature", Double.parseDouble(values[1])); + position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[2])); + break; + default: + break; + } + + return position; + } return null; |