97精品国产精品公司,久久www免费人成看片中文,天天谢天天干,日韩精品无码专区资源站

首頁 - 關(guān)于研博 - 技術(shù)筆記 - JT/T808 協(xié)議解析
JT/T808 協(xié)議解析
2025.01.10

 一.協(xié)議介紹

1.1概述

在實際應(yīng)用中,車載終端需要通過JT/T808協(xié)議接入到監(jiān)控平臺中。接入過程中,車載終端需要進(jìn)行注冊和鑒權(quán),以確保通信雙方的身份合法性。一旦接入成功,車載終端就可以向監(jiān)控平臺發(fā)送位置、狀態(tài)、報警等信息,并接收來自監(jiān)控平臺的控制指令。同時,監(jiān)控平臺也可以對車載終端進(jìn)行遠(yuǎn)程管理和控制。

1.2 接入過程詳解

(1)終端注冊

車載終端在安裝完成后,需要向監(jiān)控平臺進(jìn)行注冊。

注冊過程中,終端需要提供設(shè)備的相關(guān)信息,如設(shè)備ID、制造商ID、車輛VIN碼等。

監(jiān)控平臺在接收到注冊信息后,會驗證這些信息的有效性,并分配一個唯一的終端ID給車載終端。

終端收到平臺返回的終端ID后,注冊流程完成。

(2)鑒權(quán)

注冊成功后,車載終端需要進(jìn)行鑒權(quán),以確保只有經(jīng)過授權(quán)的終端才能與監(jiān)控平臺進(jìn)行通信。

終端會向監(jiān)控平臺發(fā)送鑒權(quán)請求,請求中包含終端ID和鑒權(quán)碼。

監(jiān)控平臺會校驗鑒權(quán)碼的正確性,以確認(rèn)車載終端的合法性。

如果鑒權(quán)碼校驗通過,則車載終端與監(jiān)控平臺之間的通信鏈路建立成功。

(3)數(shù)據(jù)上報

車載終端會根據(jù)預(yù)設(shè)的上報間隔,定期向監(jiān)控平臺發(fā)送數(shù)據(jù),包括位置信息、狀態(tài)信息、報警信息等。

這些數(shù)據(jù)會按照J(rèn)T/T808協(xié)議規(guī)定的消息格式進(jìn)行封裝,并通過之前建立的通信鏈路發(fā)送給監(jiān)控平臺。

(4)命令下發(fā)

監(jiān)控平臺可以向車載終端發(fā)送控制命令,如遠(yuǎn)程控制、信息查詢等。

這些命令同樣會按照J(rèn)T/T808協(xié)議規(guī)定的消息格式進(jìn)行封裝,并通過通信鏈路發(fā)送給車載終端。

車載終端在接收到命令后,會進(jìn)行相應(yīng)的處理,并將處理結(jié)果返回給監(jiān)控平臺。

(5)連接維護(hù)

在通信過程中,車載終端會周期性地向監(jiān)控平臺發(fā)送心跳消息,以保持連接的活躍狀態(tài)。

如果連接中斷,車載終端會嘗試重新建立連接,并重新進(jìn)行鑒權(quán)流程。

(6)數(shù)據(jù)解析與處理

監(jiān)控平臺在接收到車載終端發(fā)送的數(shù)據(jù)后,會進(jìn)行解析和處理。

根據(jù)數(shù)據(jù)的類型和內(nèi)容,監(jiān)控平臺可以進(jìn)行車輛定位、狀態(tài)監(jiān)測、報警處理等操作。

同時,監(jiān)控平臺還可以根據(jù)需要對車載終端發(fā)送控制命令,實現(xiàn)遠(yuǎn)程監(jiān)管和服務(wù)。

通過以上步驟,車載終端可以成功通過JT/T808協(xié)議接入到監(jiān)控平臺,實現(xiàn)遠(yuǎn)程監(jiān)管和服務(wù)的功能。

1.3應(yīng)用場景

JT/T808標(biāo)準(zhǔn)廣泛應(yīng)用于車輛遠(yuǎn)程監(jiān)管、物流管理、車輛安防等領(lǐng)域。例如,在車隊管理系統(tǒng)中,通過衛(wèi)星定位和通信技術(shù),監(jiān)控中心可以實時追蹤車輛位置、行駛狀態(tài)和駕駛行為;在物流行業(yè)中,物流公司可以實現(xiàn)對運輸車輛的實時監(jiān)控和路徑優(yōu)化,提高運輸效率;在車輛安全方面,通過實時監(jiān)控和追蹤,可以及時發(fā)現(xiàn)車輛異常情況,如變更路線、事故等,以便及時采取應(yīng)對措施。

 

二.報文介紹

2.1數(shù)據(jù)類型介紹

數(shù)據(jù)類型

描述及要求

BYTE

無符號單字節(jié)整形(字節(jié), 8 位)

WORD

無符號雙字節(jié)整形(字, 16 位)

DWORD

無符號四字節(jié)整形(雙字, 32 位)

BYTE[n]

n 字節(jié)

BCD[n]

8421 碼, n 字節(jié)

STRING

GBK 編碼,若無數(shù)據(jù),置空

2.2消息結(jié)構(gòu)

標(biāo)識位

消息頭

消息體

校驗碼

標(biāo)識位

1byte(0x7e)

16byte

 

1byte

1byte(0x7e)

2.3消息頭

消息ID(0-1) 消息體屬性(2-3)  終端手機號(4-9)  消息流水號(10-11)    消息包封裝項(12-15)

byte[0-1]   消息ID word(16)
byte[2-3]   消息體屬性 word(16)
        bit[0-9]    消息體長度
        bit[10-12]  數(shù)據(jù)加密方式
                        此三位都為 0,表示消息體不加密
                        第 10 位為 1,表示消息體經(jīng)過 RSA 算法加密
                        其它保留
        bit[13]     分包
                        1:消息體衛(wèi)長消息,進(jìn)行分包發(fā)送處理,具體分包信息由消息包封裝項決定
                        0:則消息頭中無消息包封裝項字段
        bit[14-15]  保留
byte[4-9]   終端手機號或設(shè)備ID bcd[6]
        根據(jù)安裝后終端自身的手機號轉(zhuǎn)換
        手機號不足12 位,則在前面補 0
byte[10-11]     消息流水號 word(16)
        按發(fā)送順序從 0 開始循環(huán)累加
byte[12-15]     消息包封裝項
        byte[0-1]   消息包總數(shù)(word(16))
                        該消息分包后得總包數(shù)
        byte[2-3]   包序號(word(16))
                        從 1 開始
        如果消息體屬性中相關(guān)標(biāo)識位確定消息分包處理,則該項有內(nèi)容
        否則無該項

 

三.協(xié)議解析開發(fā)

本次協(xié)議解析基于研博工業(yè)物聯(lián)網(wǎng)統(tǒng)一接入系統(tǒng)(stew-ot)協(xié)議擴展規(guī)范開發(fā)。

解析協(xié)議中用到的實體類

public class PackageData {

    /**
   * 16byte 消息頭
   */
    protected MsgHeader msgHeader;

    // 消息體
    protected  byte[] msgHeaderBytes;

    // 消息體字節(jié)數(shù)組
    @JSONField(serialize=false)
    protected byte[] msgBodyBytes;

    /**
   * 校驗碼 1byte
   */
    protected int checkSum;

    @JSONField(serialize=false)
    protected Channel channel;

    public MsgHeader getMsgHeader() {
        return msgHeader;
    }

    public void setMsgHeader(MsgHeader msgHeader) {
        this.msgHeader = msgHeader;
    }

    public byte[] getMsgBodyBytes() {
        return msgBodyBytes;
    }

    public byte[] getMsgHeaderBytes() {
        return msgHeaderBytes;
    }

    public void setMsgHeaderBytes(byte[] msgHeaderBytes) {
        this.msgHeaderBytes = msgHeaderBytes;
    }


    public void setMsgBodyBytes(byte[] msgBodyBytes) {
        this.msgBodyBytes = msgBodyBytes;
    }

    public int getCheckSum() {
        return checkSum;
    }

    public void setCheckSum(int checkSum) {
        this.checkSum = checkSum;
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    @Override
    public String toString() {
        return "PackageData [msgHeader=" + msgHeader + ", msgBodyBytes=" + Arrays.toString(msgBodyBytes) + ", checkSum="
        + checkSum + ", address=" + channel + "]";
    }

    public static class MsgHeader {
        // 消息ID
        protected int msgId;

        /////// ========消息體屬性
        // byte[2-3]
        protected int msgBodyPropsField;
        // 消息體長度
        protected int msgBodyLength;
        // 數(shù)據(jù)加密方式
        protected int encryptionType;
        // 是否分包,true==>有消息包封裝項
        protected boolean hasSubPackage;
        // 保留位[14-15]
        protected String reservedBit;
        /////// ========消息體屬性

        // 終端手機號
        protected String terminalPhone;
        // 流水號
        protected int flowId;

        //////// =====消息包封裝項
        // byte[12-15]
        protected int packageInfoField;
        // 消息包總數(shù)(word(16))
        protected long totalSubPackage;
        // 包序號(word(16))這次發(fā)送的這個消息包是分包中的第幾個消息包, 從 1 開始
        protected long subPackageSeq;
        //////// =====消息包封裝項

        public int getMsgId() {
            return msgId;
        }

        public void setMsgId(int msgId) {
            this.msgId = msgId;
        }

        public int getMsgBodyLength() {
            return msgBodyLength;
        }

        public void setMsgBodyLength(int msgBodyLength) {
            this.msgBodyLength = msgBodyLength;
        }

        public int getEncryptionType() {
            return encryptionType;
        }

        public void setEncryptionType(int encryptionType) {
            this.encryptionType = encryptionType;
        }

        public String getTerminalPhone() {
            return terminalPhone;
        }

        public void setTerminalPhone(String terminalPhone) {
            this.terminalPhone = terminalPhone;
        }

        public int getFlowId() {
            return flowId;
        }

        public void setFlowId(int flowId) {
            this.flowId = flowId;
        }

        public boolean isHasSubPackage() {
            return hasSubPackage;
        }

        public void setHasSubPackage(boolean hasSubPackage) {
            this.hasSubPackage = hasSubPackage;
        }

        public String getReservedBit() {
            return reservedBit;
        }

        public void setReservedBit(String reservedBit) {
            this.reservedBit = reservedBit;
        }

        public long getTotalSubPackage() {
            return totalSubPackage;
    }

    public void setTotalSubPackage(long totalPackage) {
      this.totalSubPackage = totalPackage;
    }

    public long getSubPackageSeq() {
      return subPackageSeq;
    }

    public void setSubPackageSeq(long packageSeq) {
      this.subPackageSeq = packageSeq;
    }

    public int getMsgBodyPropsField() {
      return msgBodyPropsField;
    }

    public void setMsgBodyPropsField(int msgBodyPropsField) {
      this.msgBodyPropsField = msgBodyPropsField;
    }

    public void setPackageInfoField(int packageInfoField) {
      this.packageInfoField = packageInfoField;
    }

    public int getPackageInfoField() {
      return packageInfoField;
    }

    @Override
    public String toString() {
      return "MsgHeader [msgId=" + msgId + ", msgBodyPropsField=" + msgBodyPropsField + ", msgBodyLength="
          + msgBodyLength + ", encryptionType=" + encryptionType + ", hasSubPackage=" + hasSubPackage
          + ", reservedBit=" + reservedBit + ", terminalPhone=" + terminalPhone + ", flowId=" + flowId
          + ", packageInfoField=" + packageInfoField + ", totalSubPackage=" + totalSubPackage
          + ", subPackageSeq=" + subPackageSeq + "]";
    }

  }

}

實現(xiàn)com.yanboot.iot.sdk.protocol.ProtocolCodec接口,重寫support方法,指定協(xié)議的唯一標(biāo)識、名稱、特性等內(nèi)容。

@Override
    public ProtocolSupport support() {

        return new ProtocolSupport(TransportProtocol.TCP)
                .id("JT/T808-tcp-codec")
                .name("JT/T808-tcp協(xié)議")
                .description("JT/T808-2019版")
                .feature(new 
ProtocolFeature().remoteUpgrade(false).keepOnline(true).keepOnlineTimeoutSeconds(360));
    }

實現(xiàn)com.yanboot.iot.sdk.protocol.ProtocolCodec接口的decode方法,完成協(xié)議的解碼工作。

@Override
    public void decode(OperatorSupplier supplier, DeviceSession deviceSession, ProtocolMessage<?> message, MessageExporter<DeviceMessage<?>> messageExporter) {
        TcpProtocolMessage tcpProtocolMessage = (TcpProtocolMessage) message;
        ByteBuf payload = tcpProtocolMessage.getPayload();
        if (payload.readableBytes() <= 0) {
            throw new RuntimeException("數(shù)據(jù)包異常");
        }
        byte[] bytes = new byte[payload.readableBytes()];
        payload.readBytes(bytes);
        PackageData pkg = DecodeHelper.bytes2PackageData(bytes);
        log.info("pkg{}", pkg);
        processPackageData(pkg, deviceSession);
    }

   //業(yè)務(wù)處理
    private static void processPackageData(PackageData packageData, DeviceSession deviceSession) {
        final PackageData.MsgHeader header = packageData.getMsgHeader();

        // 1. 終端心跳-消息體為空 ==> 平臺通用應(yīng)答
        if (TPMSConsts.msg_id_terminal_heart_beat == header.getMsgId()) {
            log.info(">>>>>[終端心跳],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            try {
                sendCommonReplay(deviceSession, packageData);
            } catch (Exception e) {
                log.error("<<<<<[終端心跳]處理錯誤,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
                        e.getMessage());
                e.printStackTrace();
            }
        }

        // 5. 終端鑒權(quán) ==> 平臺通用應(yīng)答
        else if (TPMSConsts.msg_id_terminal_authentication == header.getMsgId()) {
            log.info(">>>>>[終端鑒權(quán)],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            try {
//                TerminalAuthenticationMsg authenticationMsg = new TerminalAuthenticationMsg(packageData);
//                this.msgProcessService.processAuthMsg(authenticationMsg);
                //TODO 待解析鑒權(quán)消息
                sendCommonReplay(deviceSession, packageData);
                log.info("<<<<<[終端鑒權(quán)],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            } catch (Exception e) {
                log.error("<<<<<[終端鑒權(quán)]處理錯誤,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
                        e.getMessage());
                e.printStackTrace();
            }
        }
        // 6. 終端注冊 ==> 終端注冊應(yīng)答
        else if (TPMSConsts.msg_id_terminal_register == header.getMsgId()) {
            log.info(">>>>>[終端注冊],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            try {
                TcpProtocolMessage tcpProtocolMessage = new TcpProtocolMessage();
                TerminalRegisterMsg msg = DecodeHelper.toTerminalRegisterMsg(packageData);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                outputStream.write(packageData.getMsgHeader().getFlowId());
                //TODO
                outputStream.write(0);
                outputStream.write(0);
                byte[] byteArray = outputStream.toByteArray();
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byteArrayOutputStream.write(126);
                byteArrayOutputStream.write(packageData.getMsgHeaderBytes());
                byteArrayOutputStream.write(byteArray);
                byte[] byteArray1 = byteArrayOutputStream.toByteArray();
                byte checkSum = 0;
                for (int i = 1; i < byteArray1.length; i++) {
                    checkSum ^= byteArray1[i];
                }
                ByteArrayOutputStream result = new ByteArrayOutputStream(byteArray1.length + 2);
                result.write(byteArray1);
                result.write(checkSum);
                result.write(126);
                tcpProtocolMessage.payload(result.toByteArray());
                deviceSession.send(tcpProtocolMessage);
                log.info("<<<<<[終端注冊],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
                log.info("注冊信息:{}", msg.getTerminalRegInfo());
            } catch (Exception e) {
                log.error("<<<<<[終端注冊]處理錯誤,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
                        e.getMessage());
                e.printStackTrace();
            }
        }
        // 7. 終端注銷(終端注銷數(shù)據(jù)消息體為空) ==> 平臺通用應(yīng)答
        else if (TPMSConsts.msg_id_terminal_log_out == header.getMsgId()) {
            log.info(">>>>>[終端注銷],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            try {
//                this.msgProcessService.processTerminalLogoutMsg(packageData);
                sendCommonReplay(deviceSession, packageData);
                log.info("<<<<<[終端注銷],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
            } catch (Exception e) {
                log.error("<<<<<[終端注銷]處理錯誤,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
                        e.getMessage());
                e.printStackTrace();
            }
        }
        // 其他情況
        else {
            log.error(">>>>>>[未知消息類型],phone={},msgId={},package={}", header.getTerminalPhone(), header.getMsgId(),
                    packageData);
        }
    }
 public static void sendCommonReplay(DeviceSession deviceSession, PackageData packageData) {
        try {
            PackageData.MsgHeader header = packageData.getMsgHeader();
            TcpProtocolMessage tcpProtocolMessage1 = new TcpProtocolMessage();
            int msgId = header.getMsgId();
            int flowId = header.getFlowId();
            byte success = ServerCommonRespMsgBody.success;
//                byte[] bytes = JT808ProtocolUtils.generateMsgHeader(header.getTerminalPhone(), msgId, header.getMsgBodyPropsField(), header.getFlowId());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            // 1. 消息ID word(16)
            BitOperator bitOperator = new BitOperator();
            baos.write(bitOperator.integerTo2Bytes(msgId));
            baos.write(bitOperator.integerTo2Bytes(flowId));
            baos.write(success);
            byte[] byteBody = baos.toByteArray();
            byte checkSum = 0;
            for (int i = 0; i < byteBody.length; i++) {
                checkSum ^= byteBody[i];
            }
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byteArrayOutputStream.write(126);
            byteArrayOutputStream.write(packageData.getMsgHeaderBytes());
            byteArrayOutputStream.write(byteBody);
            byteArrayOutputStream.write(checkSum);
            byteArrayOutputStream.write(126);
            byte[] result = byteArrayOutputStream.toByteArray();

            tcpProtocolMessage1.payload(result);
            deviceSession.send(tcpProtocolMessage1);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

解析協(xié)議中用到的工具類

package com.yanboot.iot;

import com.yanboot.iot.common.TPMSConsts;
import com.yanboot.iot.util.BCD8421Operater;
import com.yanboot.iot.util.BitOperator;
import com.yanboot.iot.vo.PackageData;
import com.yanboot.iot.vo.req.TerminalRegisterMsg;
import lombok.extern.slf4j.Slf4j;
import com.yanboot.iot.vo.req.TerminalRegisterMsg.TerminalRegInfo;
import com.yanboot.iot.vo.PackageData.MsgHeader;
import java.nio.charset.StandardCharsets;
@Slf4j
public class DecodeHelper {
    private static final BitOperator bitOperator = new BitOperator();
    private static final BCD8421Operater bcd8421Operater = new BCD8421Operater();
    public DecodeHelper() {
//        this.bitOperator = new BitOperator();
//        this.bcd8421Operater = new BCD8421Operater();
    }
    public static PackageData bytes2PackageData(byte[] data) {
        PackageData ret = new PackageData();
        // 0. 終端套接字地址信息
        // ret.setChannel(msg.getChannel());
        // 1. 16byte 或 12byte 消息頭
        MsgHeader msgHeader = parseMsgHeaderFromBytes(data);
        ret.setMsgHeader(msgHeader);
        int msgBodyByteStartIndex = 12;
        // 2. 消息體
        // 有子包信息,消息體起始字節(jié)后移四個字節(jié):消息包總數(shù)(word(16))+包序號(word(16))
        if (msgHeader.isHasSubPackage()) {
            msgBodyByteStartIndex = 16;
        }
        byte[] tmp = new byte[msgHeader.getMsgBodyLength()];
        byte[] headerBytes = new byte[msgBodyByteStartIndex];
        System.arraycopy(data, 0, headerBytes, 0, headerBytes.length);
        System.arraycopy(data, msgBodyByteStartIndex, tmp, 0, tmp.length);
        ret.setMsgHeaderBytes(headerBytes);
        ret.setMsgBodyBytes(tmp);
        // 3. 去掉分隔符之后,最后一位就是校驗碼
        // int checkSumInPkg =
        // this.bitOperator.oneByteToInteger(data[data.length - 1]);
        int checkSumInPkg = data[data.length - 1];
        int calculatedCheckSum = bitOperator.getCheckSum4JT808(data, 0, data.length - 1);
        ret.setCheckSum(checkSumInPkg);
        if (checkSumInPkg != calculatedCheckSum) {
            log.warn("檢驗碼不一致,msgid:{},pkg:{},calculated:{}", msgHeader.getMsgId(), checkSumInPkg, calculatedCheckSum);
        }
        return ret;
    }
    //消息頭解析
    private static MsgHeader parseMsgHeaderFromBytes(byte[] data) {
        MsgHeader msgHeader = new MsgHeader();
        // 1. 消息ID word(16)
        // byte[] tmp = new byte[2];
        // System.arraycopy(data, 0, tmp, 0, 2);
        // msgHeader.setMsgId(this.bitOperator.twoBytesToInteger(tmp));
        msgHeader.setMsgId(parseIntFromBytes(data, 0, 2));
        // 2. 消息體屬性 word(16)=================>
        // System.arraycopy(data, 2, tmp, 0, 2);
        // int msgBodyProps = this.bitOperator.twoBytesToInteger(tmp);
        int msgBodyProps = parseIntFromBytes(data, 2, 2);
        msgHeader.setMsgBodyPropsField(msgBodyProps);
        // [ 0-9 ] 0000,0011,1111,1111(3FF)(消息體長度)
        msgHeader.setMsgBodyLength(msgBodyProps & 0x1ff);
        // [10-12] 0001,1100,0000,0000(1C00)(加密類型)
        msgHeader.setEncryptionType((msgBodyProps & 0xe00) >> 10);
        // [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
        msgHeader.setHasSubPackage(((msgBodyProps & 0x2000) >> 13) == 1);
        // [14-15] 1100,0000,0000,0000(C000)(保留位)
        msgHeader.setReservedBit(((msgBodyProps & 0xc000) >> 14) + "");
        // 消息體屬性 word(16)<=================
        // 3. 終端手機號 bcd[6]
        // tmp = new byte[6];
        // System.arraycopy(data, 4, tmp, 0, 6);
        // msgHeader.setTerminalPhone(this.bcd8421Operater.bcd2String(tmp));
        msgHeader.setTerminalPhone(parseBcdStringFromBytes(data, 4, 6));
        // 4. 消息流水號 word(16) 按發(fā)送順序從 0 開始循環(huán)累加
        // tmp = new byte[2];
        // System.arraycopy(data, 10, tmp, 0, 2);
        // msgHeader.setFlowId(this.bitOperator.twoBytesToInteger(tmp));
        msgHeader.setFlowId(parseIntFromBytes(data, 10, 2));
        // 5. 消息包封裝項
        // 有子包信息
        if (msgHeader.isHasSubPackage()) {
            // 消息包封裝項字段
            msgHeader.setPackageInfoField(parseIntFromBytes(data, 12, 4));
            // byte[0-1] 消息包總數(shù)(word(16))
            // tmp = new byte[2];
            // System.arraycopy(data, 12, tmp, 0, 2);
            // msgHeader.setTotalSubPackage(this.bitOperator.twoBytesToInteger(tmp));
            msgHeader.setTotalSubPackage(parseIntFromBytes(data, 12, 2));
            // byte[2-3] 包序號(word(16)) 從 1 開始
            // tmp = new byte[2];
            // System.arraycopy(data, 14, tmp, 0, 2);
            // msgHeader.setSubPackageSeq(this.bitOperator.twoBytesToInteger(tmp));
            msgHeader.setSubPackageSeq(parseIntFromBytes(data, 12, 2));
        }
        return msgHeader;
    }
//    protected static String parseStringFromBytes(byte[] data, int startIndex, int lenth) {
//        return parseStringFromBytes(data, startIndex, lenth, null);
//    }
    private static String parseStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
        try {
            byte[] tmp = new byte[lenth];
            System.arraycopy(data, startIndex, tmp, 0, lenth);
            return new String(tmp, TPMSConsts.string_charset);
        } catch (Exception e) {
            log.error("解析字符串出錯:{}", e.getMessage());
            e.printStackTrace();
            return defaultVal;
        }
    }
    private static String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth) {
        return parseBcdStringFromBytes(data, startIndex, lenth, null);
    }
    private static String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
        try {
            byte[] tmp = new byte[lenth];
            log.debug("拷貝的數(shù)組長度:{},實際拷貝長度:{},開始的未知:{}", data.length, lenth, startIndex);
            System.arraycopy(data, startIndex, tmp, 0, lenth);
            return bcd8421Operater.bcd2String(tmp);
        } catch (Exception e) {
            log.error("解析BCD(8421碼)出錯:{}", e.getMessage());
            e.printStackTrace();
            return defaultVal;
        }
    }
//    private static int parseIntFromBytes(byte[] data, int startIndex, int length) {
//        return parseIntFromBytes(data, startIndex, length, 0);
//    }
    private static int parseIntFromBytes(byte[] data, int startIndex, int length, int defaultVal) {
        try {
            // 字節(jié)數(shù)大于4,從起始索引開始向后處理4個字節(jié),其余超出部分丟棄
            final int len = length > 4 ? 4 : length;
            byte[] tmp = new byte[len];
            System.arraycopy(data, startIndex, tmp, 0, len);
            return bitOperator.byteToInteger(tmp);
        } catch (Exception e) {
            log.error("解析整數(shù)出錯:{}", e.getMessage());
            e.printStackTrace();
            return defaultVal;
        }
    }
    private static int parseIntFromBytes(byte[] data, int offset, int length) {
        int value = 0;
        for (int i = 0; i < length; i++) {
            value = (value << 8) | (data[offset + i] & 0xFF);
        }
        return value;
    }
    private static String parseStringFromBytes(byte[] data, int offset, int length) {
        return new String(data, offset, length, StandardCharsets.ISO_8859_1).trim();
    }
    //終端注冊
    public static TerminalRegisterMsg toTerminalRegisterMsg(PackageData packageData) {
        TerminalRegisterMsg ret = new TerminalRegisterMsg(packageData);
        byte[] data = ret.getMsgBodyBytes();
        TerminalRegInfo body = new TerminalRegInfo();
        // 1. byte[0-1] 省域ID(WORD)
        // 設(shè)備安裝車輛所在的省域,省域ID采用GB/T2260中規(guī)定的行政區(qū)劃代碼6位中前兩位
        // 0保留,由平臺取默認(rèn)值
        body.setProvinceId(parseIntFromBytes(data, 0, 2));
        // 2. byte[2-3] 設(shè)備安裝車輛所在的市域或縣域,市縣域ID采用GB/T2260中規(guī)定的行 政區(qū)劃代碼6位中后四位
        // 0保留,由平臺取默認(rèn)值
        body.setCityId(parseIntFromBytes(data, 2, 2));
        // 3. byte[4-8] 制造商ID(BYTE[5]) 5 個字節(jié),終端制造商編碼
        // byte[] tmp = new byte[5];
        body.setManufacturerId(parseStringFromBytes(data, 4, 5));
        // 4. byte[9-16] 終端型號(BYTE[8]) 八個字節(jié), 此終端型號 由制造商自行定義 位數(shù)不足八位的,補空格。
        body.setTerminalType(parseStringFromBytes(data, 9, 20));
        // 5. byte[17-23] 終端ID(BYTE[7]) 七個字節(jié), 由大寫字母 和數(shù)字組成, 此終端 ID由制造 商自行定義
        body.setTerminalId(parseStringFromBytes(data, 29, 7));
        // 6. byte[24] 車牌顏色(BYTE) 車牌顏 色按照J(rèn)T/T415-2006 中5.4.12 的規(guī)定
        body.setLicensePlateColor(parseIntFromBytes(data, 36, 1));
        // 7. byte[25-x] 車牌(STRING) 公安交 通管理部門頒 發(fā)的機動車號牌
        body.setLicensePlate(parseStringFromBytes(data, 37, data.length - 37));
        ret.setTerminalRegInfo(body);
        return ret;
    }
    //終端鑒權(quán)
    public static TerminalRegisterMsg toTerminalAuth(PackageData packageData) {
        return null;
    }
}
package com.yanboot.iot.util;
import java.io.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * JT808協(xié)議轉(zhuǎn)義工具類
 *
 * <pre>
 * 0x7d01 <====> 0x7d
 * 0x7d02 <====> 0x7e
 * </pre>
 *
 * @author hylexus
 */
public class JT808ProtocolUtils {
    private final Logger log = LoggerFactory.getLogger(getClass());
    private static BitOperator bitOperator = new BitOperator();
    private static BCD8421Operater bcd8421Operater = new BCD8421Operater();
    public JT808ProtocolUtils() {
//        this.bitOperator = new BitOperator();
//        this.bcd8421Operater = new BCD8421Operater();
    }
    /**
     * 接收消息時轉(zhuǎn)義<br>
     *
     * <pre>
     * 0x7d01 <====> 0x7d
     * 0x7d02 <====> 0x7e
     * </pre>
     *
     * @param bs    要轉(zhuǎn)義的字節(jié)數(shù)組
     * @param start 起始索引
     * @param end   結(jié)束索引
     * @return 轉(zhuǎn)義后的字節(jié)數(shù)組
     * @throws Exception
     */
    public byte[] doEscape4Receive(byte[] bs, int start, int end) throws Exception {
        if (start < 0 || end > bs.length)
            throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            for (int i = 0; i < start; i++) {
                baos.write(bs[i]);
            }
            for (int i = start; i < end - 1; i++) {
                if (bs[i] == 0x7d && bs[i + 1] == 0x01) {
                    baos.write(0x7d);
                    i++;
                } else if (bs[i] == 0x7d && bs[i + 1] == 0x02) {
                    baos.write(0x7e);
                    i++;
                } else {
                    baos.write(bs[i]);
                }
            }
            for (int i = end - 1; i < bs.length; i++) {
                baos.write(bs[i]);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            throw e;
        } finally {
            if (baos != null) {
                baos.close();
                baos = null;
            }
        }
    }
    /**
     * 發(fā)送消息時轉(zhuǎn)義<br>
     *
     * <pre>
     *  0x7e <====> 0x7d02
     * </pre>
     *
     * @param bs    要轉(zhuǎn)義的字節(jié)數(shù)組
     * @param start 起始索引
     * @param end   結(jié)束索引
     * @return 轉(zhuǎn)義后的字節(jié)數(shù)組
     * @throws Exception
     */
    public byte[] doEscape4Send(byte[] bs, int start, int end) throws Exception {
        if (start < 0 || end > bs.length)
            throw new ArrayIndexOutOfBoundsException("doEscape4Send error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            for (int i = 0; i < start; i++) {
                baos.write(bs[i]);
            }
            for (int i = start; i < end; i++) {
                if (bs[i] == 0x7e) {
                    baos.write(0x7d);
                    baos.write(0x02);
                } else {
                    baos.write(bs[i]);
                }
            }
            for (int i = end; i < bs.length; i++) {
                baos.write(bs[i]);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            throw e;
        } finally {
            if (baos != null) {
                baos.close();
                baos = null;
            }
        }
    }
    public int generateMsgBodyProps(int msgLen, int enctyptionType, boolean isSubPackage, int reversed_14_15) {
        // [ 0-9 ] 0000,0011,1111,1111(3FF)(消息體長度)
        // [10-12] 0001,1100,0000,0000(1C00)(加密類型)
        // [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
        // [14-15] 1100,0000,0000,0000(C000)(保留位)
        if (msgLen >= 1024)
            log.warn("The max value of msgLen is 1023, but {} .", msgLen);
        int subPkg = isSubPackage ? 1 : 0;
        int ret = (msgLen & 0x3FF) | ((enctyptionType << 10) & 0x1C00) | ((subPkg << 13) & 0x2000)
                | ((reversed_14_15 << 14) & 0xC000);
        return ret & 0xffff;
    }
    public static byte[] generateMsgHeader(String phone, int msgType, int msgBodyProps, int flowId)
            throws Exception {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            // 1. 消息ID word(16)
            baos.write(bitOperator.integerTo2Bytes(msgType));
            // 2. 消息體屬性 word(16)
            baos.write(bitOperator.integerTo2Bytes(msgBodyProps));
            // 3. 終端手機號 bcd[6]
            baos.write(bcd8421Operater.string2Bcd(phone));
            // 4. 消息流水號 word(16),按發(fā)送順序從 0 開始循環(huán)累加
            baos.write(bitOperator.integerTo2Bytes(flowId));
            // 消息包封裝項 此處不予考慮
            return baos.toByteArray();
        } finally {
            if (baos != null) {
                baos.close();
            }
        }
    }
}
package com.yanboot.iot.util;
public class HexStringUtils {
  private static final char[] DIGITS_HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  protected static char[] encodeHex(byte[] data) {
    int l = data.length;
    char[] out = new char[l << 1];
    for (int i = 0, j = 0; i < l; i++) {
      out[j++] = DIGITS_HEX[(0xF0 & data[i]) >>> 4];
      out[j++] = DIGITS_HEX[0x0F & data[i]];
    }
    return out;
  }
  protected static byte[] decodeHex(char[] data) {
    int len = data.length;
    if ((len & 0x01) != 0) {
      throw new RuntimeException("字符個數(shù)應(yīng)該為偶數(shù)");
    }
    byte[] out = new byte[len >> 1];
    for (int i = 0, j = 0; j < len; i++) {
      int f = toDigit(data[j], j) << 4;
      j++;
      f |= toDigit(data[j], j);
      j++;
      out[i] = (byte) (f & 0xFF);
    }
    return out;
  }
  protected static int toDigit(char ch, int index) {
    int digit = Character.digit(ch, 16);
    if (digit == -1) {
      throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
    }
    return digit;
  }
  public static String toHexString(byte[] bs) {
    return new String(encodeHex(bs));
  }
  public static String hexString2Bytes(String hex) {
    return new String(decodeHex(hex.toCharArray()));
  }
  public static byte[] chars2Bytes(char[] bs) {
    return decodeHex(bs);
  }
  public static void main(String[] args) {
    String s = "abc你好";
    String hex = toHexString(s.getBytes());
    String decode = hexString2Bytes(hex);
    System.out.println("原字符串:" + s);
    System.out.println("十六進(jìn)制字符串:" + hex);
    System.out.println("還原:" + decode);
  }
}

 

獲取相關(guān)資料
下載地址將會發(fā)送至您填寫的郵箱
相關(guān)新聞
水文SL651協(xié)議解析
2025-01-17
環(huán)保HJ212-2017協(xié)議介紹開發(fā)
2025-01-03
基于CentOS7系統(tǒng)初步搭建hadoop
2024-12-16
  • 在線客服
  • 電話咨詢
  • 微信
  • 短視頻
  • 亚洲精品久久久久久下一站 | 亚洲无码精品在线视频| 狼友二区| 国产3p实拍| 一本一级特黄大片中文字幕| 亚洲欧美激情另类校园| 一级做a爱片久久毛片A高清 | 乱伦一级黄色片| 亚洲最大日韩中文字幕另类 | 亚洲无码AV电影一区| 国产成人免费电影| 狠狠中文| 青青草原在线视频精品99| 欧美一级电影免费| 四虎网址在线看| 性高朝久久久久久久久久| 久久久久久久久久久一级毛片| 久久五月天国产自| 大香蕉久久久| 野花社区 av| 中国精品视频一区二区三区| 夜夜狂射影院欧美极品| 精美欧美一区二区三区久久久 | 国产欧美日本在线| 日韩精品视频一区二区三区久久久久久| 熟妇人妻精品猛烈进人| 亚欧无码观看黄色网站| 人人射人人澡| 中文字幕无线码一区二区| 国产成人亚洲综合色| 欧美色欲一区| 日在线V| 亚洲精品国男人在线视频| 亚洲AV永久天堂在线观看| 日韩毛片在线免费看| 久久久亚洲免费资源| 欧美精品秘 入口| 中文字幕无码日韩精品| 欧美成人午夜精品久久攵 | 国产喷水后入在线观看| 国产精品无码毛片久久久|