From ec6fcb5d7f476dd44b17a3d014c3fab4e2f73665 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 8 May 2026 20:45:29 -0700 Subject: [PATCH] Support versioned JT808 encoder --- .../protocol/Jt808ProtocolDecoder.java | 16 ++++-- .../protocol/Jt808ProtocolEncoder.java | 57 +++++++++++-------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Jt808ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt808ProtocolDecoder.java index f51d2247e..143f1bbfe 100644 --- a/src/main/java/org/traccar/protocol/Jt808ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt808ProtocolDecoder.java @@ -92,7 +92,7 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { return protocolVersion; } - public ByteBuf formatMessage(int delimiter, int type, ByteBuf id, boolean shortIndex, ByteBuf data) { + public ByteBuf formatMessage(int type, ByteBuf id, boolean shortIndex, ByteBuf data) { ByteBuf buf = Unpooled.buffer(); buf.writeByte(delimiter); buf.writeShort(type); @@ -125,7 +125,7 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(delimiter, MSG_GENERAL_RESPONSE, id, false, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE, id, false, response), remoteAddress)); } } @@ -136,7 +136,7 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(delimiter, MSG_GENERAL_RESPONSE_2, id, true, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE_2, id, true, response), remoteAddress)); } } @@ -228,7 +228,11 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { } } - static ByteBuf encodeId(String uniqueId) { + static ByteBuf encodeId(String uniqueId, int length) { + if (length == 10) { + return Unpooled.wrappedBuffer(DataConverter.parseHex( + String.format("%20s", uniqueId).replace(' ', '0'))); + } if (uniqueId.length() % 2 == 0) { return Unpooled.wrappedBuffer(DataConverter.parseHex(uniqueId)); } else { @@ -335,7 +339,7 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { response.writeByte(RESULT_SUCCESS); response.writeBytes(decodeId(id).getBytes(StandardCharsets.US_ASCII)); channel.writeAndFlush(new NetworkMessage( - formatMessage(delimiter, MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); + formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); } } else if (type == MSG_REPORT_TEXT_MESSAGE) { @@ -408,7 +412,7 @@ public class Jt808ProtocolDecoder extends BaseProtocolDecoder { response.writeByte(calendar.get(Calendar.MINUTE)); response.writeByte(calendar.get(Calendar.SECOND)); channel.writeAndFlush(new NetworkMessage( - formatMessage(delimiter, MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); + formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); } } else if (type == MSG_ACCELERATION) { diff --git a/src/main/java/org/traccar/protocol/Jt808ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Jt808ProtocolEncoder.java index 1f2f8ffc0..877fca3b7 100644 --- a/src/main/java/org/traccar/protocol/Jt808ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/Jt808ProtocolEncoder.java @@ -17,6 +17,8 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BasePipelineFactory; import org.traccar.BaseProtocol; import org.traccar.BaseProtocolEncoder; import org.traccar.Protocol; @@ -39,32 +41,37 @@ public class Jt808ProtocolEncoder extends BaseProtocolEncoder { } @Override - protected Object encodeCommand(Command command) { + protected Object encodeCommand(Channel channel, Command command) { + + Jt808ProtocolDecoder decoder = BasePipelineFactory.getHandler( + channel.pipeline(), Jt808ProtocolDecoder.class); boolean alternative = AttributeUtil.lookup( getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId()); - ByteBuf id = Jt808ProtocolDecoder.encodeId(getUniqueId(command.getDeviceId())); + Integer protocolVersion = decoder.getProtocolVersion(); + ByteBuf id = Jt808ProtocolDecoder.encodeId( + getUniqueId(command.getDeviceId()), protocolVersion != null ? 10 : 6); + String model = getDeviceModel(command.getDeviceId()); try { ByteBuf data = Unpooled.buffer(); switch (command.getType()) { case Command.TYPE_CUSTOM: - String model = getDeviceModel(command.getDeviceId()); if (model != null && Set.of("AL300", "GL100", "VL300").contains(model)) { data.writeByte(1); // number of parameters data.writeInt(0xF030); // AT command transparent transmission int length = command.getString(Command.KEY_DATA).length(); data.writeByte(length); data.writeCharSequence(command.getString(Command.KEY_DATA), StandardCharsets.US_ASCII); - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_CONFIGURATION_PARAMETERS, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_CONFIGURATION_PARAMETERS, id, false, data); } else if ("BSJ".equals(model)) { data.writeByte(1); // flag var charset = Charset.isSupported("GBK") ? Charset.forName("GBK") : StandardCharsets.US_ASCII; data.writeCharSequence(command.getString(Command.KEY_DATA), charset); - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_SEND_TEXT_MESSAGE, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_SEND_TEXT_MESSAGE, id, false, data); } else { return Unpooled.wrappedBuffer(DataConverter.parseHex(command.getString(Command.KEY_DATA))); } @@ -73,15 +80,15 @@ public class Jt808ProtocolEncoder extends BaseProtocolEncoder { data.writeByte(0x23); // parameter id data.writeByte(1); // parameter value length data.writeByte(0x03); // restart - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); case Command.TYPE_POSITION_PERIODIC: data.writeByte(1); // number of parameters data.writeByte(0x06); // parameter id data.writeByte(4); // parameter value length data.writeInt(command.getInteger(Command.KEY_FREQUENCY)); - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); case Command.TYPE_ALARM_ARM: case Command.TYPE_ALARM_DISARM: data.writeByte(1); // number of parameters @@ -90,50 +97,50 @@ public class Jt808ProtocolEncoder extends BaseProtocolEncoder { data.writeByte(1 + username.length()); // parameter value length data.writeByte(command.getType().equals(Command.TYPE_ALARM_ARM) ? 0x01 : 0x00); data.writeCharSequence(username, StandardCharsets.US_ASCII); - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_PARAMETER_SETTING, id, false, data); case Command.TYPE_ENGINE_STOP: case Command.TYPE_ENGINE_RESUME: if (alternative) { data.writeByte(command.getType().equals(Command.TYPE_ENGINE_STOP) ? 0x01 : 0x00); data.writeBytes(DataConverter.parseHex( new SimpleDateFormat("yyMMddHHmmss").format(new Date()))); - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_OIL_CONTROL, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_OIL_CONTROL, id, false, data); } else { - if ("VL300".equals(getDeviceModel(command.getDeviceId()))) { + if ("VL300".equals(model)) { data.writeCharSequence(command.getType().equals(Command.TYPE_ENGINE_STOP) ? "#0;1" : "#0;0", StandardCharsets.US_ASCII); - } else if ("W15L".equals(getDeviceModel(command.getDeviceId()))) { + } else if ("W15L".equals(model)) { data.writeByte(command.getType().equals(Command.TYPE_ENGINE_STOP) ? 0x64 : 0x65); } else { data.writeByte(command.getType().equals(Command.TYPE_ENGINE_STOP) ? 0xf0 : 0xf1); } - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); } case Command.TYPE_VIDEO_START: var config = getCacheManager().getConfig(); String host = URI.create(config.getString(Keys.WEB_URL)).getHost(); int port = config.getInteger( Keys.PROTOCOL_PORT.withPrefix(BaseProtocol.nameFromClass(Jt1078Protocol.class))); - int channel = command.getInteger(Command.KEY_INDEX, 1); + int videoChannel = command.getInteger(Command.KEY_INDEX, 1); data.writeByte(host.length()); data.writeCharSequence(host, StandardCharsets.US_ASCII); data.writeShort(port); // tcp port data.writeShort(0); // udp port - data.writeByte(channel); + data.writeByte(videoChannel); data.writeByte(1); // video only data.writeByte(0); // main stream - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_VIDEO_REQUEST, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_VIDEO_REQUEST, id, false, data); case Command.TYPE_VIDEO_STOP: data.writeByte(command.getInteger(Command.KEY_INDEX, 1)); data.writeByte(0); // close audio/video transmission data.writeByte(0); // close both audio and video data.writeByte(0); // main stream - return Jt808ProtocolDecoder.formatMessage( - 0x7e, Jt808ProtocolDecoder.MSG_VIDEO_CONTROL, id, false, data); + return decoder.formatMessage( + Jt808ProtocolDecoder.MSG_VIDEO_CONTROL, id, false, data); default: return null; }