Jt808应答举例

1.前言

最近客户在集成基于Jt808的产品协议的时候,经常会遇到一些问题,比如没有进行转义,或者转义的时机不对,导致校验码没有进行转义。为了让大家更熟悉Jt808的指令组包,我这里整理了一下转义的步骤。

2.组包

以此应答包0x8001为例:7E 8001 0005 413050530988 0001 0009 0200 00 7D01 7E

组包过程如下:

2.1.第一步

我们先封装消息体,包含:应答流水号+应答消息ID+应答结果,即:0009 0200 00

2.2.第二步

我们封装需要计算校验码的消息体部分:包含:应答消息ID:8001 + 消息体属性(长度):0005 + 设备S/N:413050530988 + 消息流水号:00001 + 第一步中的消息体(应答流水号+应答消息ID+应答结果):0009 0200 00;

即:8001 0005 413050530988 0001 0009 0200 00

2.3.第三步

计算校验码,将第二步所有消息进行累加和,得到的结果为7D,可参考下面的方法

    /**
     * XOR every byte
     *
     * @param buf (第二步得到的Buf)
     * @return 输出校验码,如果以上述为例得到的校验码为:7D
     */
    public static int xor(ByteBuf buf) {
        int checksum = 0;
        while (buf.readableBytes() > 0) {
            checksum ^= buf.readUnsignedByte();
        }
        return checksum;
    }

2.4.第四步

将第二步的消息体与第三步得到的校验码组成完整的消息包:8001 0005 413050530988 0001 0009 0200 00 7D

2.5.第五步

对完整的消息包进行转义,即:7E——>7D02;7D——>7D01,可采用下面的方法;最终我们得到转义后的消息为:8001 0005 413050530988 0001 0009 0200 00 7D01

    /**
     * In the message header, message body and check code, 0x7E is escaped as 0x7D 0x02, and 0x7D is escaped as 0x7D 0x01
     *
     * @param out 待输出转义后的消息体
     * @param bodyBuf 第四步中的消息内容
     */
    public static void escape(ByteBuf out, ByteBuf bodyBuf) {
        while (bodyBuf.readableBytes() > 0) {
            int b = bodyBuf.readUnsignedByte();
            if (b == 0x7E) {
                out.writeShort(0x7D02);
            } else if (b == 0x7D) {
                out.writeShort(0x7D01);
            } else {
                out.writeByte(b);
            }
        }
    }

2.6.第六步

增加包头包尾的7E,到此给设备应答的消息包组包完毕,最终下发给设备的消息体应该是:7E80010005413050530988000100090200007D017E

3.最后附上我下发指令的完整代码

3.1.封装平台通用应答

    /**
     * Platform general response
     *
     * @param ctx
     * @param jt808Msg
     * @param replyResultEnum
     * @return
     */
    public static ChannelFuture reply8001(ChannelHandlerContext ctx, Jt808Message jt808Msg, Jt808ReplyResultEnum replyResultEnum) {
        ByteBuf msgBody = Unpooled.buffer(5);
        msgBody.writeShort(jt808Msg.getMsgFlowId());
        msgBody.writeShort(jt808Msg.getMsgId());
        msgBody.writeByte(replyResultEnum.getValue());
        return sendToTerminal(ctx, Jt808MessageIdEnum.MSG_8001.getMessageId(), jt808Msg.getProtocolType(), jt808Msg.getProtocolVersion(), jt808Msg.getPhoneNumberArr(), msgBody);
    }

 3.2.封装指令下发到设备

    /**
     * Send message to terminal
     *
     * @param ctx
     * @param messageId
     * @param protocolType
     * @param protocolFlag
     * @param phoneNumberArr
     * @param msgBody
     * @return
     */
    private static ChannelFuture sendToTerminal(ChannelHandlerContext ctx, int messageId, ProtocolEnum protocolType, int protocolFlag, byte[] phoneNumberArr, ByteBuf msgBody) {
        Jt808Message replyMsg = new Jt808Message();
        replyMsg.setMsgId(messageId);
        replyMsg.setProtocolType(protocolType);
        replyMsg.setVersionFlag(protocolType == ProtocolEnum.JT808_2019 ? 1 : 0);
        replyMsg.setProtocolVersion(protocolFlag);
        replyMsg.setPhoneNumberArr(phoneNumberArr);
        replyMsg.setMsgFlowId(SessionUtil.getNextMsgFlowId(ctx));
        replyMsg.setMsgBody(msgBody);
        ChannelFuture future = ctx.writeAndFlush(replyMsg);
        future.addListener((ChannelFutureListener) channelFuture -> {
            if (!channelFuture.isSuccess()) {
                log.error("Sending data exception", channelFuture.cause());
            }
        });
        return future;
    }

 3.3.最后组成完整的包,并将指令下发给设备

/**
 * <p>Description: JT808 message entity encoder</p>
 *
 * @author Mr.Li
 * @date 2022-10-20
 */
@Slf4j
public class Jt808ProtocolEncoder extends MessageToByteEncoder<Jt808Message> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Jt808Message msg, ByteBuf out) throws Exception {
        ByteBuf msgBody = msg.getMsgBody();
        int msgBodyLen = msgBody == null ? 0 : msgBody.readableBytes();
        //Protocol Type
        ProtocolEnum protocolType = msg.getProtocolType();
        //the length of remove the tip and tail
        int contentLen = protocolType == ProtocolEnum.JT808_2019 ? Jt808Constant.JT2019_MSG_BASE_LENGTH + msgBodyLen - 2 : Jt808Constant.MSG_BASE_LENGTH + msgBodyLen - 2;
        //Subcontracting is required to add the total number of messages and packet number 4 bytes
        if (msg.isMultiPacket()) {
            contentLen = contentLen + 4;
        }

        ByteBuf bodyBuf = ByteBufAllocator.DEFAULT.heapBuffer(contentLen);
        try {
            //message id
            bodyBuf.writeShort(msg.getMsgId());
            //The length of the message body in the message body property
            int msgBodyAttr = msgBodyLen | (msg.getEncryptType() << 10);
            //The subcontract identity in the message body property
            if (msg.isMultiPacket()) {
                msgBodyAttr = msgBodyAttr | 0b00100000_00000000;
            }

            //The Jt808-2019 version adds the version identifier and protocol version number
            if (protocolType == ProtocolEnum.JT808_2019) {
                //Version identification
                msgBodyAttr = msgBodyAttr | 0b01000000_00000000;
                //message body property
                bodyBuf.writeShort(msgBodyAttr);
                //Protocol version Number
                bodyBuf.writeByte(msg.getProtocolVersion());
            } else {
                //message body properties
                bodyBuf.writeShort(msgBodyAttr);
            }

            //terminal number
            bodyBuf.writeBytes(msg.getPhoneNumberArr());
            //Message serial number
            bodyBuf.writeShort(msg.getMsgFlowId());
            //Package item of message (subcontracting needs to add the total number of message packets and packet number)
            if (msg.isMultiPacket()) {
                bodyBuf.writeShort(msg.getPacketTotalCount());
                bodyBuf.writeShort(msg.getPacketOrder());
            }
            //message body
            if (msgBodyLen > 0) {
                bodyBuf.writeBytes(msgBody);
            }
            //check code
            int checkCode = CommonUtil.xor(bodyBuf);
            bodyBuf.writeByte(checkCode);
            //message header
            out.writeByte(Jt808Constant.MSG_HEAD_TAIL_FLAG);
            //The read index is reset to the starting position
            bodyBuf.readerIndex(0);
            //escape
            Jt808PacketUtil.escape(out, bodyBuf);
            //message tail
            out.writeByte(Jt808Constant.MSG_HEAD_TAIL_FLAG);
            log.info("downlink command:{}", ByteBufUtil.hexDump(out));
        } catch (Exception e) {
            log.error("{}message encoding exception,message:{}", protocolType, msg, e);
        } finally {
            //release message body
            if (msgBody != null) {
                ReferenceCountUtil.release(msgBody);
            }
            //release bodyBuf
            ReferenceCountUtil.release(bodyBuf);
        }
    }
}

欢迎对物联网感兴趣的朋友加我微信交流学习。

猜你喜欢

转载自blog.csdn.net/qq_17486399/article/details/134138050