联系方式wx blueLuo88
package com.android.jt808demo.utils;import android.util.Log;import java.io.ByteArrayOutputStream;import java.io.IOException;public class JT808ProtocolUtils { private String TAG = "JT808ProtocolUtils"; private JTBitOperator bitOperator; private JT8421Operater bcd8421Operater; public JT808ProtocolUtils() { this.bitOperator = new JTBitOperator(); this.bcd8421Operater = new JT8421Operater(); } /** * 接收消息时转义<br> * <pre> * 0x7d01 <====> 0x7d * 0x7d02 <====> 0x7e * </pre> * @param bs 要转义的字节数组 * @param start 起始索引 * @param end 结束索引 * @return 转义后的字节数组 * @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; } } } /** * 发送消息时转义<br> * <pre> * 0x7e <====> 0x7d02 * </pre> * @param bs 要转义的字节数组 * @param start 起始索引 * @param end 结束索引 * @return 转义后的字节数组 * @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 byte[] generateMsgHeader(String phone, int msgType, byte[] body, byte[] msgBodyProps, int flowId) { ByteArrayOutputStream baos = null; try { baos = new ByteArrayOutputStream(); // 1. 消息ID word(16) // Log.i(TAG,"=消息ID length=" bitOperator.integerTo2Bytes(msgType).length); baos.write(bitOperator.integerTo2Bytes(msgType)); // 2. 消息体属性 word(16) // Log.i(TAG,"=msgBodyProps length=" msgBodyProps.length); baos.write(msgBodyProps); // 3. 终端手机号 bcd[6] // Log.i(TAG,"=终端手机号 length=" bcd8421Operater.string2Bcd(phone).length); baos.write(bcd8421Operater.string2Bcd(phone)); // 4. 消息流水号 word(16),按发送顺序从 0 开始循环累加 //Log.i(TAG,"=消息流水号 length=" bitOperator.integerTo2Bytes(flowId).length); baos.write(bitOperator.integerTo2Bytes(flowId)); // 消息包封装项 此处不予考虑,放在头部消息封装成协议消息时候封装加入 return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } /** * 包装808数据,带有分包的数据 * @param msgId 消息id * @param phone 终端手机号 * @param msgBody 消息体 * @return */ public byte[] create808Data(int msgId, String phone, byte[] msgBody, int subpkg , int flowId) { // Log.i(TAG,"=msgBody.length=" msgBody.length); //=========================标识位==================================// byte[] flag = new byte[]{0x7E}; //=========================消息头==================================// //[0,1]消息Id byte[] msgIdb = bitOperator.integerTo2Bytes(msgId); //[2,3]消息体属性 byte[] msgBodyAttributes = msgBodyAttributes(msgBody.length, subpkg); //int msgBodyProps = generateMsgBodyProps(msgBody.length, 0b000, false, 0); //[4,9]终端手机号 BCD[6](占6位) //byte[] terminalPhone = bcd8421Operater.string2Bcd(phone); byte[] terminalPhone =bitOperator.str2Bcd(phone); //[10,11]流水号 byte[] flowNum = bitOperator.integerTo2Bytes(flowId); //[12]消息包封装项 不分包 就没有// 分包:当消息体属性中第 13 位为1 时表示消息体为长消息,进行分包发送处理,具体// 分包信息由消息包封装项决定;若第13位为0,则消息头中无消息包封装项字段。// 消息包封装项内容见表:// 起始字节 字段 数据类型 描述及要求// 0 消息包总数 WORD 该消息分包后的总包数// 2 包序号 WORD 从1 开始 byte[] msgHeader =null; if(subpkg==1){ msgHeader=bitOperator.concatAll(terminalPhone,msgIdb,msgBody,msgBodyAttributes,flowNum); }else{ msgHeader=generateMsgHeader(phone, msgId, msgBody, msgBodyAttributes, flowId); } //Log.i(TAG,"=msgHeader.length=" msgHeader.length); //=========================数据合并(消息头,消息体)=====================// byte[] bytes = bitOperator.concatAll(msgHeader, msgBody); //Log.i(TAG,"total data length=" bytes.length); /** 添加校验码 */ bytes = bitOperator.byteMerge(bytes, bitOperator.getCheckCode(bytes)); //Log.i(TAG,"添加校验码后 data length=" bytes.length); /** 转义 */ bytes = bitOperator.escape(bytes); // Log.i(TAG,"转义后 data length=" bytes.length); /** 添加标识位 */ bytes = bitOperator.byteMerge(new byte[] {0x7e}, bytes); bytes = bitOperator.byteMerge(bytes, new byte[] {0x7e}); //Log.i(TAG,"添加标识位后 data length=" bytes.length); return bytes; } /** * 包装808数据,带有分包的数据 * @param msgId 消息id * @param phone 终端手机号 * @param msgBody 消息体 * @return */ public byte[] create808Data(int msgId, String phone, byte[] msgBody, int subpkg , int totalPkgCount, int pkgIndex, int flowId) { //=========================标识位==================================// byte[] flag = new byte[]{0x7E}; //=========================消息头==================================// //[0,1]消息Id byte[] msgIdb = bitOperator.integerTo2Bytes(msgId); //[2,3]消息体属性 byte[] msgBodyAttributes = msgBodyAttributes(msgBody.length, subpkg); //int msgBodyProps = generateMsgBodyProps(msgBody.length, 0b000, false, 0); //[4,9]终端手机号 BCD[6](占6位) byte[] terminalPhone = bcd8421Operater.string2Bcd(phone); //[10,11]流水号 byte[] flowNum = bitOperator.integerTo2Bytes(flowId); //[12]消息包封装项 不分包 就没有// 分包:当消息体属性中第 13 位为1 时表示消息体为长消息,进行分包发送处理,具体// 分包信息由消息包封装项决定;若第13位为0,则消息头中无消息包封装项字段。// 消息包封装项内容见表:// 起始字节 字段 数据类型 描述及要求// 0 消息包总数 WORD 该消息分包后的总包数// 2 包序号 WORD 从1 开始 byte[] msgHeader =null; if(subpkg==1){ byte[] totalPkgCountData=bitOperator.integerTo2Bytes(totalPkgCount); byte[] pkgIndexData=bitOperator.integerTo2Bytes(pkgIndex); msgHeader=bitOperator.concatAll(terminalPhone,msgIdb,msgBody,msgBodyAttributes,flowNum,totalPkgCountData,pkgIndexData); }else{ msgHeader=generateMsgHeader(phone, msgId, msgBody, msgBodyAttributes, flowId); } //=========================数据合并(消息头,消息体)=====================// byte[] bytes = bitOperator.concatAll(msgHeader, msgBody);// //=========================计算校验码==================================//// String checkCodeHexStr = bitOperator.getBCC(bytes);// byte[] checkCode = bitOperator.hexStringToByte(checkCodeHexStr);// //=========================合并:消息头 消息体 校验码 得到总数据============//// byte[] totalData = bitOperator.concatAll(bytes, checkCode);// //=========================转义 7E和7D==================================//// // 转成16进制字符串// String hexStr = bitOperator.bytesToHexString(totalData);// // 替换 7E和7D// String replaceHexStr = hexStr.replaceAll("7D", " 7D 01")// .replaceAll("7E", " 7D 02")// // 最后去除空格// .replaceAll(" ", "");// //替换好后 转回byte[]// byte[] replaceByte = bitOperator.hexStringToByte(replaceHexStr);// //=========================最终传输给服务器的数据==================================//// return bitOperator.concatAll(flag, replaceByte, flag); /** 添加校验码 */ bytes = bitOperator.byteMerge(bytes, bitOperator.getCheckCode(bytes)); /** 转义 */ bytes = bitOperator.escape(bytes); /** 添加标识位 */ bytes = bitOperator.byteMerge(new byte[] {0x7e}, bytes); bytes = bitOperator.byteMerge(bytes, new byte[] {0x7e}); return bytes; } 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.i(TAG, "The max value of msgLen is 1023, but {}=msgLen=" msgLen); } int subPkg = isSubPackage ? 1 : 0; int ret = (msgLen & 0x3FF) | ((enctyptionType << 10) & 0x1C00) | ((subPkg << 13) & 0x2000) | ((reversed_14_15 << 14) & 0xC000); return ret & 0xffff; } /** * 生成消息体属性 * @param subpackage [13]是否分包 0:不分包 1:分包 */ public byte[] msgBodyAttributes(int msgLength, int subpackage) { byte[] length = bitOperator.integerTo2Bytes(msgLength); //[0,9]消息体长度 String msgBodyLength = "" //第一个字节最后2bit (byte) ((length[0] >> 1) & 0x1) (byte) ((length[0] >> 0) & 0x1) //第二个字节8bit (byte) ((length[1] >> 7) & 0x1) (byte) ((length[1] >> 6) & 0x1) (byte) ((length[1] >> 5) & 0x1) (byte) ((length[1] >> 4) & 0x1) (byte) ((length[1] >> 3) & 0x1) (byte) ((length[1] >> 2) & 0x1) (byte) ((length[1] >> 1) & 0x1) (byte) ((length[1] >> 0) & 0x1); //[10,12]数据加密方式 0 表示不加密 String encryption = "000"; //[13]分包 String subpackageB = String.valueOf(subpackage); //[14,15]保留位 String reserve = "00"; String msgAttributes = reserve subpackageB encryption msgBodyLength; // 消息体属性 int msgBodyAttr = Integer.parseInt(msgAttributes, 2); return bitOperator.integerTo2Bytes(msgBodyAttr); }}
评论