网站首页 > 技术文章 正文
因工作关系有机会与一家做汽车数据采集公司的Tbox终端产品做数据对接。
这个产品通过can口与汽车总线交换数据,再将采集的数据打包通过4G以tcp报文发送到云端。云端通过解析、格式转换后保存到数据库。
本文介绍用nodejs实现tcp报文的解析,报文描述如下:
TBOX通讯协议描述
协议:TCP
采用固定帧长度:186(4+20+16*10+2)字节
帧头(固定4字节) | 设备序列号(20字节) | 数据包1 (16字节) | 数据包2 (16字节) | ... | 数据包10 (16字节) | CRC16校验 (2字节) |
帧头: AABB ( ASCII码格式,4 字节)
设备序列号: ICITBOX_202103240040 (ASCII码格式,20字节)
数据包: 格式见下表
CRC16校验: 计算方法,见文档附录,从帧头开始,至数据的最后一个字节结束(共184字节)
数据包格式(长度固定为16byte):
时间(4字节) | CAN-ID(4字节) | CAN-Data(8字节) |
时间: unix时间戳
CAN-ID(32bit) :
bit0 -28 : CAN-ID
bit29-31: 无效
CAN-Data:CAN的8字节数据(具体含义参见CAN协议文档)
协议解析举例:
时间戳: 0x6049CFB4 -- 转北京时间:2021-03-11 16:07:16
ID: 0xF41A0301 (bit29-31无效) 则为:0x141A0301
数据: 0x03 0x10 0xC0 0x0F 0x3A 0x38 0x40 0x3D
解决思路:
1、通过ID数据项描述,提炼出数学模型;
2、编写js脚本,通过模拟payload数据驱动脚本,依据数据结构进行相应的处理,并将处理结果返回json格式;
{ // 数学建模-- 结构标准化
"ID": "141A0301",
"name": "组合仪表显示信息(3)",
"Byte": [{
"start": 0,
"length": 2,
"mark": "Oct",
"off": 0,
"unit": "mV",
"res": 1,
"type":"BIG",
"values": [{
"name": "电池最高单体电压",
"pinyin":"dczgdtdy",
"start": 0,
"bits": 16,
"mark": "Oct"
}]
},
{
"start": 2,
"length": 2,
"mark": "Oct",
"off": 0,
"unit": "mV",
"res": 1,
"type":"BIG",
"values": [{
"name": "电池最低单体电压",
"pinyin":"dczddtdy",
"start": 0,
"bits": 16,
"mark": "Oct"
}]
},
{
"start": 4,
"length": 1,
"mark": "Oct",
"off": -40,
"unit": "℃",
"res": 1,
"type":"BIG",
"values": [{
"name": "电池最高温度",
"pinyin":"dczgwd",
"start": 0,
"bits": 8,
"mark": "Oct"
}]
},
{
"start": 5,
"length": 1,
"mark": "Oct",
"off": -40,
"unit": "℃",
"res": 1,
"type":"BIG",
"values": [{
"name": "电池最低温度",
"pinyin":"dczdwd",
"start": 0,
"bits": 8,
"mark": "Oct"
}]
},
{
"start": 6,
"length": 1,
"mark": "Oct",
"off": -40,
"unit": "℃",
"res": 1,
"type":"BIG",
"values": [{
"name": "电机控制器温度",
"pinyin":"djkzqwd",
"start": 0,
"bits": 8,
"mark": "Oct"
}]
},
{
"start": 7,
"length": 1,
"mark": "Oct",
"off": -40,
"unit": "℃",
"res": 1,
"type":"BIG",
"values": [{
"name": "电机温度",
"pinyin":"djwd",
"start": 0,
"bits": 8,
"mark": "Oct"
}]
}
/*
* payload 车载终端数据定义
* ID 车载ID
* xxx 车载数据
*
*/
function dealTBox(payload,ID){
//var xxx= "8310C00F3A38403D";
var res = [];
// decode payload to string
for( var key in payload ){
if(payload[key]['ID'] == ID){
console.log(ID);
console.log(payload[key]["name"]);
var js = payload[key]['Byte'];
var byx = [];
/*for(var k in js)
//by.push(xxx.substring(js[k]['start'],js[k]['start']+js[k]['length']*2));
if(js[k]['type'] == "BIG"){
by.push(hexToDecLE2(xxx.substring(js[k]['start'],js[k]['start']+js[k]['length']*2)));
}else{
by.push(xxx.substring(js[k]['start'],js[k]['start']+js[k]['length']*2));
}*/
for(var l in js){
if(js[l]['values']){
var js_v = js[l]['values']; // 外层values
if(js[l]['type'] == "BIG"){ // 判断大小端模式,截取数据长度
var by = hexToDecLE2(xxx.substring(js[l]['start']*2,js[l]['start']*2+js[l]['length']*2));
}else{
var by = xxx.substring(js[l]['start']*2,js[l]['start']*2+js[l]['length']*2);
}
for(var v in js_v){ // 进入第一层values循环
if(js[l]['mark'] == "Bin"){ // 需要解析为二进制?
var sum_len = js[l]['length'] * 8;
console.log("sum_len:"+sum_len);
var lll = hex_to_bin(by); // 对应截取的数据转2进制
console.log(lll);
var t_start = sum_len - js_v[v]["start"] - js_v[v]["bits"]; // 2进制字串的截取开始位
var t_end = t_start + js_v[v]["bits"]; // 2进制字串的截取结束位
console.log(t_start + "------" + t_end);
console.log(lll.substring(t_start,t_end));
var v_n = lll.substring(t_start,t_end);
if(js_v[v]["mark"] == "Oct"){
var v_n_oct = parseInt(v_n,2);
if(js_v[v]["values"]){ // 如果还有对十进制的结果有定义,可以直接读取values数组
console.log(js_v[v]["name"] +":"+ js_v[v]["values"][v_n_oct] +" "+ js[l]["unit"]);
byx.push('{"'+js_v[v]["name"]+'":"'+js_v[v]["values"][v_n_oct]+'"}');
res
}else{
console.log(js_v[v]["name"] +":"+ v_n_oct +" "+ js[l]["unit"]);
byx.push('{"'+js_v[v]["name"]+'":"'+(v_n_oct*1 + js[l]["off"]*1)+'"}');
}
}
}else if(js[l]['mark'] == "Oct"){ // 直接解析为10进制
console.log(by);
var lll = hexToDec(by); // 对应截取的数据转10进制
if(js_v[v]["values"]){ // 如果还有对十进制的结果有定义,可以直接读取values数组
console.log(js_v[v]["name"] +":"+ js_v[v]["values"][lll] +" "+ js[l]["unit"]);
byx.push('{"'+js_v[v]["name"]+'":"'+js_v[v]["values"][lll]+'"}');
}else{
console.log(js_v[v]["name"] +":"+ lll +" "+ js[l]["unit"]);
byx.push('{"'+js_v[v]["name"]+'":"'+(lll*1 + js[l]["off"]*1)+'"}');
}
}
//console.log(js[l]['values'][v]['name'])
}
}
}
res.push(JSON.parse('{"deviceName":'+'"'+payload[key]["name"]+'","body":['+byx.join(",")+']}'));
}
}
return res;
}
处理结果
猜你喜欢
- 2024-12-20 微软确认Win10更新出现问题:建议完全重置系统
- 2024-12-20 【MFRC522】STM32 MFRC522 IC读卡 程序及原理图
- 2024-12-20 win10更新又双叒翻车 这几天出了这些问题都是它的锅
- 2024-12-20 微软 Win10 KB5003173 更新安装失败,原因是没有新版 Edge浏览器
- 2024-12-20 今年最后一次可选更新:微软Win11推送26100.2454
- 2024-12-20 关于BIOS的入口地址0xFFFF0 bios information
- 2024-12-20 部分Win11 24H2用户反馈微软12月更新BUG:安装失败、蓝屏等
- 2024-12-20 Win10用户反馈安装微软3月更新时遇到0xd0000034错误
- 2024-12-20 Win11 22H2 (KB5017321) 安装更新失败如何修复
- 2024-12-20 R730调整风扇转速 r710风扇速度设置
- 1507℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 511℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 487℃MySQL service启动脚本浅析(r12笔记第59天)
- 467℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 465℃启用MySQL查询缓存(mysql8.0查询缓存)
- 445℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 424℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 421℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)