CAPL 编程系列教程的第二十二期内容 (视频中口误称为第 20 期,继续讲解 CAPL 中的事件 (Events),本期重点是极其重要和常用的 on message 事件,即报文接收事件。
CAPL 编程系列教程 - 第二十二期:事件之 on message (报文接收)
一、 课程回顾与本期目标
- 回顾: 前面几期学习了 CAPL 的事件驱动模型、系统测量事件 (
on start等)。 - 本期目标: 详细学习
on message事件,理解其触发机制、语法,以及如何在事件处理程序中访问接收到的报文信息。
二、 on message 事件详解
- 触发时机:
- 当 CANoe/CANalyzer 等测量工具在总线上探测到(接收到) 一个特定的 CAN 报文时,每次接收到就会触发一次
on message事件。 - 如果一个报文周期性发送(如每 100ms 一次),那么对应的
on message事件处理程序也会以相同的频率被触发执行。
- 当 CANoe/CANalyzer 等测量工具在总线上探测到(接收到) 一个特定的 CAN 报文时,每次接收到就会触发一次
- 基本语法:
Code snippet
on message <MessageSpecifier> { // Code to execute when the specified message is received }
MessageSpecifier(报文指定符): 用于指定要响应该哪个或哪些报文。可以是:- 报文 ID:
on message 0x123 { ... }或on message 291 { ... }(十六进制或十进制 ID)。 - DBC 报文名称:
on message EngineState { ... }(前提是已加载包含该名称的 DBC 文件)。 - ID 范围:
on message 0x100-0x200 { ... }(使用连字符-指定一个 ID 范围,包含边界)。 - 通配符
*:on message * { ... }(响应所有接收到的报文)。
- 报文 ID:
this关键字 (核心):- 在
on message事件处理程序的代码块内部,关键字this是一个特殊的预定义变量。 this代表当前刚刚接收到的那一帧报文。this本身就是一个message类型的数据。- 可以通过
this加上点运算符 (.) 来访问当前接收到的报文的各种属性和数据。
- 在
三、 通过 this 访问报文信息
- 访问报文属性:
- ID:
this.id- 获取报文的 ID (返回值为十进制整数)。 - DLC:
this.dlc- 获取报文的数据长度码 (Data Length Code, 0-8 for CAN, 0-15 for CAN FD in some contexts)。 - 数据字节数:
this.dataLength- 获取报文数据域实际包含的字节数。对于标准 CAN,通常等于 DLC;对于 CAN FD,可能不同(DLC 值 9-15 映射到更大的字节数)。
- ID:
- 访问信号 (需加载 DBC):
- 语法:
this.SignalName或this.SignalName.phys - 如果 CANoe 工程加载了包含该报文信号定义的 DBC 文件,可以直接用
this点.信号名称来访问信号。 - 获取的是物理值,CAPL 会自动根据 DBC 进行解析(位提取、字节序处理、系数偏移量计算等)。
- 示例:
engineRpm = this.EngineSpeed; status = this.OnOff;
- 语法:
- 访问原始字节 (无需 DBC 或需要原始数据时):
- 按字节:
this.byte(index)- 获取指定索引index(0-N) 的字节值。 - 按字:
this.word(index)- 获取从字节索引index开始的两个字节组成的一个字 (word) 的值。需要注意字节序(CAPL 通常按定义的字节序处理,但获取原始 word 时可能需要手动处理或理解其表示方式)。 - 其他: 可能还有
.dword(),.qword()等。 - 用途: 在没有 DBC 文件,但知道信号在字节中的位置时,或者需要对原始字节数据进行位操作等底层处理时使用。
- 示例 (手动解析信号):
- 获取 OnOff 信号 (假设在 byte 0 的 bit 0):
status = this.byte(0) & 0x01;(使用位与操作提取最低位)。 - 获取 EngineSpeed 信号 (假设在 word 0 中,需要右移 1 位):
rawSpeed = this.word(0) >> 1;(需要根据具体位域和字节序进行精确计算)。
- 获取 OnOff 信号 (假设在 byte 0 的 bit 0):
- 按字节:
四、 示例与演示
- 准备工作: 为了演示
on message,需要有一个报文源。教程中创建了另一个 CAPL 节点 (EMS.can) 来模拟 ECU,周期性发送 ID 为 0x123 (EngineState) 的报文。 - 测试代码 (
capl学习-22.can):Code snippetvariables { /* ... */ } // 响应特定 ID 或名称的报文 on message EngineState { // 或 on message 0x123 或 on message 0x100-0x200 long id_dec; int dlc_val; int len_val; int engineRpm; int onOffStatus; byte b0, b1; word w0; id_dec = this.id; // 获取十进制 ID dlc_val = this.dlc; len_val = this.dataLength; write("收到报文: ID=0x%X (%d), DLC=%d, Length=%d", id_dec, id_dec, dlc_val, len_val); // 通过信号名访问 (需要 DBC) engineRpm = this.EngineSpeed; onOffStatus = this.OnOff; write(" 信号: EngineSpeed=%d rpm, OnOff=%d", engineRpm, onOffStatus); // 通过原始字节访问 (示例) b0 = this.byte(0); b1 = this.byte(1); w0 = this.word(0); write(" 原始字节: Byte0=0x%X, Byte1=0x%X, Word0=0x%hX", b0, b1, w0); // 注意:%hX 用于输出 word (通常16位) 的十六进制 // 手动解析示例 onOffStatus = this.byte(0) & 0x01; // 假设 OnOff 在 byte0, bit0 engineRpm = (this.word(0) >> 1); // 假设 EngineSpeed 在 word0, bit 1-15 (简化示例) write(" 手动解析: OnOff=%d, EngineSpeed (raw shifted)=%d", onOffStatus, engineRpm); write("--------------------------------"); } - 运行验证: 启动 CANoe 工程,观察 Write 窗口。由于
EMS.can节点周期性发送 0x123 报文,on message EngineState会被反复触发,每次触发都会打印出接收到的报文的 ID、DLC、字节数、通过信号名解析出的物理值、原始字节值以及手动解析(示例)的值。
五、 总结与后续
- 本期深入学习了 CAPL 中处理报文接收的核心事件
on message。 - 重点掌握了
on message的触发条件、多种指定报文的方式,以及如何使用this关键字访问接收报文的各种属性(ID, DLC, Length)和数据(通过信号名或原始字节/字)。 - 理解通过信号名访问的便捷性(依赖 DBC)与通过原始字节访问的灵活性(不依赖 DBC 但需手动解析)。
on message是编写需要响应总线通信的 CAPL 脚本(如监控、记录、仿真响应、测试验证等)的基础。- 后续将继续学习与信号相关的事件 (
on signal,on signal_update) 等。