Android JT808协议通讯 您所在的位置:网站首页 808的含义 Android JT808协议通讯

Android JT808协议通讯

2024-04-10 00:43| 来源: 网络整理| 查看: 265

写在前面:

通讯协议一般格式:包头 — 消息头 — 消息体 — 校验位 — 包尾;

通讯数据格式 : bit 、 byte  ; 

bit是指的就是二进制 , 也就是位,1 byte = 8 bit ;

JT808协议:

数据类型:

通俗来讲:WORD就是长度是2的byte数组(byte[2]),DWORD就是长度是4的byte数组(byte[4]);

消息结构: 标识位: 采用 0x7e 表示,若校验码、消息头以及消息体中出现 0x7e,则要进行转义处理,转义 规则定义如下: 0x7e 0x7d 后紧跟一个 0x02; 0x7d 0x7d 后紧跟一个 0x01。 转义处理过程如下: 发送消息时:消息封装——>计算并填充校验码——>转义; 接收消息时:转义还原——>验证校验码——>解析消息。 示例: 发送一包内容为 0x30 0x7e 0x08 0x7d 0x55 的数据包, 则经过封装如下:0x7e 0x30 7d 0x02 0x08 0x7d 0x01 0x55 0x7e 消息头:

消息体:就是你要发送的数据(byte[]);校验码:

校验码指从消息头开始,同后一字节异或,直到校验码前一个字节,占用一个字节。

 

消息编码和解码: 消息编码: /** * 包装808数据,分包消息 * * @param msgId 消息id * @param phone 终端手机号 * @param msgBody 消息体 * @param subpackage 是否分包 0:不分包 1:分包 * @param totalPkg 总包数 * @param pkgNo 当前序号 */ public static byte[] generate808(int msgId, String phone, byte[] msgBody, int subpackage, long totalPkg, long pkgNo) { //=========================标识位==================================// byte[] flag = new byte[]{0x7E}; //=========================消息头==================================// //[0,1]消息Id byte[] msgIdb = BitOperator.numToByteArray(msgId, 2); //[2,3]消息体属性 byte[] msgBodyAttributes = msgBodyAttributes(msgBody.length, subpackage); //[4,9]终端手机号 BCD[6](占6位) byte[] terminalPhone = BCD8421Operater.string2Bcd(phone); //[10,11]流水号 byte[] flowNum = BitOperator.numToByteArray(SocketConfig.getSocketMsgCount(), 2); //[12]消息包封装项 不分包 就没有 //[12]消息包封装项 不分包 就没有 byte[] msgHeader; if (subpackage == 1) { //分包 byte[] totalPkgB = BitOperator.numToByteArray(totalPkg, 2); byte[] pkgNob = BitOperator.numToByteArray(pkgNo, 2); msgHeader = ByteUtil.byteMergerAll(msgIdb, msgBodyAttributes, terminalPhone, flowNum, totalPkgB, pkgNob); } else { msgHeader = ByteUtil.byteMergerAll(msgIdb, msgBodyAttributes, terminalPhone, flowNum); } //=========================数据合并(消息头,消息体)=====================// byte[] bytes = ByteUtil.byteMergerAll(msgHeader, msgBody); //=========================计算校验码==================================// String checkCodeHexStr = HexUtil.getBCC(bytes); byte[] checkCode = HexUtil.hexStringToByte(checkCodeHexStr); //=========================合并:消息头 消息体 校验码 得到总数据============// byte[] AllData = ByteUtil.byteMergerAll(bytes, checkCode); //=========================转义 7E和7D==================================// // 转成16进制字符串 String hexStr = HexUtil.byte2HexStr(AllData); // 替换 7E和7D String replaceHexStr = hexStr.replaceAll(FLAG_7D, " 7D 01") .replaceAll(FLAG_7E, " 7D 02") // 最后去除空格 .replaceAll(" ", ""); //替换好后 转回byte[] byte[] replaceByte = HexUtil.hexStringToByte(replaceHexStr); //=========================最终传输给服务器的数据==================================// return ByteUtil.byteMergerAll(flag, replaceByte, flag); } /** * 生成消息体属性 * * @param subpackage [13]是否分包 0:不分包 1:分包 */ private static byte[] msgBodyAttributes(int msgLength, int subpackage) { byte[] length = BitOperator.numToByteArray(msgLength, 2); //[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 = SocketConfig.JT808HEADER_ENCRYPT; //[13]分包 String subpackageB = String.valueOf(subpackage); //[14,15]保留位 String reserve = SocketConfig.JT808HEADER_RESERVE; String msgAttributes = reserve + subpackageB + encryption + msgBodyLength; // 消息体属性 int msgBodyAttr = Integer.parseInt(msgAttributes, 2); return BitOperator.numToByteArray(msgBodyAttr, 2); } /** * 包装808数据 * * @param msgId 消息id * @param phone 终端手机号 * @param msgBody 消息体 * @return */ public static byte[] generate808(int msgId, String phone, byte[] msgBody) { return generate808(msgId, phone, msgBody, 0, 0, 0); } 消息解码: /** * 解析服务器返回的808数据包 * * @return not null有用的数据包 null 数据校验失败 */ public static byte[] check808Data(byte[] bytes) { //去除包头 包尾的7E标识 在去掉校验码 byte[] del7eBytes = Arrays.copyOfRange(bytes, 1, bytes.length - 2); //获取数据上的校验码 String checkCode = HexUtil.byte2HexStr(Arrays.copyOfRange(bytes, bytes.length - 2, bytes.length - 1)); // 转成16进制字符串 String hexStr = HexUtil.byte2HexStr(del7eBytes); // 替换 7D 02->7E 7D 01->7D String replaceHexStr = hexStr.replaceAll(" 7D 02", " 7E") .replaceAll(" 7D 01", " 7D") // 最后去除空格 .replaceAll(" ", ""); byte[] data = HexUtil.hexStringToByte(replaceHexStr); //计算校验码 String sumCode = HexUtil.getBCC(data); if (!checkCode.equals(sumCode)) { return null; } return data; } 注册、鉴权、心跳、位置信息汇报

generate808方法就是上面的编码!!!

消息 ID:0x0002。 终端心跳数据消息体 为空。 心跳: JTT808Coding.generate808(0x0002, phone, new byte[]{}) 注册:

/** * 终端注册 * * @param manufacturerId 制造商 ID * @param terminalModel 终端型号 * @param terminalId 终端 ID * @return */ public static byte[] register(String manufacturerId, String terminalModel, String terminalId) { //省域 ID byte[] p = BitOperator.numToByteArray(31, 2); //省域 市县域 ID byte[] c = BitOperator.numToByteArray(72, 2); //制造商 ID byte[] mId = manufacturerId.getBytes(); //终端型号 byte[] tmId = terminalModel.getBytes(); //终端 ID byte[] tId = terminalId.getBytes(); //车牌颜色 byte[] s = {0}; // 车辆标识 byte[] vin = "LSFAM630000000008".getBytes(); return ByteUtil.byteMergerAll(p, c, mId, tmId, tId, s,vin); } byte[] register = JT808Directive.register("CCCBB","ANDROID0000000000000","AAAAAAA"); byte[] body = JTT808Coding.generate808(0x0100,SocketConfig.getmPhont(),register); socketManager.send((body)); 鉴权 , authCode是注册返回的鉴权码:

byte[] body = JTT808Coding.generate808(0x0102,SocketConfig.getmPhont(),authCode); socketManager.send((body)); 上传位置信息:

/** * 位置信息汇报 * * @param lat 纬度 * @param lng 经度 * @param altitude 高度 * @param speed 速度 * @param bearing 角度/方向 * @param accuracy 精度 * @param time 时间 * @return */ public static byte[] reportLocation(double lat, double lng, double altitude, float speed, float bearing, float accuracy, String time) { byte[] alarm = {0, 0, 0, 0}; // byte[] state = {0, 0, 0, 0}; byte[] state = stateLocationData(lat,lng); //DWORD经纬度 double pow106 = Math.pow(10, 6); double lat106 = lat * pow106; double lng106 = lng * pow106; byte[] latb = ByteUtil.longToDword(Math.round(lat106)); byte[] lngb = ByteUtil.longToDword(Math.round(lng106)); // WORD 高度 速度 方向 byte[] gaoChen = BitOperator.numToByteArray((int) altitude, 2); byte[] speedb = BitOperator.numToByteArray((int) (speed * 3.6), 2); byte[] orientation = BitOperator.numToByteArray((int) bearing, 2); //bcd时间 byte[] bcdTime = BCD8421Operater.string2Bcd(time); //位置信息附加项 byte[] additionLocation = additionLocationData(SocketConfig.getmOrderId(),(int) altitude,String.valueOf(accuracy)); return ByteUtil.byteMergerAll(alarm, state, latb, lngb, gaoChen, speedb, orientation, bcdTime,additionLocation); } public static byte[] reportLocation(Jt808MapLocation jtData){ return reportLocation(jtData.getLat() ,jtData.getLng() ,jtData.getAccuracy() ,jtData.getSpeed() ,jtData.getBearing() ,jtData.getAccuracy() ,jtData.getTime()); } /** * 位置信息状态项 * * @param * @return */ private static byte[] stateLocationData(double lat, double lng) { String state = "00"; state = state + (lat < 0 ? "1" : "0"); state = state + (lng < 0 ? "1" : "0"); state = state + "0000000000000000000000000000"; byte[] stateByte = ByteUtil.int2DWord(Integer.parseInt(state, 2)); return stateByte; } /** * 位置信息附加项 * @param order 订单号 * @param altitude 高度 * @param accuracy 精度 * @return */ private static byte[] additionLocationData(String order, int altitude, String accuracy) { byte[] orderType = new byte[2]; //订单号 orderType[0] = (byte) (0xE1); orderType[1] = ByteUtil.int2Byte(order.getBytes().length); byte[] orderMsg = ByteUtil.byteMergerAll(orderType, order.getBytes()); byte[] altitudeType = new byte[3]; //高度的正负 altitudeType[0] = (byte) (0xE2); altitudeType[1] = (byte) (0x01); altitudeType[2] = (byte) (altitude < 0 ? 0x31 : 0x30); byte[] accuracyType = new byte[2]; //订单号 accuracyType[0] = (byte) (0xE3); accuracyType[1] = ByteUtil.int2Byte(accuracy.getBytes().length); byte[] accuracyMsg = ByteUtil.byteMergerAll(accuracyType, accuracy.getBytes()); return ByteUtil.byteMergerAll(orderMsg, altitudeType, accuracyMsg); } 批量上传位置信息:

8.12位置信息汇报 就是第3节的上传位置信息;

 

byte[] batchBytes = JT808Directive.batchReportLocation(locations); byte[] body = JTT808Coding.generate808(0x0704, SocketConfig.getmPhont(), batchBytes); socketManager.send((body)); /** * 批量位置信息汇报 * @return */ public static byte[] batchReportLocation(List locations) { byte[] counts = ByteUtil.int2Word(locations.size()); //数据项个数 byte[] batchType = {0}; //位置数据类型 0:正常位置批量汇报,1:盲区补报 List formatLocations = new ArrayList(); for (int i = 0; i < locations.size(); i++) { //取出每一条的位置数据,然后拼接(位置汇报数据体长度+位置汇报数据体) byte[] cLocation = locations.get(i); byte[] clocationLength = ByteUtil.int2Word(cLocation.length); //位置汇报数据体长度 formatLocations.add(ByteUtil.byteMergerAll(clocationLength, cLocation)); } byte[] batchLocations = ByteUtil.byteMergerAll(formatLocations); //位置汇报数据体 return ByteUtil.byteMergerAll(counts, batchType, batchLocations); }

OK!

文中有大量的工具类、例如:校验码的算法、byte和int相互转换 、 bit和byte互相转换、byte和String互相转换;

Socket的连接和处理;

如有需要请移步 源码:https://github.com/CuiChenbo/JT808_OkSocket

如果没看太懂或有疑惑,请移步至:Android JTT 808-2011 道路运输车辆卫星定位系统终端通讯协议及数据格式

同时,感谢该博主的文章;



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有