2. 诊断响应接收与解析

任务目标

编写CAPL程序,通过CANoe的Diagnostic Layer接收ECU返回的诊断响应报文,并从中解析出关键数据——以读取DTC状态为例,提取状态字节并判断当前是否存在故障。

实现原理

诊断请求通过DiagSendRequest发送,响应由事件函数DiagResponse接收。响应数据以参数形式存储,通过DiagGetComplexParameter按字节偏移提取原始数据,再按UDS协议解析DTC状态字节(如0x19 02服务返回的DTC状态掩码)。

关键完整代码

/* 诊断响应接收与解析 - DTC状态提取 */
includes
{
}

variables
{
  diagRequest reqReadDTC;
  diagResponse respDTC;
  byte dtcStatusMask;
  char msgStr[100];
}

on start
{
  // 创建读取DTC信息的请求 (UDS 0x19 0x02)
  reqReadDTC = DiagCreateRequest("ReadDTCInformation");
  DiagSetParameter(reqReadDTC, "subFunction", 0x02); // 通过状态掩码读取
  DiagSendRequest(reqReadDTC);
}

// 诊断响应事件处理
on diagResponse respDTC
{
  byte statusByte;
  word dtcHighByte, dtcLowByte;
  dword dtcCode;
  int i;
  int numDTCs;
  char dtcStr[20];

  // 检查是否为期望的响应
  if (DiagGetObjectID(respDTC) != DiagGetObjectID(reqReadDTC))
    return;

  // 检查响应是否包含肯定响应
  if (DiagGetResponseCode(respDTC) == 0x00) // 0x00表示肯定响应
  {
    // 获取DTC数量(从响应参数中读取)
    numDTCs = DiagGetComplexParameter(respDTC, "DTCStatusAvailabilityMask", 0);
    write("DTC状态可用性掩码: 0x%02X", numDTCs);

    // 遍历所有DTC(最多解析前5个,避免过度处理)
    for (i = 0; i < 5; i++)
    {
      // 尝试读取第i个DTC的高字节
      if (DiagGetComplexParameter(respDTC, "DTCAndStatusRecord", i, "DTC", 0) == 0)
        break; // 没有更多DTC

      // 提取DTC码(3字节)
      dtcHighByte = DiagGetComplexParameter(respDTC, "DTCAndStatusRecord", i, "DTC", 0);
      dtcLowByte = DiagGetComplexParameter(respDTC, "DTCAndStatusRecord", i, "DTC", 1);
      dtcCode = (dtcHighByte << 16) | (dtcLowByte << 8) | 
                DiagGetComplexParameter(respDTC, "DTCAndStatusRecord", i, "DTC", 2);

      // 提取状态字节
      statusByte = DiagGetComplexParameter(respDTC, "DTCAndStatusRecord", i, "statusOfDTC");

      // 格式化DTC码(如 P0100 -> 0x0100)
      snprintf(dtcStr, elcount(dtcStr), "0x%06X", dtcCode);
      write("DTC[%d]: %s, 状态=0x%02X", i, dtcStr, statusByte);

      // 判断故障状态(bit0 = testFailed)
      if (statusByte & 0x01)
        write("  -> 当前存在故障");
      else
        write("  -> 当前无故障");
    }
  }
  else
  {
    write("诊断响应错误,响应码: 0x%02X", DiagGetResponseCode(respDTC));
  }
}

on diagError
{
  write("诊断通信错误");
}

运行与验证

环境要求:CANoe 11.0及以上版本,需配置诊断功能(Diagnostic/ISO TP),并加载ECU仿真或真实节点。

  1. 在CANoe中创建CAPL测试节点,将上述代码粘贴到节点的CAPL Browser中。
  2. 在Diagnostic/ISO TP配置中,确保已定义名为ReadDTCInformation的诊断请求对象,且其响应PDU包含DTCAndStatusRecord结构(含DTC三字节和statusOfDTC字段)。
  3. 编译并运行仿真。触发on start自动发送读取DTC请求。
  4. 在Write窗口观察输出。

通过条件:若ECU返回肯定响应,Write窗口应打印DTC状态可用性掩码及每个DTC的编号、状态字节,并正确显示“当前存在故障”或“当前无故障”。若ECU无DTC,则循环正常退出,不报错。

关键点说明

  • DiagGetComplexParameter的索引参数从0开始,需根据实际诊断数据库(CDD/ODX)中的参数路径调整。
  • DTC状态字节bit0(testFailed)为1表示当前故障激活,实际项目中可按需解析其他bit位。
  • 若响应中DTC数量超过5个,可修改循环上限或使用DiagGetComplexParameter返回值动态判断。