这里写自定义目录标题
官网文档下载
CSDN下载 : DBC-File-Format-Documentation.pdf
网络拓扑
理解Network,Node,Message,Signal
文件格式解读
说明:dbc文件以空格符拆分数据信息,类似于csv文件以",“拆分数据的意思,” “属于分隔符。还有其他分割信息的符号,如 “|”,”,“,”()“,”[]"等
Keyword | Object Type | Comment |
---|---|---|
VERSOIN | 版本信息,为空,也可以自定义 | |
BU_ | Network Node | 网络节点 |
BO_ | Message | 消息、报文 |
SG_ | Signal | 信号 |
EV_ | Environment Variable | 环境变量 |
CM_ | 注解部分 | |
BA_DEF_ | 特征名称类型定义 | |
BA_DEF_DEF_ | 特征默认值定义 | |
BA_ | 特征项目设置值定义 | |
NS_: | new symbol | 后面紧跟着一堆ns,一般是创建dbc时自动生成,不用太关心 |
VAL_ | 数值表部分 |
Symbol | Meaning |
---|---|
= | A name on the left of the = is defined using the syntax on the right (syntax rule). |
; | The semicolon terminates a definition. |
| | The vertical bar indicates an alternative. |
[ … ] | The definitions within brackets are optional (zero or one occurrence). |
{ … } | The definitions within braces repeated (zero or multiple occurrences) |
( … ) | Parentheses define grouped elements. |
’ … ’ | Text in hyphens has to appear as defined. |
(* … *) | Comment. |
文件格式
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
BS_:
BU_: Engine Gateway
BO_ 100 EngineData: 8 Engine
SG_ PetrolLevel : 24|8@1+ (1,0) [0|255] "l" Gateway
SG_ EngPower : 48|16@1+ (0.01,0) [0|150] "kW" Gateway
SG_ EngForce : 32|16@1+ (1,0) [0|0] "N" Gateway
SG_ IdleRunning : 23|1@1+ (1,0) [0|0] "" Gateway
SG_ EngTemp : 16|7@1+ (2,-50) [-50|150] "degC" Gateway
SG_ EngSpeed : 0|16@1+ (1,0) [0|8000] "rpm" Gateway
CM_ "CAN communication matrix for power train electronics
VAL_ 100 IdleRunning 0 "Running" 1 "Idle" ;
BS_:[baudrate:BTR1,BTR2];
//波特率设置,BS_为关键字,其中[]中为可选部分
BU_:Nodename1 Nodename2 Nodename3 ……
//网络节点定义,其中Nodename为网络节点名字,用户自定义,名字需要唯一性
1、报文消息数据格式解读 BO_
格式:
BO_ MessageId MessageName: MessageSize Transmitter
MessageId 为10进制表示的报文ID,类型为longlogn型,即CAN ID
MessageName 报文的名字,与C语言命令规范相同
MessageSize 报文数据段字节数
Transmitter 该报文的网络节点,如果该报文没有指定发送节点,则该值需设置为”Vector__XXX”
举例:
以下是DBC中代表一条消息的描述信息
BO_ 1015 IPK_ODO_Consump: 8 Vector__XXX
解释
BO_ 代表一条消息的起始标识
1015 消息ID的十进制形式,=0x3f7
IPK_ODO_Consump 消息名
: 分割符号
8 消息报文长度,帧字节数
Vector__XXX 发出该消息的网络节点,标识为Vector__XXX时未指明具体节点
2、信号信息数据格式解读 SG_
格式
SG_ SignalName (SigTypeDefinition) : StartBit|SignalSize@ByteOrder ValueType (Factor,Offset) [Min|Max] Unit Receiver
SignalName (SigTypeDefinition) 表示该信号的名字 和 多路选择信号的定义
SigTypeDefinition 是可选项,有3种格式:
a> 空
b> M 表示多路选择器信号
c> m50 表示被多路选择器选择的信号,50表示当‘M’定义的信号的值等于50的时候,该报文使用此通路
StartBit|SignalSize 表示该信号的起始位及信号长度
ByteOrder 表示信号的字节顺序:0代表Motorola格式,1代表Inter格式
ValueType 表示该信号的数值类型:+表示无符号数,-表示有符号数
Factor,Offset 表示因子,偏移量;这两个值用于信号的原始值与物理值之间的转换。 转换公式:物理值=原始值*因子+偏移量
Min|Max 表示该信号的最小值和最大值,即指定了该信号值的范围;这两个值为double类型
Unit 表示该信号的物理单位,为字符串类型
Receiver 表示该信号的接收节点(可以是多个节点);若该信号没有指定的接收节点,则必须设置为” Vector__XXX”
举例:
每条报文消息里面有多个报文信号,报文信号的信息的起始标识为"SG_",
它以一个"BO_"开始至下一"BO_"之间的内容止,详细报文消息以缩进1或2个空格符形式类似树图子节点的方式呈现。
一条消息下的一个信号的信息,此处缩进一个空格
BO_ 100 EngineData: 8 Engine
SG_ IPK_EVDTEodometer : 7|12@0+ (1,0) [0|999] "km" TBOX
解释:
SG_ 代表一个信号信息的起始标识
IPK_EVDTEodometer 信号名,分长名与短名,此处是短名。长名非必须存在,可以不定义
: 分割符号
7 信号起始bit
| 分割符号
12 信号总长度
@0+ @0表示是Motorola格式(Intel格式是1),+表示是无符号数据
(1,0) (精度值,偏移值)
[0|999] [最小值|最大值], 物理意义的最小与最大,现实世界的有物理意义的值,比如此处仪表续航里程最大999KM
"km" "单位"
TBOX 接收处理此信号的节点,同样可以不指明,写为Vector__XXX
2.1 Motorola格式与Intel格式
填格子的方式分为Intel和Motorola两种:
Intel
格式也即小端,MSB存放在高字节单元,反映到矩阵图中就是以起始位为原点,自上而下填充。
Motorola
格式也即大端,MSB存放在低字节单元,反映到矩阵图中就是以起始位为原点,自下而上填充
这个决定了信号起始bit, 生成报文计算信号值时的大小端算法。
2.2 精度值与偏移量
物理值与信号值的关系公式:
信号值*精度值 + 偏移量 = 物理值
总线上报文消息中传递的是信号值,当此信号传递到ECU时,需转换为物理意义的值在输出接口显示。
举例:
SG_ TCU_TransOilTemp : 7|8@0+ (1,-40) [-40|214] "°C" TBOX
若传感器显示16度,则:
信号值=(物理值-偏移量)/精度值 = (16 - (-40))/1 = 56(dec) = 0x38
报文呈现为“38,00,00,00,00,00,00,00” (此处00表示未设置信号,","分割字节,以上同)
2.3 消息与信号的详细描述 CM_
CM_ Object MessageId/NodeName “Comment”
Object 表示注解的对象类型,可以是节点“BU_”、报文“BO_”、消息”SG_”
MessageId/NodeName
表示进行注解的对象,若前面的对象类型是信号或者报文,则这里的值应为报文的ID(10进制数表示);
若前面的对象类型为节点,则这里的值应为节点的名字
Comment 表示进行注解的文本信息
举例:
CM_ BO_ 1015 "Transmitted by IPK, power consumption and fuel consumption";
CM_ SG_ 1015 IPK_EVDTEodometer "The EV DTE odometer ";
解释:
CM_ 起始标识,我猜测CM_为comment缩写
2.4 消息发送周期 BA_
举例:
BA_ "GenMsgCycleTime" BO_ 1015 1000;
解释:
BA_ 起始标识,描述消息与信号更详尽的信息。 以上语句描述了消息的周期,单位ms
2.5 信号默认值,起始值 BA_
举例:
BA_ "GenSigStartValue" SG_ 1015 IPK_EVDTEodometer 4095;
解释:
BA_ 起始标识,描述消息与信号更详尽的信息。 以上语句描述了消息中具体信号的初始值,十进制表示
2.6 值枚举或特殊值列举或取值范围描述 VAL_
格式:
VAL_ MessageId SignalName N "DefineN" …… 0 "Define0";
MessageId 表示该信号所属的报文ID(10进制数表示)
SignalName 表示信号名
N "DefineN" …… 0 "Define0" 表示定义的数值表内容,即该信号的有效值分别用什么符号表示
举例:
VAL_ 1015 IPK_EVDTEodometer 4095 "Invalid" ;
解释:
VAL_ 起始标识符,对信号值的描述
3、特征(属性)BA_DEF_ , BA_DEF_DEF, BA_
格式:
BA_DEF_ Object AttributeName ValueType Min Max;
Object 特征类型,可以是:
BU_(节点特征定义)
BO_(报文特征定义)
SG_(信号特征定义)
空格(项目特征定义)
AttributeName
特征名称(C语言变量格式)
ValueType
特征值类型(只能是十进制、十六进制、浮点数、枚举、字符5种类型)
Min Max
数值类型这里出现范围,枚举类型这里是枚举值,字符类型,这里是空
BA_DEF_DEF_ AttributeName DefaultValue;
AttributeName 特征名称
DefaultValue 该特征的默认设置值
BA_ AttributeName projectValue;
AttributeName 特征名称
projectValue 该特征的设置值
Python 解析 DBC
下面推荐一篇从开源社区看到的一篇:
说一下下述文章的读后感吧,毕竟我也从头看到尾,而且很多细节需要自己改
- 下述文章总体思路是没有问题
- dbc文件每个人的都不同,所以下述脚本由一些细节地方需要手改
- 报错是肯定会的,关键在于改
- 最关键的是吸收思路
#! python3
"""
File: dbcParser.py
Created: 03/29/2017
This Python script contains classes for describing the contents of a CAN
database file. Given a .dbc file, it will parse it into objects in memory
and generate a C header file for use by the Network Manager embedded
firmware application for packaging and unpackaging data in CAN data frames.
Before editing, please read the EVT Wiki page describing the objects in a
CAN Database, located here:
https://wiki.rit.edu/display/EVT/CAN+Database
"""
import re
__regex_pattern__ = re.compile(r""" SG_ (?P<name>.*) : (?P<start_bit>[0-9]{1,2})\|(?P<length>[0-9]{1,2})@(?P<format>[0-1])(?P<type>[+-]) \((?P<factor>.*),(?P<offset>.*)\) \[(?P<min>.*)\|(?P<max>.*)\] "(?P<unit>.*)"(\s{1,2})(?P<rx_nodes>.*)""")
class CANDatabase:
"""
Object to hold all CAN messages in a network as defined by the DBC file.
"""
# Private Properties
_name = ""
_dbcPath = ""
_comment = ""
_messages = list()
_txNodes = list()
_extended = False
_attributes = list()
_iter_index = 0
def __init__(self, dbc_path):
"""
Constructor for the CAN Database.
Arguments:
- dbcPath: The file path to .dbc file.
"""
self._dbcPath = dbc_path
def __iter__(self):
"""
Defined to make the object iterable.
"""
return self
def __next__(self):
"""
Get the next iterable in the CANMessage list.
"""
if self._iter_index == len(self._messages):
self._iter_index = 0
raise StopIteration
self._iter_index += 1
return self._messages[self._iter_index-1]
def Load(self):
"""
Opens the DBC file and parses its contents.
"""
try:
file = open(self._dbcPath)
except OSError:
print("Invalid file path specified.")
print(self._dbcPath)
return
building_message = False
can_msg = None
line_number = 0
for line in file:
line = line.rstrip('\n')
line_number += 1 # keep track of the line number for error reporting
if line.startswith("BU_:"):
self._parseTransmittingNodes(line)
elif line.startswith("BO_"):
can_msg = self._parseMessageHeader(line)
building_message = True
elif line.startswith(" SG_") and building_message:
can_msg.AddSignal(self._parseSignalEntry(line))
elif line == "":
if building_message:
building_message = False
self._messages += [can_msg]
can_msg.UpdateSubscribers()
can_msg = None
elif line.startswith("VAL_"):
val_components = valueLineSplit(line)
new_value_name = val_components[2]
new_value_canID = int(val_components[1], 16)
# Tuple: (Name, CAN ID, Item Pairs)
new_value = (new_value_name, new_value_canID, list())
pairs = val_components[3:]
for i in range(0, len(pairs), 2):
try:
# add item value/name pairs to list in new_value tuple
item_value = int(pairs[i])
item_name = pairs[i+1]
new_value[2].append((item_value, item_name))
except IndexError:
print("Invalid value: " + new_value_name +
". Found on line " + str(line_number) + '.')
return None
for message in self:
if message.CANID() == new_value[1]:
message.AddValue(new_value)
break
# parse attributes
elif line.startswith("BA_DEF_ BO_"):
components = line.split(' ')
# warning: indices are one higher than they appear to be because of double space in line
attr_name = components[3].strip('"')
attr_type = components[4]
attr_min = components[5]
attr_max = components[6].rstrip(';')
new_attr = (attr_name, attr_type, attr_min, attr_max)
self._attributes.append(new_attr)
elif line.startswith("BA_ "):
components = line.split(' ')
attr_name = components[1].strip('"')
attr_msgID = int(components[3])
attr_val = components[4].rstrip(';')
new_attr = (attr_name, attr_val)
for message in self:
if message.CANID() == attr_msgID:
message.AddAttribute(new_attr)
break
elif line.startswith("CM_"):
components = line.split(' ')
if len(components) <= 2:
break
for message in self:
if message.CANID() == int(components[2]):
for signal in message:
if signal.Name() == components[3]:
comment_str = ''
for each in components[4:]:
comment_str += each
signal.AddComment(comment_str)
break
break
self._iter_index = 0
return self
def Name(self):
"""
Gets the CAN Database's name.
"""
return self._name
def Messages(self):
"""
Gets the list of CANMessage objects.
"""
return self._messages
def _parseTransmittingNodes(self, line):
"""
Takes a string and parses the name of transmitting nodes in the CAN bus
from it.
"""
items = line.split(' ')
for each in items:
if each == "BU_:":
pass
else:
self._txNodes += [each]
return
def _parseMessageHeader(self, line):
"""
Creates a signal-less CANMessage object from the header line.
"""
items = line.split(' ')
msg_id = int(items[1])
msg_name = items[2].rstrip(':')
msg_dlc = int(items[3])
msg_tx = items[4].rstrip('\n')
return CANMessage(msg_id, msg_name, msg_dlc, msg_tx)
def _parseSignalEntry(self, line):
"""
Creates a CANSignal object from DBC file information.
The Regex used is compiled once in order to save time for the numerous
signals being parsed.
"""
result = __regex_pattern__.match(line)
name = result.group('name')
start_bit = int(result.group('start_bit'))
length = int(result.group('length'))
sig_format = int(result.group('format'))
sig_type = result.group('type')
factor = int(result.group('factor'))
offset = int(result.group('offset'))
minimum = int(result.group('min'))
maximum = int(result.group('max'))
unit = result.group('unit')
rx_nodes = result.group('rx_nodes').split(',')
return CANSignal(name, sig_type, sig_format, start_bit, length, offset,
factor, minimum, maximum, unit, rx_nodes)
class CANMessage:
"""
Contains information on a message's ID, length in bytes, transmitting node,
and the signals it contains.
"""
def __init__(self, msg_id, msg_name, msg_dlc, msg_tx):
"""
Constructor.
"""
self._canID = msg_id
self._name = msg_name
self._dlc = msg_dlc
self._txNode = msg_tx
self._idType = None
self._comment = ""
self._signals = list()
self._attributes = list()
self._iter_index = 0
self._subscribers = list()
def __iter__(self):
"""
Defined to make the object iterable.
"""
self._iter_index = 0
return self
def __next__(self):
"""
Defines the next CANSignal object to be returned in an iteration.
"""
if self._iter_index == len(self._signals):
self._iter_index = 0
raise StopIteration
self._iter_index += 1
return self._signals[self._iter_index-1]
def AddSignal(self, signal):
"""
Takes a CANSignal object and adds it to the list of signals.
"""
self._signals += [signal]
return self
def Signals(self):
"""
Gets the signals in a CANMessage object.
"""
return self._signals
def SetComment(self, comment_str):
"""
Sets the Comment property for the CANMessage.
"""
self._comment = comment_str
return self
def CANID(self):
"""
Gets the message's CAN ID.
"""
return self._canID
def AddValue(self, value_tuple):
"""
Adds a enumerated value mapping to the appropriate signal.
"""
for signal in self:
if signal.Name() == value_tuple[0]:
signal.SetValues(value_tuple[2])
break
return self
def AddAttribute(self, attr_tuple):
"""
Adds an attribute to the message.
"""
self._attributes.append(attr_tuple)
return self
def Attributes(self):
return self._attributes
def Name(self):
return self._name
def TransmittingNode(self):
return self._txNode
def DLC(self):
return self._dlc
def UpdateSubscribers(self):
"""
Iterates through signals in the message to note all of the receiving
nodes subscribed to the message.
"""
for signal in self:
nodes = signal.RxNodes()
for each in nodes:
if each not in self._subscribers:
self._subscribers += [each]
return self
class CANSignal:
"""
Contains information describing a signal in a CAN message.
"""
def __init__(self, name, sigtype, sigformat, startbit, length, offset, factor,
minVal, maxVal, unit, rx_nodes):
"""
Constructor.
"""
self._name = name
self._type = sigtype
self._format = sigformat
self._startbit = startbit
self._length = length
self._offset = offset
self._factor = factor
self._minVal = minVal
self._maxVal = maxVal
self._units = unit
self._values = list()
self._comment = ""
self._rx_nodes = rx_nodes
def __lt__(self, other):
return self._startbit < other._startbit
def Name(self):
"""
Gets the name of the CANSignal.
"""
return self._name
def SetValues(self, values_lst):
"""
Sets the enumerated value map for the signal's data.
"""
self._values = values_lst
return self
def Length(self):
return self._length
def SignType(self):
return self._type
def AddComment(self, cm_str):
self._comment = cm_str
def ReadComment(self):
return self._comment
def RxNodes(self):
return self._rx_nodes
def valueLineSplit(line):
"""
Custom split function for splitting up the components of a value line.
Could not use normal String.split(' ') due to spaces in some of the value
name strings.
"""
components = list()
part = ""
in_quotes = False
for ch in line:
if ch == ' ' and not in_quotes:
components.append(part)
part = ""
elif ch == '"' and not in_quotes:
in_quotes = True
elif ch == '"' and in_quotes:
in_quotes = False
else:
part += ch
return components
def main():
"""
Opens a DBC file and parses it into a CANDatabase object and uses the
information to generate a C header file for the Network Manager
application.
"""
file = "EVT_CAN.dbc"
candb = CANDatabase(file)
candb.Load()
input()
if __name__ == "__main__":
main()
解析了DBC能做什么
我有一些想法:
- 通过解析dbc自动生成CAPL Simulation代码
- 汇总dbc信息,自动生成测试脚本
- 或者大家有什么想法请在下方评论留言