优秀的编程知识分享平台

网站首页 > 技术文章 正文

车载Tbox-CAN终端数据采集实现方案

nanyue 2024-12-20 17:46:24 技术文章 4 ℃

因工作关系有机会与一家做汽车数据采集公司的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;
}

处理结果

Tags:

最近发表
标签列表