1.开发包介绍
此开发主要是针对AOVX的产品,对设备上行数据进行解码以及平台下行控制命令进行组包的工具包。采用了Java SpringBoot2.x框架。
开发主要是实现了对设备上报数据的解包,将设备上报的二进制流数据转成我们所熟知的JSON字符串。
2.源码介绍
项目名称为:jt808-decode,方法入口类为DataParse.
此类中包含了两个方法:
(1)receiveData(String rowData):接收设备上报的原始数据(16进制),并将数据转成JSON字符串
(2)splitData(String rowData):对设备上报的原始数据进行拆包详解,返回一个拆包分段的介绍
项目中包含了3个包文件:
(1)constant:定义了解析设备原始数据过程中用到的一些常量或者枚举定义
(2)model:定义了一些解包过程中的实体类
(3)utils:方法工具包,里面是解包过程中使用的一些方法类
2.1.receiveData(String rowData)
根据不同的消息ID调用不同的解析方法解析:
/**
* Receive the data reported by the device (hex) and parse it into a JSON string
* @param rowData Data reported by the device (hex)
* @return json
*/
public static String receiveData(String rowData) throws Exception {
if(StringUtils.isBlank(rowData)){
return null;
}
//Convert Hex string to ByteBuf
ByteBuf byteBuf = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(rowData));
Object obj= Jt808PacketUtil.decodeJt808Packet(byteBuf);
String resultJson="";
if(obj==null){
ReferenceCountUtil.release(byteBuf);
return null;
}else{
Jt808Message jt808Msg= Jt808ProtocolDecoder.decode((ByteBuf)obj);
switch (jt808Msg.getMsgId()){
case 0x0001:
CommonReplyParam commonReplyParam= Message0001Parser.parse(jt808Msg,jt808Msg.getMsgBody());
resultJson=JSON.toJSONString(commonReplyParam);
break;
case 0x0002:
Heartbeat heartbeat= Message0002Parser.parse(jt808Msg);
heartbeat.setReplyMsg(Jt808PacketUtil.reply8001(jt808Msg));
resultJson=JSON.toJSONString(heartbeat);
break;
case 0x0100:
TerminalRegisterInfo registerInfo=Message0100Parser.parse(jt808Msg,jt808Msg.getMsgBody());
registerInfo.setReplyMsg(Jt808PacketUtil.reply8100(jt808Msg,registerInfo.getAuthCode()));
resultJson=JSON.toJSONString(registerInfo);
break;
case 0x0102:
TerminalAuthInfo authInfo=Message0102Parser.parse(jt808Msg,jt808Msg.getMsgBody());
authInfo.setReplyMsg(Jt808PacketUtil.reply8001(jt808Msg));
resultJson=JSON.toJSONString(authInfo);
break;
case 0x0104:
DeviceParam deviceParam=Message0104Parser.parse(jt808Msg,jt808Msg.getMsgBody());
resultJson=JSON.toJSONString(deviceParam);
break;
case 0x0200:
Location location= Message0200Parser.parse(jt808Msg,jt808Msg.getMsgBody());
location.setReplyMsg(Jt808PacketUtil.reply8001(jt808Msg));
resultJson=JSON.toJSONString(location);
break;
case 0x0900:
PassThroughData throughData=Message0900Parser.parser(jt808Msg,jt808Msg.getMsgBody());
resultJson=JSON.toJSONString(throughData);
break;
default:
break;
}
}
return resultJson;
}
2.2.splitData(String rowData)
按照协议内容格式对数据进行拆分说明
/**
* Introduction to the data unpacking process for hex strings reported by devices
* @param rowData Data reported by the device (hex)
* @return
*/
public static List<String> splitData(String rowData){
if(StringUtils.isBlank(rowData)){
return null;
}
List<String> dataArr=new ArrayList<>();
//Convert Hex string to ByteBuf
ByteBuf byteBuf = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(rowData));
Object obj= Jt808PacketUtil.decodeJt808Packet(byteBuf);
if(obj==null){
ReferenceCountUtil.release(byteBuf);
return null;
}else{
ByteBuf msgBuf= (ByteBuf) obj;
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedByte(),2),"Start Flag"));
int msgId=msgBuf.readUnsignedShort();
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgId,4),"Message ID"));
//message body properties
short msgBodyAttr = msgBuf.readShort();
//Version ID (version ID 0 refers to the version in 2011 and 1 refers to the version in 2019)
int versionFlag = (msgBodyAttr & 0b01000000_00000000)>0?1:0;
//is multi packet?
boolean multiPacket = (msgBodyAttr & 0b00100000_00000000) > 0;
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBodyAttr,4),"Properties of Message Body"));
//Terminal phone number array,JT808-2019 is 10 bytes
byte[] phoneNumberArr;
if (versionFlag == 1) {
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedByte(),2),"Protocol Version"));
phoneNumberArr = new byte[10];
} else {
phoneNumberArr = new byte[6];
}
msgBuf.readBytes(phoneNumberArr);
dataArr.add(String.format("%s-->%s",ByteBufUtil.hexDump(phoneNumberArr),"Device Number"));
//Message serial number
int msgFlowId = msgBuf.readUnsignedShort();
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgFlowId,4),"Message serial number"));
//multi packet?
if (multiPacket) {
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedShort(),4),"Packet Total Count"));
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedShort(),4),"Packet Order"));
}
//message body length
int msgBodyLen = msgBodyAttr & 0b00000011_11111111;
if(msgBodyLen>msgBuf.readableBytes()-2){
byte[] msgBodyArr=new byte[msgBuf.readableBytes()-2];
msgBuf.readBytes(msgBodyArr);
dataArr.add(String.format("%s-->%s",ByteBufUtil.hexDump(msgBodyArr),"Insufficient message body length!"));
}else{
ByteBuf msgBodyBuf =msgBuf.readSlice(msgBuf.readableBytes()-2);
switch (msgId){
case 0x0001:
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBodyBuf.readShort(),4),"Response serial number"));
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBodyBuf.readShort(),4),"Response message ID"));
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBodyBuf.readShort(),2),"Results (0: success; 1: failure; 2: message error; 3: not supported)"));
break;
case 0x0002:
if(msgBodyBuf.readableBytes()>0){
byte[] msgBodyArr=new byte[msgBodyBuf.readableBytes()];
msgBodyBuf.readBytes(msgBodyArr);
dataArr.add(String.format("%s-->%s",ByteBufUtil.hexDump(msgBodyArr),"Message Body"));
}
break;
case 0x0100:
if(versionFlag == 1){
SplitUtil.splitTerminalRegisterInfo(msgBodyBuf,dataArr);
}else{
SplitUtil.splitTerminalRegisterInfo2019(msgBodyBuf,dataArr);
}
break;
case 0x0102:
SplitUtil.splitAuthInfo(msgBodyBuf,dataArr,versionFlag);
break;
case 0x0104:
SplitUtil.splitTerminalParameterResponse(msgBodyBuf,dataArr);
break;
case 0x0200:
SplitUtil.splitLocationInfo(msgBodyBuf,dataArr);
break;
case 0x0900:
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBodyBuf.readUnsignedByte(),2),"Transparent message type"));
if(msgBodyBuf.readableBytes()>0){
byte [] msgContentArr=new byte[msgBodyBuf.readableBytes()];
msgBodyBuf.readBytes(msgContentArr);
dataArr.add(String.format("%s-->%s",ByteBufUtil.hexDump(msgContentArr),"Transparent message content"));
}
break;
default:
byte[] msgBodyArr=new byte[msgBodyBuf.readableBytes()];
msgBodyBuf.readBytes(msgBodyArr);
dataArr.add(String.format("%s-->%s",ByteBufUtil.hexDump(msgBodyArr),"Message Body"));
break;
}
}
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedByte(),2),"Check Code"));
dataArr.add(String.format("%s-->%s",NumberUtil.hexStr(msgBuf.readUnsignedByte(),2),"End Flag"));
}
return dataArr;
}
3.测试
3.1.解码方法效果
调用示例:
@Test
public void receiveData() throws Exception {
String rowData="7E020000CA593054482897004800000000B0080010000000000000000000000000000023030110180430019E310100F00E0136019A04C14B10000083039F03F22C414F56585F414D3330302D474C5F48322E305F424739354D334C415230324130345F56322E302E343A763135F423542160033C41C354216000E04DC33CB74B797D02B8C0F4C114700274BD3EB74B797D02BEBBF60A00090000000000000000F70400000DE3F81D04086459305448289789320420000012218671414D3330302D474C0000F912000F000000000000000000000000000000008C7E";
String parseDataJson=dataParser.receiveData(rowData);
System.out.println("ParseData:"+parseDataJson);
}
输出内容:
{
"acc": 0,
"alarmTypeList": [],
"altitude": 0,
"battery": 0,
"direction": 0,
"expandMap": {
"wifi": "[{\"rssi\":-61,\"mac\":\"54:21:60:03:3c:41\"},{\"rssi\":-61,\"mac\":\"54:21:60:00:e0:4d\"},{\"rssi\":-64,\"mac\":\"3c:b7:4b:79:7e:b8\"},{\"rssi\":-67,\"mac\":\"f4:c1:14:70:02:74\"},{\"rssi\":-69,\"mac\":\"3e:b7:4b:79:7e:be\"}]",
"auxiliary": "{\"gnss_time\":\"000000000000\",\"acc_duration\":0,\"position_age\":0,\"hdop\":0}",
"sensor": "[{\"light\":0,\"accelerometer\":\"x:0,y:0,z:0\",\"data_type\":0}]",
"software_version": "\"AOVX_AM300-GL_H2.0_BG95M3LAR02A04_V2.0.4:v15\"",
"device": "{\"iccid\":\"89320420000012218671\",\"imei\":\"0864593054482897\",\"device_type\":\"AM300-GL\\u0000\\u0000\",\"work_model\":4}"
},
"gnssTime": "2023-03-01T10:18:04Z",
"gnssValue": 0,
"gsmValue": 158,
"hexMsgId": "0x0200",
"lat": 0.0,
"lbsCells": ["310,410,33539,79776528,-97"],
"locationType": 0,
"lon": 0.0,
"mileage": 0.0,
"msgFlowId": 72,
"recvTime": "2023-04-27T08:34:11.902Z",
"replyMsg": "7e80010005593054482897004800480200004c7e",
"speed": 0.0,
"statusMap": {
"operate": 1,
"load": 0,
"realtime_data": 1,
"oil_electric": 0,
"normal_data": 0,
"door_lock": 0,
"oil_circuit": 0,
"confidential": 0
},
"terminalNum": "593054482897",
"voltage": 3.555
}
3.2.拆包方法效果
调用示例:
@Test
public void splitData() throws Exception {
String rowData="7E020000CA593054482897004800000000B0080010000000000000000000000000000023030110180430019E310100F00E0136019A04C14B10000083039F03F22C414F56585F414D3330302D474C5F48322E305F424739354D334C415230324130345F56322E302E343A763135F423542160033C41C354216000E04DC33CB74B797D02B8C0F4C114700274BD3EB74B797D02BEBBF60A00090000000000000000F70400000DE3F81D04086459305448289789320420000012218671414D3330302D474C0000F912000F000000000000000000000000000000008C7E";
List<String> splitDataList=dataParser.splitData(rowData);
System.out.println("SplitData:"+splitDataList);
}
输出内容:
[
7E- - > Start Flag,
0200-- > Message ID,
00 CA-- > Properties of Message Body,
593054482897-- > Device Number,
0048-- > Message serial number,
00000000-- > Alarm flag,
B0080010-- > Terminal status,
00000000-- > Latitude,
00000000-- > Longitude,
0000-- > Altitude,
2303-- > Speed,
1101804-- > Direction,
30019e310100-- > Datetime,
F0-- > Extension ID,
0E- - > Extension information length,
0136019a04c14b10000083039f03-- > Extension information,
F2-- > Extension ID,
2C-- > Extension information length,
414f56585f414d3330302d474c5f48322e305f424739354d334c415230324130345f56322e302e343a763135-- > Extension information,
F4-- > Extension ID,
23-- > Extension information length,
542160033c41c354216000e04dc33cb74b797eb8c0f4c114700274bd3eb74b797ebebb-- > Extension information,
F6-- > Extension ID,
0A-- > Extension information length,
00090000000000000000-- > Extension information,
F7-- > Extension ID,
04-- > Extension information length,
00000de3-- > Extension information,
F8-- > Extension ID,
1D-- > Extension information length,
04086459305448289789320420000012218671414d3330302d474c0000-- > Extension information,
F9-- > Extension ID,
12-- > Extension information length,
000f00000000000000000000000000000000-- > Extension information,
8 C-- > Check Code,
7E- - > End Flag
]
4.协议组包
4.1.组包方法 encodeCommand(String paramsJson)
传入的JSON字符串
4.2.传入JSON参数说明
{"msgFlowId":1,"msgId":33027,"params":{1:10,61488:"AT+QUERY?"},"terminalNum":"12345678901"}
字段名称 | 字段类型 | 字段说明 |
---|---|---|
terminalNum | String | 设备S/N |
msgFlowId | int | 指令流水号(1~65535) |
msgId | int | 协议定义的消息ID,例如:0x8103,0x8104,0x8105 |
params | Map | key:value;其中,key:命令ID,value:配置的值 |
其中0x8103(设置终端参数)
对应的params,可以设置的命令ID以及传入的值说明。
其中0x8104(查询终端参数)
params为空
其中0x8105(终端控制命令)
4.3.组包方法示例
此方法是以0x8103为例,同时设置心跳间隔以及发送AT命令
对应的JSON字符串如下:
{"msgFlowId":1,"msgId":33027,"params":{1:10,61488:"AT+QUERY?"},"terminalNum":"12345678901"}
作者:admin 创建时间:2023-04-26 11:02
最后编辑:admin 更新时间:2024-10-08 16:22
最后编辑:admin 更新时间:2024-10-08 16:22