Custom Protocol Help

JohnHamsmitha month ago

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:

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
Anton Tananaeva month ago

I don't see any error messages.

JohnHamsmitha month ago

That's what is confusing me, the devices on the web side aren't showing any location or updating.

Anton Tananaeva month ago

That's because the value is invalid.

JohnHamsmitha month ago

Any value in particular?

Anton Tananaeva month ago

Never mind. I was looking at the raw coordinates.

JohnHamsmitha month ago

No problem, sorry to be a pain but do you see anything else that could cause the devices to not show a position on the web side?

Anton Tananaeva month ago

Don't see anything obvious.

JohnHamsmitha month ago

Finally got it working, seemed to be a problem with the device ID but all sorted.

The only other issue I'm having now is that when the data is being pushed into Traccar, the session opens, the data packet comes in then the session is instantly closed after which shows the device as offline but it's position updates.

Is there anything I can do to make the device appear online if it's recieved a data packet within the last x Seconds, even after the connection is closed.

Anton Tananaeva month ago

You can set status.ignoreOffline attribute.