1 Working with Databases
CANalyzer支持使用符号数据库。这些数据库除其他功能外,还包含消息标识符与符号消息名称之间的对应关系,以及以符号形式描述消息中数据段的信号。
在CANdb++编辑器的帮助下,可以创建和编辑数据库。在数据库选择对话框("分析与仿真"功能区选项卡"数据库配置")中,您可以将一个或多个数据库分配给CANalyzer。之后,您可以在CAPL模型以及测量配置的功能块和测量窗口中使用这些符号参数,而不是标识符和数据字节。
您可以通过从符号资源管理器/MOST符号资源管理器中拖放来输入符号消息名称或信号名称。
您可以在"文件"功能区选项卡|"概览"中找到已分配数据库的概览。
Example:
如果你将一个数据库分配给CANalyzer,且该数据库中定义了标识符为123的"EngineData"消息,那么在"跟踪窗口"中,例如,在符号模式下,你将获得"EngineData"的明文,而不是标识符123。
在数据库中,您还可以定义信号,即消息中数据段的符号描述。除了数据段的条目外,信号定义还包括机器格式(摩托罗拉/英特尔)、符号处理、转换公式和测量物理单位等特性。这使得可以在数据窗口中直接显示物理参数,例如:
Speed = 810.4 rpm
在功能块中,相关文本输入框旁边通常设有小按钮,用于输入符号消息名称或信号名称。当按钮被激活时,数据库中定义的所有符号列表会下拉显示,您可以通过点击选择一个或多个名称(在过滤器配置对话框中)。
2 Use of multiple databases (使用多个数据库)
有关定义数据库的详细信息,请参阅CANdb++文档。
对于大型系统,将消息和信号的描述分布到多个部分数据库中可能是合理的。此外,在使用多个总线时,为每个系统单独建立数据库进行描述也是合理的。
CANalyzer支持同时使用多个数据库。您可通过菜单路径"文件|分配数据库"(File ribbon tab|Overview)来配置需分配给CANalyzer的数据库。
您可以在"文件"功能区选项卡的"概览"中查看已分配数据库的总览。
之后,您可以在所有功能块和CAPL中为所有数据库的消息和信号使用符号名称。为此,请在相应的输入框中输入符号名称。您将在信号选择对话框中找到所有符号名称的列表。通过激活位于相应输入框旁边的按钮即可打开该列表。然后您可以从列表中选择所需的符号名称。
若使用多个数据库,除首个数据库外,后续数据库中的消息均需附加数据库名称作为限定。但此类限定名仅用于消除歧义。只要符号名在所有数据库中保持唯一性,即可省略函数块中所有符号名的限定,以及在编辑CAPL程序时的限定操作。
3、Working with Symbolic Identifiers (使用符号标识符)
除了消息、信号和环境变量的符号名称外,您还可以为信号和整数环境变量值使用符号名称。为此,您需要在数据库编辑器中为相应的信号或整数环境变量分配符号名称。
注意:
请注意使用符号标识符时的C命名约定。
在CAPL中,您可以通过在特定信号或环境变量名称前加上两个冒号(::)来使用这些名称而不是值。因此,让我们假设您在数据库LIGHT 中定义了消息LightData 的信号LightStatus的值,如下所示。DBC:
| Value | Name |
|---|---|
| 1 | Red |
| 2 | Yellow |
| 3 | Green |
| 然后,为了在总线上发送具有信号LightStatus 的符号值Red(=1)的消息LightData,您需要在CAPL中写入以下内容: |
cpp
message LightData msg;
msg.LightStatus = LightData.LightStatus::Red;
output(msg);
要访问符号值描述,请使用以下命令:
cpp
on message LightData
{
if(this.LightStatus == LightData.LightStatus::Red)
write("LightData.LightStatus = Red");
类似地,如果您在数据库中定义了Integer类型的环境变量的符号值,您也可以使用它们。
4 Symbolic Database Access (符号数据库访问)
CAPL允许在建模库(DLL文件)中使用符号访问数据库对象,例如TestServiceLibrary。
数据库对象用以下语法标识
- 网络接入:
dbNetwork::DBName - 节点访问:
\[dbNetwork::DBName::]dbNode::NodeName - 消息访问:
\[dbNetwork::DBName::]\[dbNode::NodeName::]dbMsg::MessageName - 信号接入:
\[dbNetwork::DBName::]\[dbNode::NodeName::]\[dbMsg::MessageName::]dbSig::SignalName
如果将信号分配给特定消息,则需要指定消息名称。
用于标识对象的名称均由限定符"::"分隔。
关键词
- dbNetwork
- dbNode
- dbMsg
- dbSig
是可选的,只有在节点和消息具有相同名称的情况下才需要。
除了节点名称,您还可以使用关键字thisNode。这里指的是在网络节点的配置对话框中分配给节点的节点名称。
Example:
测试服务库的使用:
cpp
mSigCheckId = ChkStart_MsgSignalValueRangeViolation(
MsgValue::SigValue, // Signal to supervise
lSigMinValue, // Minimum allowed value
lSigMaxValue, // Maximum allowed value
"SigCallback"); // CAPL callback for violation notification
5 Access to Database Attributes (访问数据库属性)
如果在指定的数据库中为某些对象定义了属性,则可以在CAPL中访问这些属性的值。
在CAPL中,您可以通过在数据库对象和属性名称之间写入句点(.)来选择数据库对象的属性值。
考虑以下CAPL代码摘录:
cpp
float rpm;
int time, n;
char buff[100];
...
time = enginedata.cycle_time;
n = ABS.node_number;
rpm = enginedata.speed.max_rpm;
strncpy(buff,motordat.db_name,elcount(buff)-1);
在这个例子中,读取了以下值:消息属性cycle_time(对象:enginedata,类型INT)、信号属性max_rpm(对象:消息enginedata的信号速度,类型FLOAT)、网络节点属性node_number(对象:ABS,类型INT-)和数据库属性db_name(对象:数据库motordat,类型STRING)的值。
枚举类型的属性值在CAPL中被视为字符串。因此,随着
cpp
strncpy(buff,enginedata.type,elcount(buff)-1);
您可以将ENUM消息属性的值复制到缓冲区缓冲区。请注意,所有属性都有相同的命名空间。因此,例如,不可能同时使用同名的数据库属性和消息属性。
备注
如果在CAPL中读取对象的属性值,而没有在数据库中明确定义该对象的值,则可以获得在数据库编辑器中为每个属性的定义输入的默认值。
6 Symbolic Access to Message Attributes (对消息属性的符号访问)
您可以象征性地访问can和LIN帧、参数组(PG)以及FlexRay帧的特定属性。
这些属性是:
- ID
- DLC
- Transmitter
- Attributes defined the database of these objects
以下是图片内容的翻译及表格形式输出:

在这里,访问可以是静态的或动态的(见下文))
*: FR_SlotID, FR_Cycle, Type, FR_PayloadLength, FR_Flags, FR_HeaderCRC, FR_Segment, FR_Status, DIR, Simulated
下表给出了概述。
6.1 静态访问
静态访问是对已知对象的访问。因此,可以直接或通过使用局部变量进行访问。
Example
- Static Access
cpp
int myID;
myID = EngineData.ID
message msg EngineData;
myID = msg.ID
- 对消息ID进行直接静态访问
cpp
void foo (int id)
{
if (id == EngineData.id)
{
// do something
}
}
- 对数据库属性的直接静态访问
cpp
void foo ()
{
int cycleTime;
cycleTime = EngineData.GenMsgCycleTime;
}
See also: Access to Database Attributes
- 使用变量进行静态访问
cpp
message EngineData msg;
void foo( int id)
{
if (id == msg.id)
{
// do something
}
}
6.2 动态访问
动态访问是在不知道对象名称的情况下访问属性。这在使用通配符选择器的事件过程中很常见,例如在message*上。
Example
动态访问:
cpp
on message *
{
myID = this.ID;
}
动态访问CAN消息的数据链路连接器
cpp
on message *
{
int currentDLC;
currentDLC = this.DLC;
}
or
void foo( message * msg)
{
if (msg.id == 0x100)
{
// do something
}
}
6.3 特性
备注:
Transmitter属性以及数据库属性只能通过DBLookup动态读取。
Example:
cpp
on message *
{
int myAttributeValue;
myAttributeValue = DBLookup(this).MyAttribute;
write(this.Transmitter); // compiler error
write(DBLookup(this).Transmitter); // OK
}
使用DBLookup以下方法
cpp
on message EngineData
{
int myDLC;
myDLC = DBLookup(this).DLC;
}
可能无法获得相同的结果
cpp
myDLC = EngineData.DLC;
原因:第一个命令返回存储在数据库中的DLC,而第二个命令返回特定消息的当前值。它们可能不同,因为DLC可以更改,例如在CAPL程序中。
7 Access to all Messages of a Node (访问节点的所有消息)
对于某些应用程序,了解特定节点发送或接收哪些消息是有用的。您可以通过特别预定义的字段访问特定节点的所有消息的ID,特别是迭代这些字段的内容。
以下字段可用:
- Nodename.Tx::包含节点发送的所有消息
- Nodename.Rx:包含节点接收到的所有消息
- Nodename.ALL:包含节点发送和接收的所有消息
如果多个数据库中包含同名节点,则可以用数据库名称限定它:DBName::Nodename.Tx。
您可以像往常一样使用elcount指定字段的大小。
函数概要:eCount
| 项目 | 描述 |
|---|---|
| 函数语法 | long eCount(...) // 当与作为函数参数的数组一起使用时 dword eCount(...) // 在所有其他情况下 |
| 功能说明 | 确定数组的元素个数。 |
| 注意 | 当使用关联字段调用时,eCount 始终返回 1;该函数仅用于非关联字段(数组)。 |
| 参数 | 任意类型的数组。 |
| 返回值 | 元素个数。 |
| 起始版本 | 所有版本 |
| 示例 | void bsp(int ar[]) { int i; for(i=0; i < eCount(ar); i++) ... } void bsp2(byte ar[]) { int i, j; for(j=0; j < eCount(ar); j++) for(i=0; i <= eCount(ar[j]); i++) ... } |
| 限制到 | --- |
| 分析分支支持 | --- |
| 发送分支支持 | --- |
cpp
Example
// checks for all messages of a Node that the DLC is correct.
// summarizes DLC info for the messages in a table at measurement end.
// uses DB lookup only before or after measurement
variables
{
// max DLC, min DLC, counter, channel; key is always the id
int gFrameMaxDLC[long];
int gFrameMinDLC[long];
int gFrameDefaultDLC[long];
dword gFrameCounter[long];
dword gFrameIncorrectCounter[long];
}
on preStart
{
dword i;
message * m;
m.can = 2;
// initialize DLC maps with the default DLC defined in the DB
for (i = 0; i < elcount(PowerTrain::Gateway.TX); ++i)
{
m.id = PowerTrain::Gateway.TX[i];
gFrameMaxDLC[m.id] = gFrameMinDLC[m.id] = gFrameDefaultDLC[m.id]
= DBLookup(m).DLC;
}
}
on message *
{
// is the frame one of those transmitted by the node?
if (gFrameDefaultDLC.containsKey(this.id))
{
gFrameCounter[this.id]++;
// check if the DLC is correct; if not, remember the minimum
// or maximum DLC of the received frames
if (this.dlc != gFrameDefaultDLC[this.id])
gFrameIncorrectCounter[this.id]++;
if (this.dlc < gFrameMinDLC[this.id])
gFrameMinDLC[this.id] = this.dlc;
else if (this.dlc > gFrameMaxDLC[this.id])
gFrameMaxDLC[this.id] = this.dlc;
} // else: another node, not interesting
output(this); // use only if the node is in the Measurement Setup
}
on stopMeasurement
{
message * m;
m.can = 2;
for (long currentId : gFrameDefaultDLC)
{
m.id = currentId;
write ("Frame %s[%d]:", DBLookup(m).Name, currentId);
write (" Received count: %d", gFrameCounter[currentId]);
write (" Incorrect count: %d", gFrameIncorrectCounter[currentId]);
write (" DLC as in DB: %d", DBLookup(m).DLC);
write (" Actual min DLC: %d", gFrameMinDLC[currentId]);
write (" Actual max DLC: %d", gFrameMaxDLC[currentId]);
}
}
备注:
预定义的消息字段与旧版本的CANalyzer不兼容。因此,从7.1版本开始,它只能用于CANalyzer的CAPL程序。
8 Symbolic Signal Access (符号信号接入)
使用项目数据库时,可以通过输入单个信号的名称来访问它们。编译器承担确定位偏移和位长度的任务,以及转换机器格式的任务(Intel <-> Motorola)。这些值以原始格式解释。
8.1物理值计算
如果要使用物理解释,则必须附加运算符phys。然后,编译器根据数据库中指示的转换公式(因子、偏移量)缩放该值。您可以使用can数据库中的值表为信号值分配符号描述。
符号信号接入示例
如果您在数据库中用信号Rpm定义了消息EngineData,您将写:
cpp
message EngineData m;
char errSymbol[32];
...
m.Rpm = 100;
m.Rpm.phys = 123.5; // Use of conversion formula
m.Rpm = EngineData.Rpm::Error // Use a symbolic value description
if(m.Rpm == EngineData.Rpm::Error) // Accessing the symbolic
write("EngineData.Rpm = Error"); //description of a signal value
在第一行中,原始值100被分配给您在数据库中分配给信号Rpm的数据位。在第二行中,利用数据库中分配的转换公式来计算比特值。在第三行中,使用了值的符号描述。符号值描述将使用信号的值表转换为原始值。最后一行显示了对符号值描述的访问。
注释:
确保信号值始终离散保存。如果指定了物理值,则缩放后将保存最接近的离散原始值。如果随后读出信号,则结果不一定与初始值一致。
8.2数据选择器
使用数据选择器,您还可以访问信号的单个字节。您可以将整数CAPL数据类型(long、dword、int、byte等)用作数据选择器,与访问消息字节的方式相同。作为参数,传输第一个选定字节的索引。
cpp
Example
message MsgWithLargeSignal msg;
msg.LargeSignal.byte(0) = 0x1;
msg.LargeSignal.byte(1) = 0x2;
msg.LargeSignal.word(2) = 0x3;
注释:
数据选择器自8.0 SP4版本起可用,不能与选择器.phys组合使用。
9 Resolution of Ambiguities(歧义的解决)
使用多个数据库时,使用符号名称时可能会出现歧义,这些歧义必须由程序解决。一方面,程序通过两个CAN控制器之一注册的来自总线的消息在两个数据库中可以具有不同的符号名称。另一方面,在某些情况下,用户可能希望定义在不同数据库中具有相同名称的符号消息。
第一种类型的歧义通过接收消息的通道上数据库的顺序来解决。您可以在数据库管理窗口的CANalyzer中更改顺序。
为了解决配置测量窗口和功能块时的名称冲突,还使用了数据库列表的搜索序列。但是,您还可以通过限定符号名称来解决此类歧义。
9.1 模糊名称的解析
9.1.1 CAPL中的隐式优先级解析
- 消息对象:message, frPDU, frFrame, PDU
- 事件处理程序(处理函数):on message, on frPDU, on PDU
对于消息对象,有以下限定选项可用。
以下是图片内容的中文翻译及表格形式输出,便于复制到 Markdown 文档中使用。
1. 报文(message)
限定符数量:
| 1 个限定符 | 2 个限定符 | 3 个限定符 | 4 个限定符 |
|---|---|---|---|
| message | 优先级1:database::message | 优先级1:network::database::message | network::database::node::message |
| 优先级2:network::message | 优先级2:database::node::message | network::database::node::message | |
| 优先级3:node::message | 优先级3:network::node::message | network::database::node::message |
2. frFrame、frPDU
frPDU 的用法与 frFrame 相同
限定符数量:
| 1 个限定符 | 2 个限定符 | 3 个限定符 | 4 个限定符 |
|---|---|---|---|
| frFrame | 优先级1:database::frFrame | 优先级1:database::node::frFrame | network::database::node::frFrame |
| 优先级2:node::frFrame | 优先级2:network::database::frFrame | network::database::node::frFrame | |
| 优先级3:network::frFrame | 优先级3:network::node::frFrame | network::database::node::frFrame |
3. PDU(AUTOSAR)
当使用 PDU 名称(而非 hdr-id)时,适用相同的规则/优先级顺序。
限定符数量:
| 1 个限定符 | 2 个限定符 | 3 个限定符 | 4 个限定符 |
|---|---|---|---|
| (优先级1) | 优先级1:channel/network::<hdr-id> | channel/network::node::<\hdr-id> | --- |
| 优先级2:node::<hdr-id> | channel/network::node::<\hdr-id> | --- |
9.1.2 CAPL中通过精确类型规范实现的显式解析
使用此程序时,无需搜索优先级。该程序自CANalyzer 10.0 SP2版本起可用。
以下类型限定符可用作前缀,为名称的一部分设置特定类型:
- message: dbMsg::
- node: dbNode::
- database: dbNetwork::
- frFrame: dbFrFrame::
- frPDU: dbFrPDU::
- pdu: dbPDU::
- hdr-id: dbHdrId::
Examples:
cpp
# Example for 2 qualifiers (search via priorities)
message MyNode::message myMessageVar
// Here all 3 priorities are evaluated for CAN messages to search for the specific message.
message dbNode::MyNode::message myMessageVar
// Here only the third priority is used for CAN messages.
使用未按照上述规则定义的类型限定符将导致错误:
cpp
Examples for errors
message dbNode::MyNode::MyMessage::MyXYZ myMessageVar
// The message is already completely specified with "MyMessage"
message dbNode::MyNode::dbNetwork::MyDatabase::MyMessage myMessageVar
// The database name must be set in front of the node name
message dbNode::MyNode::dbFrFrame::MyMessage myMessageVar
// Even if "MyMessage" is of "message" type the correct type qualifier must be set here thus "dbMsg" instead of "dbFrFrame"
10 Dynamic Search of Messages and Signals in Databases (数据库中消息和信号的动态搜索)
在测量过程中,您可以在数据库中搜索特定消息或特定信号的信息。如果编译时不知道确切的消息/信号,例如在接收信号作为参数的on message事件程序或测试函数中,则此操作十分必要。
要在指定的数据库中查找消息/信号,请编写DBLookup(变量),例如DBLookup(this)。您可以在此表达式后附加选择器,以获取有关消息/信号的信息。
您还可以使用该函数来查找类型为dbNode、dbMsg、dbFrFrame、dbFrPDU和dbPDU的对象的数据库属性。
以下是与消息类型相关的选择器:
表:选择器说明
| 选择器 | 描述 | 返回类型 | 消息类型 |
|---|---|---|---|
| Name | 消息名称 | char \[\] | message, pg, linFrame, frFrame, j1708message, FRPDU |
| DLC | 由 DLC 定义的数据字段大小(字节) | long | message, pg, linFrame, frFrame, j1708message, FRPDU, PDU |
| Transmitter | 消息的发送节点;如果发送节点数量为零或大于一,则返回空字符串 | char \[\] | message, pg, linFrame, frFrame, j1708message, FRPDU |
| AttributeName | 自定义数据库属性的名称 | 字符串属性为 char \[\],否则为 float | message, pg, linFrame, j1708message |
以下是与信号相关的选择器:
表:信号与服务信号选择器说明
| 选择器 | 描述 | 返回类型 | 信号 | 服务信号 |
|---|---|---|---|---|
| bitstart | 信号在消息中的起始位 | dword | ● | --- |
| bitcount | 信号的位数 | dword | ● | ● |
| offset | 原始值转换为物理值的偏移量 | float | ● | ● |
| factor | 原始值转换为物理值的系数 | float | ● | ● |
| unit | 信号的单位 | char \[\] | ● | --- |
| minimum | 信号的最小值 | float,若未定义则为 0 | ● | ● |
| maximum | 信号的最大值 | float,值为 信号大小 * 物理值(系数)+ 偏移量 | ● | ● |
| dbyte | 来自数据库的信号定义 | dbSig * | ● | --- |
| AttributeName | 自定义数据库属性的名称 | 字符串属性为 char \[\],否则为 float | ● | --- |
| DefaultValue | 信号的默认值,FlexRay 专用 | int64 | ● | --- |
| NotValidLowerLimit | 信号的无效下限值 | int64 | ● | --- |
| NotValidUpperLimit | 信号的无效上限值 | int64 | ● | --- |
| signalgroup | 所属信号组的名称;若信号不属于任何组,则返回空字符串 | char \[\] | ● | --- |
| txpdu | 发送节点的名称;有多个发送者时返回发送者列表 | char \[\] | ● | --- |
| frameID | 包含该信号的帧的 ID。注意:PDU 不支持此选择器。 | dword | ● | --- |
以下选择器可用于dbNode、dbMsg、dbFrFrame、dbFrPDU和dbPDU:
表:数据库对象选择器说明
| 选择器 | 描述 | 返回类型 |
|---|---|---|
| Name | 数据库对象的名称 | char \[\] |
| AttributeName | 自定义数据库属性的名称 | 字符串属性为 char \[\],否则为 float |
DBLookup(variable)表达式也可以在没有选择器的情况下使用,以检查数据库中是否存在特定对象。然后它会返回0或1。
如果消息在分配给同一通道的多个数据库中都有定义,由于搜索具有歧义,这些数据库将被视为未找到。无论如何,如果你知道要在哪个数据库中进行搜索,你可以将数据库名称放在前面作为附加参数:例如,DBLookup(PowerTrain, this)。
注释:
在数据库中动态搜索消息可能会耗费一些时间,尤其是在大型数据库中。因此,如果已知消息类型,则更倾向于静态访问属性。
cpp
Example
// checks for all messages of a specific node that the DLC is correct.
// summarizes DLC info for incorrect DLCs in a table at measurement end
variables
{
// system under test
char gNodeUnderTest[30] = "Gateway";
// max DLC, min DLC, counter, channel; key is always the id
int gFrameMaxDLC[long];
int gFrameMinDLC[long];
int gFrameCounter[long];
byte gFrameChannel[long];
}
on message *
{
// check all frames from one node for the correct dlc and count them
// is the frame defined in the database?
if (DBLookup(this))
{
// is the frame defined as Tx-Frame for the node under test?
// note: compare is case sensitive
if (strncmp(DBLookup(this).Transmitter, gNodeUnderTest, 30) == 0)
{
// check if the DLC is correct; if not, remember the minimum
// or maximum DLC of the received frames
if (this.dlc < DBLookup(this).DLC)
{
gFrameCounter[this.id]++;
gFrameChannel[this.id] = this.can;
if (!gFrameMinDLC.containsKey(this.id) || this.dlc < gFrameMinDLC[this.id])
gFrameMinDLC[this.id] = this.dlc;
}
else if (this.dlc > DBLookup(this).DLC)
{
gFrameCounter[this.id]++;
gFrameChannel[this.id] = this.can;
// note: the first access to a map-element returns 0, which is always smaller
if (this.dlc > gFrameMaxDLC[this.id]) gFrameMaxDLC[this.id] = this.dlc;
}
} // else: another node, not interesting
} // else: event is not defined in the database
output(this); // use only if the node is in the Measurement Setup
}
on stopMeasurement
{
message * m;
for (long currentId : gFrameCounter)
{
m.id = currentId;
m.can = gFrameChannel[currentId];
write ("Frame %s[%d]:", DBLookup(m).Name, currentId);
write (" Incorrect count: %d", gFrameCounter[currentId]);
write (" DLC as in DB: %d", DBLookup(m).DLC);
if (gFrameMinDLC.containsKey(currentId))
write (" Actual min DLC: %d", gFrameMinDLC[currentId]);
if (gFrameMaxDLC.containsKey(currentId))
write (" Actual max DLC: %d", gFrameMaxDLC[currentId]);
}
}
注释
- 消息的动态搜索与旧版本的CANalyzer不兼容。 因此,自7.1版本起,它仅可用于CANalyzer的CAPL程序。 自7.2
- SP3版本起,动态信号搜索功能仅限用于CANalyzer的CAPL程序。