Hi,
I'm trying to integrate a tracking device we use that uses a custom protocol, I've linked the protocol document below as well as my code for the custom protocol and the most recent log with an example of the messages / errors I'm getting.
Protocol Document (Only forwarding Tracking Packets via TCP base64: https://tachosys.com/files/tech-doc/data_packet_forwarding_from_digiCentral.pdf
ProtocolDecode File:
public class TachosysProtocolDecoder extends BaseProtocolDecoder {
private static final Logger LOGGER = LoggerFactory.getLogger(TachosysProtocolDecoder.class);
public TachosysProtocolDecoder(Protocol protocol) {
super(protocol);
LOGGER.info("TachosysProtocolDecoder Loaded");
}
@Override
protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String xml = ((ByteBuf) msg).toString(StandardCharsets.UTF_8);
LOGGER.info("Received XML: {}", xml);
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xml)));
Element root = document.getDocumentElement();
String deviceSerialNumber = root.getElementsByTagName("deviceSerialNumber").item(0).getTextContent();
String vehicleRegistrationNumber = root.getElementsByTagName("vehicleRegistrationNumber").item(0).getTextContent();
String data = root.getElementsByTagName("data").item(0).getTextContent();
LOGGER.info("Device serial number: {}", deviceSerialNumber);
LOGGER.info("Vehicle registration number: {}", vehicleRegistrationNumber);
byte[] decodedData = Base64.getDecoder().decode(data);
ByteBuf buf = Unpooled.wrappedBuffer(decodedData);
int packetType = buf.readUnsignedByte();
int packetSubtype = buf.readUnsignedByte();
LOGGER.info("Packet Type: {}", packetType);
LOGGER.info("Packet Subtype: {}", packetSubtype);
if (packetType == 0x47 && packetSubtype == 0x00) {
Position position = new Position(getProtocolName());
LOGGER.info("Position packet");
if (getDeviceSession(channel, remoteAddress, deviceSerialNumber) != null) {
position.setDeviceId(Long.parseLong(deviceSerialNumber));
LOGGER.debug("Device ID: {}", position.getDeviceId());
if (buf.readableBytes() >= 4) {
long timeReal = buf.readUnsignedInt();
LocalDateTime dateTime = LocalDateTime.ofEpochSecond(timeReal, 0, ZoneOffset.UTC);
position.setTime(Date.from(dateTime.toInstant(ZoneOffset.UTC)));
}
if (buf.readableBytes() >= 8) {
int rawLatitude = buf.readInt();
int rawLongitude = buf.readInt();
double latitude = rawLatitude / 600000.0;
double longitude = rawLongitude / 600000.0;
if (longitude < -180 || longitude > 180) {
throw new IllegalArgumentException("Longitude out of range: " + longitude);
}
position.setLatitude(latitude);
position.setLongitude(longitude);
LOGGER.info("Raw Latitude: {}, Latitude: {}", rawLatitude, latitude);
LOGGER.info("Raw Longitude: {}, Longitude: {}", rawLongitude, longitude);
}
if (buf.readableBytes() >= 2) {
double altitude = buf.readShort();
position.setAltitude(altitude);
LOGGER.info("Altitude: {}", altitude);
}
if (buf.readableBytes() >= 4) {
position.setSpeed(buf.readUnsignedShort() * 0.1);
position.setCourse(buf.readUnsignedShort() * 0.1);
if (buf.readableBytes() >= 2) {
buf.skipBytes(2);
}
}
if (buf.readableBytes() >= 2) {
position.set("maxSpeed", buf.readUnsignedShort() / 100.0);
}
if (buf.readableBytes() >= 4) {
position.set("minAcceleration", buf.readShort() / 100.0);
position.set("maxAcceleration", buf.readShort() / 100.0);
}
if (buf.readableBytes() >= 2) {
position.set("duration", buf.readUnsignedShort());
}
if (buf.readableBytes() >= 4) {
position.set("distance", buf.readUnsignedInt() / 100.0);
}
if (buf.readableBytes() >= 1) {
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
}
if (buf.readableBytes() >= 1) {
buf.skipBytes(1);
}
if (buf.readableBytes() >= 4) {
long gpsTimeReal = buf.readUnsignedInt();
LocalDateTime gpsDateTime = LocalDateTime.ofEpochSecond(gpsTimeReal, 0, ZoneOffset.UTC);
LOGGER.info("GPS TimeReal: {}", gpsDateTime);
position.setFixTime(Date.from(gpsDateTime.toInstant(ZoneOffset.UTC)));
}
position.setValid(true);
LOGGER.info("Position: {}", position);
return position;
} else {
LOGGER.info("Device session not found for serial number: {}", deviceSerialNumber);
return null;
}
} else {
LOGGER.info("Unknown packet type or subtype: {} / {}", packetType, packetSubtype);
return null;
}
} catch (Exception e) {
LOGGER.error("Error decoding message: " + e.getMessage(), e);
return null;
}
}
}
Error Log:
2025-01-29 16:37:47 INFO: Operating system name: Linux version: 6.8.12-5-pve architecture: amd64
2025-01-29 16:37:47 INFO: Java runtime name: OpenJDK 64-Bit Server VM vendor: Eclipse Adoptium version: 21.0.4+7-LTS
2025-01-29 16:37:47 INFO: Memory limit heap: 15466mb non-heap: 0mb
2025-01-29 16:37:47 INFO: Character encoding: UTF-8
2025-01-29 16:37:47 INFO: Version: 6.6
2025-01-29 16:37:47 INFO: Starting server...
2025-01-29 16:37:47 INFO: HikariPool-1 - Starting...
2025-01-29 16:37:47 INFO: HikariPool-1 - Added connection conn0: url=jdbc:h2:./data/database user=SA
2025-01-29 16:37:47 INFO: HikariPool-1 - Start completed.
2025-01-29 16:37:48 INFO: Set default schema name to PUBLIC
2025-01-29 16:37:48 INFO: Clearing database change log checksums
2025-01-29 16:37:48 INFO: Successfully acquired change log lock
2025-01-29 16:37:48 INFO: Successfully released change log lock
2025-01-29 16:37:50 INFO: Reading from PUBLIC.DATABASECHANGELOG
2025-01-29 16:37:51 INFO: Reading from PUBLIC.DATABASECHANGELOG
2025-01-29 16:37:51 INFO: Reading from PUBLIC.DATABASECHANGELOG
2025-01-29 16:37:51 INFO: UPDATE SUMMARY
2025-01-29 16:37:51 INFO: Run: 0
2025-01-29 16:37:51 INFO: Previously run: 50
2025-01-29 16:37:51 INFO: Filtered out: 0
2025-01-29 16:37:51 INFO: -------------------------------
2025-01-29 16:37:51 INFO: Total change sets: 50
2025-01-29 16:37:51 INFO: Update summary generated
2025-01-29 16:37:51 INFO: Successfully released change log lock
2025-01-29 16:37:51 INFO: Command execution complete
2025-01-29 16:37:51 INFO: Health check enabled with period 480000
2025-01-29 16:37:52 INFO: jetty-11.0.24; built: 2024-08-26T18:11:22.448Z; git: 5dfc59a691b748796f922208956bd1f2794bcd16; jvm 21.0.4+7-LTS
2025-01-29 16:37:52 INFO: Started o.t.w.@3538ac9c{/,null,AVAILABLE}
2025-01-29 16:37:52 INFO: Session workerName=node0
2025-01-29 16:37:53 INFO: Started o.e.j.s.ServletContextHandler@66d8b984{/,null,AVAILABLE}
2025-01-29 16:37:53 INFO: Started ServerConnector@6fb8b61e{HTTP/1.1, (http/1.1)}{0.0.0.0:8082}
2025-01-29 16:37:53 INFO: Started Server@4ed77e15{STARTING}[11.0.24,sto=0] @7486ms
2025-01-29 16:38:11 INFO: TachosysProtocolDecoder Loaded
2025-01-29 16:38:11 INFO: [T39928c86] connected
2025-01-29 16:41:11 INFO: [Te9036368: tachosys < ip (hidden)] <digiCentralDataPacket><deviceSerialNumber>40550001</deviceSerialNumber><vehicleRegistrationNumber>vehicle-registration (hidden)</vehicleRegistrationNumber><data>RwBnmkt2Aeo3Pv/5yb3/kgBzC1UATAKY/+YAJAA8AACDVAEAZ5pLdw==</data></digiCentralDataPacket>
2025-01-29 16:41:11 INFO: Received XML: <digiCentralDataPacket><deviceSerialNumber>40550001</deviceSerialNumber><vehicleRegistrationNumber>vehicle-registration (hidden)</vehicleRegistrationNumber><data>RwBnmkt2Aeo3Pv/5yb3/kgBzC1UATAKY/+YAJAA8AACDVAEAZ5pLdw==</data></digiCentralDataPacket>
2025-01-29 16:41:11 INFO: Device serial number: 40550001
2025-01-29 16:41:11 INFO: Vehicle registration number: vehicle-registration (hidden)
2025-01-29 16:41:11 INFO: Packet Type: 71
2025-01-29 16:41:11 INFO: Packet Subtype: 0
2025-01-29 16:41:11 INFO: Position packet
2025-01-29 16:41:11 INFO: Raw Latitude: 32126782, Latitude: 53.54463666666667
2025-01-29 16:41:11 INFO: Raw Longitude: -407107, Longitude: -0.6785116666666666
2025-01-29 16:41:11 INFO: Altitude: -110.0
2025-01-29 16:41:11 INFO: GPS TimeReal: 2025-01-29T15:38:31
2025-01-29 16:41:11 INFO: Position: org.traccar.model.Position@7f4988c4
2025-01-29 16:41:11 INFO: [Te9036368] disconnected
Hi,
I'm trying to integrate a tracking device we use that uses a custom protocol, I've linked the protocol document below as well as my code for the custom protocol and the most recent log with an example of the messages / errors I'm getting.
Protocol Document (Only forwarding Tracking Packets via TCP base64: https://tachosys.com/files/tech-doc/data_packet_forwarding_from_digiCentral.pdf
ProtocolDecode File:
public class TachosysProtocolDecoder extends BaseProtocolDecoder { private static final Logger LOGGER = LoggerFactory.getLogger(TachosysProtocolDecoder.class); public TachosysProtocolDecoder(Protocol protocol) { super(protocol); LOGGER.info("TachosysProtocolDecoder Loaded"); } @Override protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String xml = ((ByteBuf) msg).toString(StandardCharsets.UTF_8); LOGGER.info("Received XML: {}", xml); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new InputSource(new StringReader(xml))); Element root = document.getDocumentElement(); String deviceSerialNumber = root.getElementsByTagName("deviceSerialNumber").item(0).getTextContent(); String vehicleRegistrationNumber = root.getElementsByTagName("vehicleRegistrationNumber").item(0).getTextContent(); String data = root.getElementsByTagName("data").item(0).getTextContent(); LOGGER.info("Device serial number: {}", deviceSerialNumber); LOGGER.info("Vehicle registration number: {}", vehicleRegistrationNumber); byte[] decodedData = Base64.getDecoder().decode(data); ByteBuf buf = Unpooled.wrappedBuffer(decodedData); int packetType = buf.readUnsignedByte(); int packetSubtype = buf.readUnsignedByte(); LOGGER.info("Packet Type: {}", packetType); LOGGER.info("Packet Subtype: {}", packetSubtype); if (packetType == 0x47 && packetSubtype == 0x00) { Position position = new Position(getProtocolName()); LOGGER.info("Position packet"); if (getDeviceSession(channel, remoteAddress, deviceSerialNumber) != null) { position.setDeviceId(Long.parseLong(deviceSerialNumber)); LOGGER.debug("Device ID: {}", position.getDeviceId()); if (buf.readableBytes() >= 4) { long timeReal = buf.readUnsignedInt(); LocalDateTime dateTime = LocalDateTime.ofEpochSecond(timeReal, 0, ZoneOffset.UTC); position.setTime(Date.from(dateTime.toInstant(ZoneOffset.UTC))); } if (buf.readableBytes() >= 8) { int rawLatitude = buf.readInt(); int rawLongitude = buf.readInt(); double latitude = rawLatitude / 600000.0; double longitude = rawLongitude / 600000.0; if (longitude < -180 || longitude > 180) { throw new IllegalArgumentException("Longitude out of range: " + longitude); } position.setLatitude(latitude); position.setLongitude(longitude); LOGGER.info("Raw Latitude: {}, Latitude: {}", rawLatitude, latitude); LOGGER.info("Raw Longitude: {}, Longitude: {}", rawLongitude, longitude); } if (buf.readableBytes() >= 2) { double altitude = buf.readShort(); position.setAltitude(altitude); LOGGER.info("Altitude: {}", altitude); } if (buf.readableBytes() >= 4) { position.setSpeed(buf.readUnsignedShort() * 0.1); position.setCourse(buf.readUnsignedShort() * 0.1); if (buf.readableBytes() >= 2) { buf.skipBytes(2); // Reserved } } if (buf.readableBytes() >= 2) { position.set("maxSpeed", buf.readUnsignedShort() / 100.0); } if (buf.readableBytes() >= 4) { position.set("minAcceleration", buf.readShort() / 100.0); position.set("maxAcceleration", buf.readShort() / 100.0); } if (buf.readableBytes() >= 2) { position.set("duration", buf.readUnsignedShort()); } if (buf.readableBytes() >= 4) { position.set("distance", buf.readUnsignedInt() / 100.0); } if (buf.readableBytes() >= 1) { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); } if (buf.readableBytes() >= 1) { buf.skipBytes(1); // Reserved } if (buf.readableBytes() >= 4) { long gpsTimeReal = buf.readUnsignedInt(); LocalDateTime gpsDateTime = LocalDateTime.ofEpochSecond(gpsTimeReal, 0, ZoneOffset.UTC); LOGGER.info("GPS TimeReal: {}", gpsDateTime); position.setFixTime(Date.from(gpsDateTime.toInstant(ZoneOffset.UTC))); } position.setValid(true); LOGGER.info("Position: {}", position); return position; } else { LOGGER.info("Device session not found for serial number: {}", deviceSerialNumber); return null; } } else { LOGGER.info("Unknown packet type or subtype: {} / {}", packetType, packetSubtype); return null; } } catch (Exception e) { LOGGER.error("Error decoding message: " + e.getMessage(), e); return null; } } }
Error Log: