本文通过Matlab函数regexp的正则表达式功能,解析DBC文件中的报文帧信息。DBC文件中的信号等其他信息都可以通过类似的方式解析出来。
1 DBC文件
DBC数据库文件是用来描述CAN网络节点间数据通讯的一种文件,做汽车CAN网络通信的话肯定是绕不开DBC文件的。
关于DBC文件格式的内容可以参考另一位博主的文章关于DBC文件的格式解析(DBC文件系列其二),本文后面也会引用到其中的一些知识内容。
DBC文件的格式有很强的规律性,所以可以很轻松地通过正则表达式函数regexp解析出其中地信息。实际上,Matlab提供了封装好的函数canDatabase(),可以更加快速准确地解析DBC中的帧和信号。本文没有使用封装的函数,是希望以DBC为例,训练正则表达式的应用技巧,以便在遇到其他需求没有现成的函数时,自己也能造出轮子。
2 正则表达式函数
在Matlab中,正则表达式函数是regexp。关于正则表达式的语法不用死记硬背,用到的时候去查Matlab帮助文档就行。
3 实例:解析报文帧信息
首先,自己新建一个DBC文件,一般是通过CANdb++这类DBC工具。不过这里为了方便就不去手动建立了,直接把如下内容拷贝进文本文件,并命名为DBC_Demo.dbc。
BS_:
BU_: AVNT ACU HUD
BO_996 HUD_1_B: 8 HUD
SG_ HUD_OffSt : 0|1@0- (1,0) [-1|0] "" ACU,AVNT
SG_ HUD_HeightLv : 7|7@0+ (1,0) [0|127] "lv" ACU,AVNT
SG_ HUD_BrightnessLv : 15|4@0+ (1,0) [0|15] "lv" ACU,AVNT
CM_ BO_ 996 "The message of control hud status";
CM_ SG_ 996 HUD_BrightnessLv "Control hud brighness level";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其中,BO_开头的那一行就是报文帧,其格式定义如下:
4 Matlab脚本
1)首先要将DBC文件的文本内容导入到Matlab中。在Matlab命令行输入
DBCText = fileread('DBC_Demo.dbc')
- 1
就会将DBC文件内容作为字符串返回到DBCText变量中。
2)用正则表达式提取出报文帧那一行,也就是BO_开头的那一行。这里开始就要研究BO_这一行的模式了。
- BO关键字,在正则表达式模式中直接用BO_完全匹配即可;
- 报文ID,是若干个数字可以用d+表示1个或以上的阿拉伯数字;
- 报文名称,一般是由字母、数字和下划线组成的字符串,所以可以用w+表示;
- 报文数据域字节数,无符号整数,直接用d+表示;
- 网络节点:一般是由字母、数字和下划线组成的字符串,所以可以用w+表示;
所以报文帧这一行的模式可以写为:
>> pattern = 'BO_s*d+s+w+:s+d+s+w+';
- 1
中间的s+或者s*代表空格符。
然后就可以通过regexp解析出BO这一行了。注意加上’match’这个参数以返回字符串。
>> BO_Cell = regexp(DBCText,pattern,'match')
- 1
这里要注意,返回的的是一个字符串组成的元胞数组。由于demo中只有一个报文帧的定义,所以元胞数组中只有一个字符串。
3)解析元胞数组中的每一个报文帧行,提取出信息,这里只以一个报文帧为例。首先解析出报文ID,只要把之前的pattern改一下就行。
pattern_MessageId = '(?<=BO_s*)d+(?=s+w+:s+d+s+w+)';
- 1
这里用了正则表达式的上下文匹配,语法是*(?=test)和(?<=test)*,其中test是上下文内容。这样就能根据上下文匹配出报文帧了。
再比如说报文名称也用上下文匹配:
>> MessageName_Cell = regexp(BO_Cell{1},'(?<=BO_s*d+s+)w+(?=:s+d+s+w+)','match')
MessageName_Cell =
1×1 cell 数组
{'HUD_1_B'}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5 总结
用正则表达式解析DBC文件比较容易,只是需要一点耐心慢慢地调。除了报文帧,其他信号、波特率、节点等信息都能通过类似的方式解析。这样的技术也可以迁移到其他文件,只要我们了解了该文件的书写规则就行。
附 完整代码
测试用的DBC文件如下:
BS_:
BU_: AVNT ACU HUD
BO_996 HUD_1_B: 8 HUD
SG_ HUD_OffSt : 0|1@0- (1,0) [-1|0] "" ACU,AVNT
SG_ HUD_HeightLv : 7|7@0+ (1,0) [0|127] "lv" ACU,AVNT
SG_ HUD_BrightnessLv : 15|4@0+ (1,0) [0|15] "lv" ACU,AVNT
CM_ BO_ 996 "The message of control hud status";
CM_ SG_ 996 HUD_BrightnessLv "Control hud brighness level";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
博主自己写的脚本,可能会有考虑不周的地方,不一定适用于任何DBC文件。但是思路和方法是这样的,可以在遇到问题时尝试修改脚本,不断迭代。脚本内容如下:
function Message_Struct = ImportDBC()
%% 导入DBC文本
DBCText = fileread('DBC_Demo.dbc');
%% 解析BO_和SG_
pattern = 'BO_s*d+s+w+:s+d+s+w+s*
(s*SG_.*?
)*';
BO_Cell = regexp(DBCText,pattern,'match');
for i = 1:length(BO_Cell)
BO_Str = BO_Cell{i};
Message_Struct = Parse_Message(BO_Str);
SG_Cell = regexp(BO_Str,'SG.*?
','match');
Signal_Cell = {};
for j = 1:length(SG_Cell)
SG_Str = SG_Cell{j};
Signal_Struct = Parse_Signal(SG_Str);
Signal_Cell{end+1} = Signal_Struct;
end
Message_Struct = setfield(Message_Struct,'Signal',Signal_Cell);
end
end
%解析信号属性
function Signal_Struct = Parse_Signal(SG_Str)
%pattern:SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+rn
%SignalName
Temp = regexp(SG_Str,'(?<=SG_s*)w+(?=s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
SignalName = Temp{1};
%StartBit
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*)d+(?=|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
StartBit = Temp{1};
%SignalSize
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|)d+(?=@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
SignalSize = Temp{1};
%ByteOrder
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@)(0|1)(?=s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
if(strcmp(Temp{1},'0'))
ByteOrder = 'Motorola';
else
ByteOrder = 'Intel';
end
%ValueType
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*)(+|-)(?=s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
if(strcmp(Temp{1},'+'))
ValueType = 'Unsigned';
else
ValueType = 'Signed';
end
%Factor
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*()-*d+(?=,-*d+)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
Factor = Temp{1};
%Offset
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,)-*d+(?=)s*[-*d+|-*d+]s*"w*"s*(w|,)+
)','match');
Offset = Temp{1};
%Min
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[)-*d+(?=|-*d+]s*"w*"s*(w|,)+
)','match');
Min = Temp{1};
%Max
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|)-*d+(?=]s*"w*"s*(w|,)+
)','match');
Max = Temp{1};
%Unit
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*)"w*"(?=s*(w|,)+
)','match');
Unit = Temp{1};
%Receiver
Temp = regexp(SG_Str,'(?<=SG_s*w+s*:s*d+|d+@(0|1)s*(+|-)s*(-*d+,-*d+)s*[-*d+|-*d+]s*"w*"s*)(w|,)+(?=
)','match');
Receiver = Temp{1};
%Signal_Struct
Signal_Struct = struct('SignalName',SignalName,'StartBit',StartBit,'SignalSize',SignalSize,'ByteOrder',ByteOrder,'ValueType',ValueType,...
'Factor',Factor,'Offset',Offset,'Min',Min,'Max',Max,'Unit',Unit,'Receiver',Receiver);
end
% 解析报文属性
function Message_Struct = Parse_Message(BO_Str)
%pattern:'BO_s*d+s+w+:s+d+s+w+'
%MessageId
Temp = regexp(BO_Str,'(?<=BO_s*)d+(?=s+w+:s+d+s+w+)','match');
MessageId = Temp{1};
%MessageName
Temp = regexp(BO_Str,'(?<=BO_s*d+s+)w+(?=:s+d+s+w+)','match');
MessageName = Temp{1};
%MessageSize
Temp = regexp(BO_Str,'(?<=BO_s*d+s+w+:s+)d+(?=s+w+)','match');
MessageSize = Temp{1};
%Transmitter
Temp = regexp(BO_Str,'(?<=BO_s*d+s+w+:s+d+s+)w+','match');
Transmitter = Temp{1};
%Message_Struct
Message_Struct = struct('MessageId',MessageId,'MessageName',MessageName,'MessageSize',MessageSize,'Transmitter',Transmitter,'Signal','');
end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
评论记录:
回复评论: