CAPL 编程系列教程的第二十三期内容 ,继续讲解 CAPL 中的事件 (Events),本期重点是与信号 (Signal) 相关的两个事件:on signal 和 on signal_update,以及多种获取信号值的方法。
CAPL 编程系列教程 - 第二十三期:事件之 on signal 与 on signal_update
一、 课程回顾与本期目标
- 回顾: 前面学习了 CAPL 的事件驱动模型、系统测量事件、报文接收事件 (
on message)。 - 本期目标: 学习专门用于响应信号变化的两种事件:
on signal和on signal_update,理解它们的区别、用法,以及如何在事件处理程序或其他地方获取信号的值。 - 前提条件: 使用这两个事件必须在 CANoe 工程中加载了包含相应信号定义的 DBC 文件。如果没有 DBC 文件定义该信号,这两个事件将无意义。
二、 信号事件详解
-
on signal <SignalName>- 触发时机: 仅当指定的信号
<SignalName>在总线上出现,并且其值与上一次接收到的该信号的值发生变化时,才会触发一次。 - 逻辑: 如果信号周期性发送,但其值保持不变,则只有在第一次从无到有出现该信号时,或者后续某次值发生改变时才会触发。值不变的后续接收不会触发。
- 用途: 主要用于捕获和响应信号值的有效改变,执行与状态变化相关的逻辑。
- 语法:
Code snippet
on signal EngineSpeed { // 当 EngineSpeed 信号的值发生变化时执行 // ... 处理代码 ... }
- 触发时机: 仅当指定的信号
-
on signal_update <SignalName>- 触发时机: 只要指定的信号
<SignalName>在总线上被接收到(更新),无论其值是否发生变化,都会触发一次。 - 逻辑: 如果信号周期性发送(如 100ms 一次),那么这个事件处理程序也会以 100ms 的周期被触发执行。
- 用途: 当需要每次信号出现都执行某些操作时使用(例如,每次都记录该信号的出现,或基于每次出现做一些计算),即使值没有变化。相对
on signal用得可能较少。 - 语法:
Code snippet
on signal_update EngineSpeed { // 每当收到 EngineSpeed 信号时执行 // ... 处理代码 ... }
- 触发时机: 只要指定的信号
- 对比:
on signal关注变化,on signal_update关注每次出现/更新。
三、 在信号事件中获取信号值 (this 关键字)
this的含义: 在on signal或on signal_update事件处理程序的代码块内部,关键字this代表当前触发事件的那个信号的值。this的类型: 直接使用this时,它代表的是信号的物理值 (Physical Value),并且其数据类型始终是浮点数 (float或double),即使信号在 DBC 中定义为整数。- 获取物理值:
this: 直接使用this获取物理值 (浮点数)。this.physical(或this.phys): 使用.physical选择器明确获取物理值 (浮点数),效果与直接用this相同,但更清晰。
- 获取原始值:
this.raw: 使用.raw选择器获取信号的原始值 (Raw Value) (即根据位域提取出来,但未经过系数和偏移量转换的整数值),返回值类型也是浮点数 (虽然其值通常是整数)。
- 类型转换: 由于
this、this.physical、this.raw返回的都是浮点数,如果需要将其作为整数使用(例如用%d输出或进行整数运算),需要进行强制类型转换(int)。- 示例:
write("当前物理值: %d", (int)this);或write("当前原始值: %d", (int)this.raw);
- 示例:
- 物理量与原始量:
- 物理量是经过 DBC 中定义的系数 (Factor) 和偏移量 (Offset) 转换后的、具有实际物理意义的值(如 2000 rpm)。
- 原始量是信号在报文字节中直接对应的数值(通常是整数),未进行转换。
- 仅当信号的系数为 1 且偏移量为 0 时,物理量才等于原始量。
四、 在其他地方获取信号值的多种方法 (不局限于信号事件)
-
场景: 有时需要在非信号事件处理程序中(如
on key,on timer,on message或自定义函数)主动获取某个信号的当前值。此时不能使用this来代表信号值。 -
方法:
- 美元符号 (
$) + 信号名:- 语法:
$SignalName - 作用: 直接获取信号
<SignalName>的当前物理值。 - 类型: 返回值是浮点数。
- 选择器: 同样可以使用
.physical(效果同不加) 或.raw获取物理量或原始量(仍为浮点数)。$SignalName等同于$SignalName.physical$SignalName.raw获取原始值。
- 优点: 语法简洁。
- 注意: 信号名前必须加
$符号。
- 语法:
getSignal()函数:- 语法:
getSignal(SignalName)或getSignal("SignalName") - 作用: 获取信号
<SignalName>的当前物理值。 - 参数: 可以是信号的符号名(如
EngineSpeed)或信号名称的字符串(如"EngineSpeed")。 - 类型: 返回值是浮点数。
- 限制: 只能获取物理量。
- 语法:
getRawSignal()函数:- 语法:
getRawSignal(SignalName)或getRawSignal("SignalName") - 作用: 获取信号
<SignalName>的当前原始值。 - 参数: 同
getSignal()。 - 类型: 返回值是浮点数(即使原始值是整数)。
- 限制: 只能获取原始量。
- 语法:
- 美元符号 (
-
命名空间/冲突解决 (适用于方法 1 和方法 2/3): 如果信号名在不同报文或节点间存在冲突,可以在信号名前加上限定符,用双冒号
::分隔:NodeName::SignalName(节点限定)MessageName::SignalName(报文限定)NodeName::MessageName::SignalName(节点和报文限定)- (视频中还尝试了
BusName::...和DatabaseName::...但似乎当前版本不支持或语法有误) - 通常,直接使用信号名即可;若有冲突,优先考虑用报文名限定。
五、 示例与演示
- 教程使用上一期的仿真 ECU (
EMS.can) 周期性发送EngineState(ID 0x123) 报文,并在其中加入了随机逻辑,使得EngineSpeed信号的值在 500 和 800 之间随机变化。 - 在测试脚本 (
capl学习-23.can) 中:- 编写
on signal EngineSpeed事件,在其值变化时打印变化信息和当前值 (使用this)。 - 编写
on signal_update EngineSpeed事件,在每次收到信号时打印接收信息和当前值 (使用this)。 - 通过运行对比两个事件的输出频率,清晰地展示了它们的区别(
_update频繁触发,on signal仅在 500/800 切换时触发)。 - 演示了在
on signal事件中使用this,this.physical,this.raw获取物理量和原始量的区别(通过修改 DBC 中信号的系数和偏移量来体现差异)。 - 演示了使用
$EngineSpeed,$EngineSpeed.raw,getSignal(EngineSpeed),getRawSignal(EngineSpeed)这几种方式获取信号值,并验证其结果与this方式获取的一致性。
- 编写
六、 总结与后续
- 本期详细学习了 CAPL 中响应信号的两个核心事件
on signal(值变化时触发)和on signal_update(每次更新时触发)的区别与用法。 - 掌握了在信号事件中使用
this关键字获取信号物理值 (this或this.physical) 和原始值 (this.raw) 的方法,并注意其返回类型为浮点数。 - 学习了在任意代码位置主动获取信号值的其他常用方法:
$<SignalName>、getSignal()和getRawSignal()。 - 这些事件和函数是编写需要监控和响应特定信号变化的 CAPL 脚本的基础。