本文主要描述的是汽车行业中DBC文件的格式,如何去解析它,如何通过文本编辑器去修改它,了解DBC文件之前如果不懂DBC的请去查看我之前的博客。
DBC,DBC解析,DBC数据库,DBC文件,C#解析DBC文件。
DBC文件在汽车行业应用十分广泛,如果编辑DBC文件我们都使用Vector工具CANdb++ Editor去编辑,那效率将会是十分的低下,当我们了解了DBC文件的构成后,我们可以通过其他方式进行解析,可以大大的提高工作的效率,我们可以通过记事本或者其他工具打开DBC文件。
关键字:VERSION
格式:VERSION "version"
version就是版本信息,版本信息可以为空,但是不能省略""符。
例如:VERSION "V1.0"代表版本号为V1.0。
关键字:NS_:
DBC生成后都会自动生成新符号,所以这一部分的信息我们无需过多留意,一般默认即可,如果是通过其他方式去生成DBC文件时,可以直接复制该信息即可。
关键字:BS_:
格式:BS_:[baudrate:BTR1,BTR2]
其中[ ]内容表示为可选部分,可以省略,但关键字”BS_:”必须存在,省略会出错。
关键字:BU_:
格式:BU_: name1 name2 name3 …
格式中的name1 、name2 等表示定义的网络节点名字,由用户自己定义;但需保证节点命名的唯一性。
例如:BU_: GW代表网络节点有GW。
关键字:BO_
格式:BO_ Msg_ID Msg_Name: Msg_Length Msg_Transmitter
Msg_ID:报文标识符,十进制表示。
Msg_Name:报文名称,命名规则和C语言变量相同。
Msg_Length :报文长度,长度范围为0-8。
Msg_Transmitter:发送节点,如果该报文没有指定发送节点,则该值需设置为” Vector__XXX”。
例如:BO_ 1024 BCM_400: 8 BCM代表报文ID为0x400,报文名称为BCM_400,长度为8一个字节,发送节点为BCM。
关键字:SG_
格式:SG_ Signal_Name : Start_Bit|Bit_Length@Byte_Order Date_Type (Factor, Offset) [Signal_Min_Value_Phy|Signal_Max_Value_Phy] “Unit” Receivers
Signal_Name:信号名称,命名规则和C语言变量相同。
Start_Bit:信号的起始位,范围为0-63。
Bit_Length:信号的长度,范围为1-64。
Byte_Order:信号的字节顺序:0代表Motorola格式,1代表Inter格式,这儿强调一下Motorola格式下的文本存储的是Motorola Msb格式的;
Date_Type:信号的数值类型:+表示无符号数,-表示有符号数。
Factor:精度。
Offset:偏移量,Factor和Offset这两个值于该信号的原始值与物理值之间的转换,转换如下:物理值=原始值*因子+偏移量。
Signal_Min_Value_Phy:物理总线最小值。
Signal_Max_Value_Phy:物理总线最大值。
Unit:信号的单位,为字符串类型,可以省略。
Receivers:信号的接收节点,若该信号没有指定的接收节点,则必须设置为” Vector__XXX”。
例如:SG_ BCM_WakeUp : 0|1@0+ (1,0) [0|1] “%” Vector__XXX代表信号名称为BCM_WakeUp,信号起始位为0,信号长度为 1,排列格式为Motorola Msb,数据类型为无符号,精度为1,偏移量为0,单位为%,无接收节点。
关键字:CM_
格式:CM_ Object Msg_ID/Signal_Name “Signal_Description;”
Object:注解的对象类型,可以是节点“BU_”、报文“BO_”、消息”SG_”。
Msg_ID:报文标识符,十进制表示。
Signal_Name:信号名称,命名规则和C语言变量相同。
Signal_Description:信号描述,为字符串类型。
例如:CM_ BO_ 1024 “网络管理报文”;代表报文ID为0x400的报文描述为网络管理报文。
CM_ SG_ 1024 BCM_WakeUp “唤醒信号”;代表报文ID为0x400的报文中信号名称为BCM_WakeUp的描述信息为唤醒信号。
关键字:**BA_DEF_ **
格式:BA_DEF_ Object AttributeName ValueType Min Max;”
Object:注解的对象类型,可以是节点“BU_”、报文“BO_”、消息”SG_”、网络节点” ”(用空格表示)。
AttributeName: 自定义属性名称,命名规则和C语言变量相同。
ValueType:属性值的类型,可以是整型、字符串、浮点型、枚举类型等。
Min:属性值的最小值(字符串类型没有此项)。
Min:属性值的最大值(字符串类型没有此项)。
例如:BA_DEF_ “ECU” STRING ;代表自定义网络节点属性名称为ECU,类型为字符串型。
BA_DEF_ BO_ “MsgCycleTime” INT 0 1000;代表自定义报文属性名称为MsgCycleTime,类型为整型,取值范围为0-1000。
关键字:**BA_DEF_DEF_ **
格式:BA_DEF_DEF_ AttributeName DefaultValue;”
AttributeName: 自定义属性名称,命名规则和C语言变量相同。
DefaultValue:默认值。
例如:BA_DEF_DEF_ “MsgCycleTime” 0;代表自定义属性名称为MsgCycleTime的默认值为0。
关键字:BA_
格式:BA_ AttributeName Object /Signal_Name Msg_ID DefaultValue;”
AttributeName: 自定义属性名称,命名规则和C语言变量相同。
Object:注解的对象类型,可以是节点“BU_”、报文“BO_”、消息”SG_”、网络节点” ”(用空格表示)。
Signal_Name:信号名称,命名规则和C语言变量相同。
Msg_ID:报文标识符,十进制表示。
DefaultValue:默认值。
例如:BA_ “MsgCycleTime” BO_ 1024 0;代表自定义报文ID为0x400的报文属性名称MsgCycleTime值为100。
关键字:VAL_
格式:VAL_ Msg_ID Signal_Name N “DefineN” …… 0 “Define0”;”
Msg_ID:报文标识符,十进制表示。
Signal_Name:信号名称,命名规则和C语言变量相同。
N “DefineN” …… 0 “Define0”:表示定义的数值表内容,即该信号的有效值分别用什么符号表示。
例如:VAL_ 1024 BCM_WakeUp 1 “off” 0 “on” ;代表报文ID为0x400中的信号名称为BCM_WakeUp的值0x01代表off,0x00代表on。
通过以上描述,我相信各位对DBC文件的结构有一定的了解,但是我们如何去解析呢,很多人第一时间想到的应该是按行解析,其实DBC文件有许多容错处理,单纯按行解析我们会错过许多细节部分,例如下图其实也没有出错,如果按行解析的话报文就解析不到了。
还有很多类似的容错处理在里面,所以单纯按行解析是不行的,并且有的时候也是不能通过空格来分开数据的,比如带有“”的前后是可以不追加空格的。
经过好久的挣扎,查询资料,终于在一个博客的找到了一种解析的方式,通过正则表达式去解析,当然他其实是按行解析,其实会丢掉一些数据,我将方法进行了一些简单的优化,先读取整个文件到字符串,把多行空格转换成一行空格,然后把换行符转换成空格符,然后进行查找,然后按照标准进行拆分。我这边解析的主要使用的C#语言,因为C#语言对字符串有友好的操作,我将部分代码粘贴如下,方便大家参考学习。
/***********************************************
* Name :AnalysisDBCVersion
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCVersion(string data)
{
string pattern = "VERSION(([ ]+)|)\"((\\w+)|)\"";
MatchCollection matchs = Regex.Matches(data, pattern);
foreach (Match match in matchs)
{
if (matchs.Count == 1)
{
string[] array = match.Value.Split(new string[] {@"""" }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length >= 2)
{
string version = array[1];
}
return true;
}
}
return true;
}
/***********************************************
* Name :AnalysisDBCMessagePattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCMessagePattern(List<Message> messages, string data)
{
string pattern = "BO_[ ]+(\\d+)[ ]+(\\w+):(([ ]+)|)(\\d+)[ ]+(\\w+)";
foreach (Match match in Regex.Matches(data, pattern))
{
string[] array = match.Value.Split(new string[] { " ", ":" }, StringSplitOptions.RemoveEmptyEntries);
Message message = FindCanMsg(messages, (uint)ConvertToInt(array[1]));
if (message != null)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
message = new Message();
message.ID = (uint)ConvertToInt(array[1]);
message.Name = ConvertToName(array[2]);
message.DLC = ConvertToInt(array[3]);
message.Transmitter = array[4].Replace(" ", "") == "Vector__XXX" ? null : array[4].Replace(" ", "");
message.CycleTime = message.CycleTimeFast = message.NrOfRepetition = message.DelayTime = -1;
messages.Add(message);
}
return true;
}
/***********************************************
* Name :AnalysisDBCSignalPattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCSignalPattern(List<Message> messages, string data)
{
string pattern = "SG_[ ]+(\\w+)(([ ]+)|)(((m)(\\d+[ ]+))|(M[ ]+))?:(([ ]+)|)(\\d+)(([ ]+)|)\\|(([ ]+)|)(\\d+)(([ ]+)|)@(([ ]+)|)([0|1])(([ ]+)|)([+|-])(([ ]+)|)+\\((([ ]+)|)(-?\\d+(\\.\\d+)?)(([ ]+)|),(([ ]+)|)(-?\\d+(\\.\\d+)?)(([ ]+)|)\\)(([ ]+)|)\\[(([ ]+)|)(-?\\d+(\\.\\d+)?)(([ ]+)|)\\|(([ ]+)|)(-?\\d+(\\.\\d+((E\\+\\d+)|))?)(([ ]+)|)\\](([ ]+)|)\"((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*)\"(([ ]+)|)(\\w+(,[ ]*\\w+)*)";
foreach (Match match in Regex.Matches(data, pattern))
{
Signal signal = new Signal();
string strPre = data.Substring(0, data.IndexOf(match.Value));
pattern = "BO_[ ]+(\\d+)[ ]+(\\w+):(([ ]+)|)(\\d+)[ ]+(\\w+)";
MatchCollection mc = Regex.Matches(strPre, pattern);
if (mc.Count == 0)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
string[] arr = mc[mc.Count - 1].Value.Split(new string[] { " ", ":" }, StringSplitOptions.RemoveEmptyEntries);
uint id = (uint)ConvertToInt(arr[1]);
Message message = FindCanMsg(messages, id);
if (message == null)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
string str = match.Value.Replace("+", " +").Replace("-", " -");
string[] array1 = str.Split(new string[] { @"""" }, StringSplitOptions.None);
string[] array2 = array1[0].Split(new string[] { " ", ":", ",", "|", "@", "(", ")", "[", "]" }, StringSplitOptions.RemoveEmptyEntries);
int startbit = ConvertToInt(array2[2]);
int bitlenth = ConvertToInt(array2[3]);
int sigbyte = (startbit / 8 + (bitlenth - 1) / 8 + ((startbit % 8 + 1 - ((bitlenth % 8) == 0 ? 8 : (bitlenth % 8))) >= 0 ? 0 : 1));
int sigbit = (startbit + 1 - bitlenth % 8) % 8;
int siglsb = sigbyte * 8 + sigbit;
signal.Name = ConvertToName(array2[1]);
signal.Startbit = array2[4] == "0" ? siglsb : ConvertToInt(array2[2]);
signal.Length = ConvertToInt(array2[3]);
signal.ByteOrder = (ByteOrderEnum)ConvertToInt(array2[4]);
signal.DataType = array2[5] == "-" ? DataTypeEnum.Signed : DataTypeEnum.Unsigned;
signal.Factor = ConvertToDouble(array2[6]);
signal.Offst = ConvertToDouble(array2[7]);
signal.MinValuePhy = ConvertToDouble(array2[8]);
signal.MaxValuePhy = ConvertToDouble(array2[9]);
signal.Unit = array1[1].Replace(" ", "");
signal.Receivers = array1[2].Replace(" ", "") == "Vector__XXX" ? null : array1[2].Replace(" ", "");
signal.InitValue = signal.InvalidValue = signal.InvalidValue = signal.InactiveValue = signal.MinValue = signal.MaxValue = -1;
message.Signals.Add(signal);
}
return true;
}
/***********************************************
* Name :AnalysisDBCCommentsPattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCCommentsPattern(List<Message> messages, string data)
{
string pattern = "CM_[ ]+((((BU_)|(BO_)|(SG_))[ ]+(\\d+)[ ]+(\\w+)(([ ]+)|))|)\"((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*)\"(([ ]+)|);";
foreach (Match match in Regex.Matches(data, pattern))
{
bool result = false;
string[] array1 = match.Value.Split(new string[] {@"""" }, StringSplitOptions.None);
string[] array2 = array1[0].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (array2.Length == 1)
{
string comments = array1[1];
}
else
{
if (array2[1] == "SG_")
{
uint id = (uint)ConvertToInt(array2[2]);
string name = ConvertToName(array2[3]);
foreach (var message in messages)
{
if (id == message.ID)
{
foreach (var signal in message.Signals)
{
if (name == signal.Name)
{
result = true;
signal.Comments = array1[1];
break;
}
}
}
}
if (!result)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
}
}
}
return true;
}
/***********************************************
* Name :AnalysisDBCAttributeDefinitionPattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCNatureDefinitionPattern(List<Nature> natures, string data)
{
string pattern = "BA_DEF_[ ]+((BU_)|(BO_)|(SG_)|(EV_))?(([ ]+)|)\"(\\w+)\"(([ ]+)|)(((INT)[ ]+([+|-]?\\d+)[ ]+([+|-]?\\d+))|((HEX)[ ]+([+|-]?\\d+)[ ]+([+|-]?\\d+))|((FLOAT)[ ]+([+|-]?\\d+.?\\d*)[ ]+([+|-]?\\d+.?\\d*))|(STRING)|((ENUM)(([ ]+)|)(\"((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*)\"([ ]*,(([ ]+)|)\"((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*)\")*)))[ ]*(([ ]+)|);";
foreach (Match match in Regex.Matches(data, pattern))
{
string[] array1 = match.Value.Split(new string[] { " ", @"""" }, StringSplitOptions.RemoveEmptyEntries);
foreach (Nature nature in natures)
{
if ((nature.Family == array1[1]) && (nature.Name == array1[2]))
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
}
Nature nat = new Nature();
nat.Family = array1[1];
nat.Name = ConvertToName(array1[2]);
int StartNum = 3;
if ((nat.Family != "BU_") && (nat.Family != "BO_") && (nat.Family != "SG_") && (nat.Family != "EV_"))
{
StartNum = 2;
nat.Family = null;
nat.Name = ConvertToName(array1[1]);
}
nat.Type = array1[StartNum];
if ((nat.Type == "INT") || (nat.Type == "HEX") || (nat.Type == "FLOAT"))
{
nat.data.Add(array1[StartNum + 1]);
nat.data.Add(array1[StartNum + 1]);
}
else if (nat.Type == "ENUM")
{
string[] array2 = match.Value.Split(new string[] {"ENUM"}, StringSplitOptions.RemoveEmptyEntries);
string[] array3 = array2[1].Replace(" ", "").Split(new string[] { ",", @"""" }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < array3.Length - 1; i++)
{
nat.data.Add(array3[i]);
}
}
natures.Add(nat);
}
return true;
}
/***********************************************
* Name :AnalysisDBCNatureDefaultPattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCNatureDefaultPattern(List<Message> messages, List<Nature> natures, string data)
{
string pattern = "BA_DEF_DEF_[ ]+\"(\\w+)\"(([ ]+)|)\"?(([+|-]?\\d*.?\\d*)|((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*))\"?(([ ]+)|);";
foreach (Match match in Regex.Matches(data, pattern))
{
bool result = false;
string str = match.Value.Replace(@""" ", @"""").Replace(@" """, @"""").Trim(';');
string[] array = str.Split(new string[] {@""""}, StringSplitOptions.RemoveEmptyEntries);
string name = ConvertToName(array[1]);
string init = 2 >= array.Length ? "" : array[2];
foreach (Nature nature in natures)
{
if (nature.Name == name)
{
foreach (Message message in messages)
{
switch (name)
{
case "GenMsgCycleTime":
message.CycleTime = ConvertToInt(init);
break;
case "GenMsgCycleTimeFast":
message.CycleTimeFast = ConvertToInt(init);
break;
case "GenMsgNrOfRepetition":
message.NrOfRepetition = ConvertToInt(init);
break;
case "GenMsgDelayTime":
message.DelayTime = ConvertToInt(init);
break;
case "NmMessage":
message.Type = init;
break;
case "GenMsgSendType":
message.SendType = init;
break;
default:
break;
}
foreach (Signal signal in message.Signals)
{
switch (name)
{
case "GenSigStartValue":
signal.InitValue = ConvertToInt(init);
break;
case "GenSigTimeoutValue":
signal.InvalidValue = ConvertToInt(init);
break;
case "GenSigInactiveValue":
signal.InactiveValue = ConvertToInt(init);
break;
case "GenSigSendType":
signal.SendType = init;
break;
case "GenSigMinValue":
signal.MinValue = ConvertToInt(init);
break;
case "GenSigMaxValue":
signal.MaxValue = ConvertToInt(init);
break;
default:
break;
}
}
}
nature.InitValue = init;
result = true;
}
}
if (!result)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
}
return true;
}
/***********************************************
* Name :AnalysisDBCNatureValuePattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCNatureValuePattern(List<Message> messages, List<Nature> natures, string data)
{
string pattern = "BA_[ ]+\"(\\w+)\"(([ ]+)|)(((BU_)[ ]+(\\w+)[ ]+)|((BO_)[ ]+(\\d+)[ ]+)|((SG_)[ ]+(\\d+)[ ]+(\\w+)[ ]+)|((EV_)[ ]+(\\w+)[ ]+))?\"?(([+|-]?\\d*.?\\d*)|((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*))\"?(([ ]+)|);";
foreach (Match match in Regex.Matches(data, pattern))
{
bool result = false;
string str = match.Value.Trim(';');
string[] array = str.Split(new string[] { " ", @"""" }, StringSplitOptions.RemoveEmptyEntries);
string name = ConvertToName(array[1]);
uint id = array.Length <= 3 ? 0 : (uint)ConvertToInt(array[3]);
foreach (Nature nature in natures)
{
if (nature.Name == name)
{
foreach (Message message in messages)
{
if (message.ID == id)
{
if (array[2] == "BO_")
{
switch (name)
{
case "GenMsgCycleTime":
message.CycleTime = ConvertToInt(array[4]);
break;
case "GenMsgCycleTimeFast":
message.CycleTimeFast = ConvertToInt(array[4]);
break;
case "GenMsgNrOfRepetition":
message.NrOfRepetition = ConvertToInt(array[4]);
break;
case "GenMsgDelayTime":
message.DelayTime = ConvertToInt(array[4]);
break;
case "NmMessage":
message.Type = nature.data[ConvertToInt(array[4])];
break;
case "GenMsgSendType":
message.SendType = nature.data[ConvertToInt(array[4])];
break;
default:
break;
}
result = true;
}
else if (array[2] == "SG_")
{
foreach (Signal signal in message.Signals)
{
if (signal.Name == array[4])
{
switch (name)
{
case "GenSigStartValue":
signal.InitValue = ConvertToInt(array[5]);
break;
case "GenSigTimeoutValue":
signal.InvalidValue = ConvertToInt(array[5]);
break;
case "GenSigInactiveValue":
signal.InactiveValue = ConvertToInt(array[5]);
break;
case "GenSigSendType":
signal.SendType = nature.data[ConvertToInt(array[5])];
break;
case "GenSigMinValue":
signal.MinValue = ConvertToInt(array[5]);
break;
case "GenSigMaxValue":
signal.MaxValue = ConvertToInt(array[5]);
break;
default:
break;
}
result = true;
}
}
}
}
}
}
else
{
result = true;
}
}
if (!result)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
}
return true;
}
/***********************************************
* Name :AnalysisDBCSignalValueDescriptionPattern
* Author :WangHu
* Function:***
* Version :V1.0.0
* Data :2020.4.15
***********************************************/
internal static bool AnalysisDBCSignalValueDescriptionPattern(List<Message> messages, string data)
{
string pattern = "VAL_[ ]+(\\d+)[ ]+(\\w+)[ ]+(((\\d+.?\\d*)(([ ]+)|)\"((([^\"\\s])|([\\s\\u4e00-\\u9fa5]))*)\"[ ]*)*)+(([ ]+)|);";
foreach (Match match in Regex.Matches(data, pattern))
{
bool result = false;
string str = match.Value.Replace(@""" ", @"""").Replace(@" """, @"""").Trim(';');
string[] array1 = str.Split(new string[] { @"""" }, StringSplitOptions.RemoveEmptyEntries);
string[] array2 = array1[0].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
uint id = (uint)ConvertToInt(array2[1]);
string name = ConvertToName(array2[2]);
foreach (var message in messages)
{
if (message.ID == id)
{
foreach (var signal in message.Signals)
{
if (signal.Name == name)
{
result = true;
signal.ValueDescrptions = "";
for (int i = 0; i < (array1.Length)/2; i++)
{
if (signal.ValueDescrptions == "")
{
signal.ValueDescrptions += "0x" + ConvertToInt(array2[3]).ToString("X") + ":" + array1[1];
}
else
{
signal.ValueDescrptions += " \r\n0x" + ConvertToInt(array1[2 * i]).ToString("X") + ":" + array1[2 * i + 1];
}
}
break;
}
}
}
}
if (!result)
{
ShowMsg(@"Find line """ + match.Value + @""" message fault", ShowEnum.Fault);
return false;
}
}
return true;
}
这儿我只把链接放上,具体使用说明见后续章节。
DBC转Excel;DBC转位定义;Excel转DBC;Excel转位定义:https://download.csdn.net/download/weixin_44926112/12594467
本文主要是讲解DBC文件的结构和如何解析DBC文件,有些地方可能会有描述性的错误,希望看到的朋友及时指出,我会及时更正错误,其他地方有些借鉴的描述,写此文章的目的是为了交流,非商业用途,欢迎私信讨论,感谢大家阅读。
【1】:https://blog.csdn.net/weixin_44536482/article/details/89030152
【2】:https://blog.csdn.net/Jiang_Mr/article/details/103417163