2. DoIP节点发现与路由:车辆声明报文(VAN)、路由激活请求/响应、诊断电源模式、DoIP实体状态机

好,咱们进入第二章。这一章讲的是DoIP通信的“敲门砖”——节点发现和路由激活。说白了,就是你的诊断仪怎么找到车上的ECU,以及怎么跟它建立一条能说话的通道。

我个人习惯把这一章叫做“握手三部曲”。你想想看,两个设备要通信,总得先打个招呼,确认一下身份,再商量好怎么聊吧?DoIP也是这个道理。咱们一步步拆开看。

2.1 车辆声明报文(VAN)——ECU的“自我介绍”

车辆声明报文,英文叫Vehicle Announcement,简称VAN。这是DoIP实体(也就是车上的ECU)上线后干的第一件事。

它什么时候发?

  • ECU上电,DoIP协议栈初始化完成
  • 网络链路从断开变为连接(比如网线插上了)
  • 收到诊断仪的“车辆识别请求”(Vehicle Identification Request)

报文长什么样?

VAN报文是一个UDP广播报文,目标端口是UDP 13400,目标地址是255.255.255.255。它的载荷结构如下:

| 字节偏移 | 字段名              | 长度(字节) | 说明                          |
|----------|---------------------|------------|-------------------------------|
| 0        | 协议版本            | 1          | 当前DoIP版本,如0x02          |
| 1        | 反向协议版本        | 1          | 与协议版本相同,用于校验       |
| 2-3      | 载荷类型            | 2          | 0x0004表示车辆声明             |
| 4-7      | 载荷长度            | 4          | 后续数据的长度                 |
| 8-11     | VIN(车辆识别码)   | 4          | 实际VIN为17字节,这里存前4字节 |
| ...      | ...                 | ...        | ...                           |

嗯,这里要注意:VAN报文里携带的是完整的17字节VIN码、EID(逻辑地址)和GID(组地址)。诊断仪收到后,就能知道车上挂了哪些ECU。

我的经验: 我在项目中遇到过一个问题——某个ECU的VAN报文发得太快了,诊断仪还没准备好接收,结果丢包了。后来我们在ECU启动后加了500ms的延迟,问题就解决了。说白了,别一上来就抢着说话,等一等更稳。

2.2 路由激活请求/响应——建立诊断通道

找到ECU之后,下一步就是激活路由。这一步很关键,它决定了你能不能往ECU发诊断请求。

路由激活请求(Routing Activation Request)

诊断仪发一个TCP报文给ECU,目标端口是13400。报文里包含:

  • 源地址:诊断仪的逻辑地址(比如0x0E80)
  • 目标地址:目标ECU的逻辑地址(比如0x0001)
  • 激活类型:0x01表示“默认路由激活”,0x02表示“带证书的激活”

路由激活响应(Routing Activation Response)

ECU收到请求后,会检查激活类型是否支持。如果没问题,就返回一个响应:

| 字段              | 值示例     | 说明                          |
|-------------------|------------|-------------------------------|
| 协议版本          | 0x02       |                               |
| 反向协议版本      | 0x02       |                               |
| 载荷类型          | 0x0005     | 路由激活响应                  |
| 载荷长度          | 0x00000007 | 后续7字节                     |
| 客户端逻辑地址    | 0x0E80     | 诊断仪的地址                  |
| 激活类型          | 0x01       | 默认激活                      |
| 响应码            | 0x10       | 0x10=成功,0x11=拒绝          |
| 保留              | 0x00       |                               |
避坑指南: 我曾经遇到一个案例——诊断仪发了路由激活请求,ECU也回了成功,但后续的诊断请求就是没响应。查了半天,发现是ECU的TCP接收缓冲区太小,丢包了。所以,我建议你在做压力测试时,一定要关注TCP窗口大小和重传机制。

2.3 诊断电源模式——ECU的“清醒状态”

ECU不是随时都能响应诊断的。它有自己的电源模式,说白了就是“清醒”还是“睡觉”。

DoIP协议里定义了四种电源模式:

模式编号 模式名称 说明
0x00 Not Ready ECU刚上电,还没准备好
0x01 Ready ECU正常工作,可以响应诊断
0x02 Not Ready(休眠中) ECU处于低功耗模式,不响应诊断
0x03 Transition ECU正在切换模式,暂时不可用

你想想看,如果ECU在休眠状态,你发诊断请求过去,它根本不理你。所以,做压力测试前,一定要确认ECU处于Ready模式。

注意: 有些ECU在休眠模式下会主动发送“电源模式变化通知”报文。诊断仪收到后,应该暂停发送诊断请求,等ECU恢复Ready再继续。我曾经在测试中忽略了这一点,结果压测脚本一直报超时,浪费了半天时间。

2.4 DoIP实体状态机——ECU的“生命周期”

每个DoIP实体内部都有一个状态机,管理着它的生命周期。这个状态机有以下几个状态:

  • 初始化(Initialization):ECU上电,协议栈初始化
  • 未激活(Inactive):初始化完成,但还没收到路由激活请求
  • 已激活(Active):路由激活成功,可以收发诊断消息
  • 休眠(Sleep):ECU进入低功耗模式
  • 错误(Error):发生严重错误,需要复位

状态之间的转换条件,我列一下:

  1. 初始化完成后,自动进入未激活状态
  2. 收到有效的路由激活请求,进入已激活状态
  3. 长时间无活动(比如30秒),进入休眠状态
  4. 收到唤醒报文,从休眠回到未激活状态
  5. 发生协议错误,进入错误状态,然后自动复位

我个人习惯在压力测试脚本里,先读取ECU的当前状态,确认是Active再发诊断请求。这样可以避免很多不必要的超时。

小技巧: 你可以用UDS的0x22服务读取DID 0xF1C0,这个DID通常存储着DoIP实体的当前状态。我在做自动化测试时,会在每个测试用例开始前先读一下这个DID,确保ECU状态正确。

好了,这一章的内容就这些。节点发现、路由激活、电源模式、状态机——这四个概念是DoIP通信的基础。你把这些搞清楚了,后面的压力测试才能有的放矢。

下一章,咱们聊聊诊断消息的封装和传输,也就是怎么把UDS请求塞进DoIP报文里。到时候见。