jidu_flasher 模块是集度 OTX 代理插件中刷写业务流程的顶层编排层,向上暴露一组 C 风格的 OTX_API 接口供 OTX 诊断脚本调用,向下聚合 ECUFlasherManager(刷写引擎)、CVehicleConfig(整车配置)和 DataCenter(数据中心)三大子系统,完成从 ECU 任务注册、硬件版本校验、安全常量绑定到并行下载执行与 FOTA 状态热跟踪的完整链路。
核心数据结构:刷写任务与状态枚举
模块以 EcuFlasherTask 结构体作为单个 ECU 刷写任务的内存画像。该结构体记录了刷写进度(progress)、优先级(priority)、FOTA 状态机当前值(status),以及三个秒级时间戳——下载开始(startTimeStamp)、下载结束(endTimeStamp)和安装完成(InstallTimeStamp),同时还缓存了 ecuName、ecuId、evName、logicalLink 等路由信息。全局 map<uint16_t, EcuFlasherTask> mapEcuFlasherTask 以 ecuId 为键聚合所有待刷写任务,贯穿整个刷写生命周期。
Sources: jidu_flasher.cpp
FOTA 状态枚举 EcuFotaStatus_t 定义了六种状态:Ecu_FotaStatus_Expect(待刷写,值为 0)、Ecu_FotaStatus_Executing(刷写中,值为 1)、Ecu_FotaStatus_Fail(刷写失败,值为 2)、Ecu_FotaStatus_Success(刷写成功,值为 3)、Ecu_FotaStatus_ResetStart(复位开始,值为 4)和 Ecu_FotaStatus_ResetEnd(复位结束,值为 5)。这与 jidu_macro.h 中定义的面向 OTX 脚本的外部状态枚举 JIDU_ECU_FOTA_STATUS_t(值 0-5 对应无结果、离线、硬件不匹配、升级失败、升级成功、选装件不需刷写)形成内外两层语义映射——内部枚举关注刷写执行流水线的微观状态,外部枚举面向诊断脚本的宏观结果判定。
Sources: jidu_flasher.cpp, jidu_macro.h
刷写包类型枚举 EcuFotaPackageType_t 区分六种文件类型:Data(普通数据,值 0)、SBL(Secondary Bootloader,值 1)、App(应用程序,值 2)、PBL(Primary Bootloader,值 3)、ESS 和 EXE(扩展类型,值 4/5)。在下游文件注册阶段,pthread_download 通过字符串匹配(SBL、APP、DATA、PBL、ESS、EXE 前缀)将 JD_VSP_FLASH_CONFIG_t 中的 strFileType 映射到此枚举,随后传递给 ECUFlasherManager::addECUFile。
Sources: jidu_flasher.cpp
任务注册:从 OTX 命名空间到刷写引擎的映射桥
OTX 脚本通过 jd_ecuflash_addEcu(const char* ecuName, const char* evName, const char* logicalLinkName, int priority) 注册刷写任务。该函数首先调用 CVehicleConfig::getEcuName_byOrderEcuName 将订单中的 ECU 名称(如 "ECM3")转换为整车配置包中的规范名称,然后在 vecListEcu 中匹配对应的 ecuId,最终构造 EcuFlasherTask 并插入 mapEcuFlasherTask。名称映射是必需的,因为订单系统和配置包对同一 ECU 可能使用不同命名。
Sources: jidu_flasher.cpp
jd_ecuflash_addEcu_byEcuId 提供了基于 ecuId 的替代注册路径。其匹配策略采用掩码比较 (ecuId & 0xFFF0) == (vecEcuConfig.at(i).nEcuId & 0xFFF0),而非精确相等,这是因为同一物理 ECU 的不同功能面(如 0x5051 BECM1_75 与 0x5052 BECM1_102)共享同一高 12 位基础地址,掩码匹配可容忍此类子地址差异。
Sources: jidu_flasher.cpp
下表比较两种注册路径的关键差异:
| 维度 | jd_ecuflash_addEcu | jd_ecuflash_addEcu_byEcuId |
|---|---|---|
| 输入标识 | 订单 ECU 名称字符串 | ECU ID 数值 |
| 名称转换 | 经 getEcuName_byOrderEcuName 映射 | 直接从 vecListEcu 取 strEcuName |
| ID 匹配策略 | 精确匹配 nEcuId | 掩码匹配 (id & 0xFFF0) |
| 适用场景 | 标准多 ECU 并行刷写 | 指定 ID 的针对性刷写(如桥文件刷写) |
硬件版本检查:前置条件验证的双重路径
jd_ecuflash_hwpnCheck 和 jd_ecuflash_hwpnCheck_byEcuId 构成刷写前的硬件版本前置校验。两者遵循相同的三段式逻辑:(1)从 CVehicleConfig 获取目标 ECU 的配置信息(JD_ECU_CONFIG_t);(2)若 vecPreconditionList 为空(无前置依赖),直接返回 true;(3)否则遍历前置条件列表,逐一比对车辆当前硬件版本号(HWPN)是否落在允许的硬件版本集合中。
Sources: jidu_flasher.cpp, jidu_flasher.cpp
硬件版本号的查询也遵循双重路径。首选从 DataCenter::m_vehicleInfo.mapEcuInfo(车辆实时扫描数据)中获取,若缺失则降级到 get_jd_orderInfo_ecuHwpn_byEcuName(从服务器订单中获取)。匹配算法采用子串匹配 oneHwpn.find(ecuHwpn) != string::npos,这意味着配置中的允许列表可以包含部分版本号前缀,为版本兼容性提供了一定的弹性。
flowchart TD
A["jd_ecuflash_hwpnCheck<br/>jd_ecuflash_hwpnCheck_byEcuId"] --> B["CVehicleConfig::getEcuConfig<br/>获取 ECU 配置"]
B --> C{"vecPreconditionList<br/>为空?"}
C -->|是| D["返回 true<br/>(无前置依赖)"]
C -->|否| E["遍历 vecPreconditionList"]
E --> F{"mapEcuInfo<br/>中有该 ECU?"}
F -->|是| G["从车辆实时数据<br/>取 ecuHwpn"]
F -->|否| H["从订单信息<br/>取 ecuHwpn"]
G --> I["逐项匹配 vecHwPn<br/>子串包含即命中"]
H --> I
I --> J{"命中?"}
J -->|是| K["返回 true"]
J -->|否| L["返回 false<br/>硬件版本不匹配"]订单一致性校验:HWPN 与 SWPN 双层比对
jd_ecuflash_orderCheck 和 jd_ecuflash_orderCheck_byEcuId 在刷写执行前进行订单级别的版本一致性核验。对于订单中的每个 ECU,依次执行:第一层(硬件)——比对车辆实时 HWPN 与订单 HWPN,不匹配则记录日志并返回 false;第二层(软件)——通过 CVehicleConfig::getEcuFileInfo 获取刷写文件列表,遍历每个文件的 strFileType 和 strVersion,与订单中的 vecEcuSwpnList 进行逐项比对。若两者在 packageType 匹配但版本不同,则判定为 SWPN 不匹配。
Sources: jidu_flasher.cpp
值得注意的是,HWPN 不匹配直接导致校验失败,但 SWPN 不匹配仅记录错误日志——这暗示 SWPN 不一致在某些场景下可被容忍(例如车辆当前版本与订单基线版本差异在可接受范围内时,上层脚本可能选择继续刷写)。
高压检查:ECU 功率平台判定
jd_ecuflash_checkHighVoltage 位于 jidu.cpp 而非 jidu_flasher.cpp,它通过查表 ecutable[JD_EUC_COUNT] 判断指定 ECU 是否属于高压平台。ecutable 中的 isHighPwr 字段(表中最后列,0 或 1)标记了 BECM、MGM、IEM、HVCM、ECM3 等高压域 ECU。
安全访问模式:PinCode 与换件刷写
jd_ecuflash_setSecurityAccessMode(int mode) 设置模块级静态变量 mEcurityAccessMode,控制刷写时的安全访问策略:模式 0(默认)优先使用从 DataCenter 获取的 ECU 安全常量 keySwdl 作为 PinCode;模式 1(换件刷写)则优先使用 FFFFFFFF 作为 PinCode,用于 ECU 更换后首次刷写场景。该模式值最终在 pthread_download 中通过 manager->addECU 的 securityAccessMode 参数传递给刷写引擎。
Sources: jidu_flasher.cpp, jidu_flasher.cpp
FOTA 状态热跟踪:DefaultEventHandler 事件驱动模型
DefaultEventHandler 是模块内部定义的关键子类,多重继承自 otx::flash::ECUFlasherEventHandler,覆盖了六个核心事件回调,全部以 std::lock_guard<std::mutex> lock(mItemLock) 保证线程安全:
| 事件回调 | 触发动机 | 对 mapEcuFlasherTask 的更新 |
|---|---|---|
onECUDownloadStart | ECU 开始下载 | status → Executing,记录 startTimeStamp |
onECUDownloadEnd | ECU 下载结束 | 根据 arg.getStatus() 设置 Success 或 Fail,记录 endTimeStamp |
onECUResetStart | ECU 复位开始 | status → ResetStart |
onECUResetEnd | ECU 复位结束 | status → ResetEnd |
onFileDownloadStart | 单个文件下载开始 | status → Executing,日志记录文件路径 |
onFileDownloadEnd | 单个文件下载结束 | 日志记录 |
onFileDownloadProgress | 文件下载进度更新 | 更新 progress 百分比,计算 remainder(剩余秒数),进度 100% 时记录 InstallTimeStamp |
Sources: jidu_flasher.cpp
在 onFileDownloadProgress 中,剩余时间 remainder 通过 sender->getRemainTime(arg.getEcu()) / 1000000 从微秒转换为秒。此外,当剩余时间超过一小时(> 1000000 * 60 * 60 微秒)时会输出 error 级别日志,作为一种异常超时检测机制。
Sources: jidu_flasher.cpp
FOTA 状态查询 API:进度、时间戳与统计
模块提供了一组 byEcuName / byEcuId 双形参风格的查询接口,供 OTX 脚本在刷写过程中实时获取状态:
jd_ecuflash_getProgress/_byEcuId:返回 0-100 的下载进度百分比,未注册返回 -1jd_ecuflash_getStatus/_byEcuId:返回EcuFotaStatus_t枚举值,未注册返回Ecu_FotaStatus_Failjd_ecuflash_getRemainder/_byEcuId:返回预估剩余秒数jd_ecuflash_getInstallRemainder/_byEcuId:基于InstallTimeStamp和传入的expectedTime计算安装剩余时间。算法为expectedTime - (timeNow - InstallTimeStamp),若已超时则返回 1(保底值,避免返回 0 或负数)jd_ecuflash_getStartTimestamp/_byEcuId和jd_ecuflash_getEndTimestamp/_byEcuId:返回秒级 Unix 时间戳jd_ecuflash_ecuCount(int status):遍历mapEcuFlasherTask,统计处于指定状态的 ECU 数量
Sources: jidu_flasher.cpp
刷写执行引擎:三种启动路径的统一架构
模块通过三个顶层入口函数暴露不同的刷写启动模式,三者共享相同的架构骨架(创建 ECUFlasherManager → 注册事件处理器 → 构造任务 → 启动独立线程 → detach),但在任务构造和文件注册策略上存在差异。
flowchart TD
subgraph "入口层"
A1["jd_ecuflash_start<br/>多 ECU 并行刷写"]
A2["jd_ecuFlash_start2<br/>单 ECU 工程刷写"]
A3["jd_ecuFlash_start3<br/>桥文件刷写"]
end
subgraph "线程层"
B1["pthread_download()"]
B2["pthread_download2()"]
B3["pthread_download3()"]
end
subgraph "引擎层"
C["ECUFlasherManager"]
end
subgraph "数据层"
D1["CVehicleConfig<br/>配置与文件匹配"]
D2["DataCenter<br/>安全常量 / 车辆信息"]
end
A1 --> B1
A2 --> B2
A3 --> B3
B1 --> C
B2 --> C
B3 --> C
B1 --> D1
B1 --> D2
B2 --> D1
B2 --> D2
B3 --> D1
B3 --> D2路径一:jd_ecuflash_start(标准多 ECU 并行刷写)
jd_ecuflash_start 是最常用的入口。它创建新的 ECUFlasherManager 实例并启动 pthread_download 线程。
Sources: jidu_flasher.cpp
pthread_download 是核心编排函数。它遍历 mapEcuFlasherTask 中的每个待刷写 ECU,执行以下步骤:
步骤 1——PKI 名称解析:通过 (ecuId & 0xFFF0) 掩码匹配从车辆 ECU 信息中查找 pkiName(PKI 安全体系下的 ECU 标识)。若 pkiName 为空则回退到 ecuName。特殊处理:BGM、CDC、ACU 后缀 _SOC,因为域控芯片的安全常量以 SOC 维度管理。
步骤 2——安全常量绑定:调用 DataCenter::getEcuSafetyConstant(pkiName, safetyConstant) 获取刷写安全常量,提取 keySwdl 作为 PinCode。获取失败则标记该 ECU 为 Ecu_FotaStatus_Fail 并跳过。
步骤 3——域控与非域控分流:TCAM、BGM、CDC、ACU 四个域控制器走 Bin 文件分支,其余 ECU 走 VBF 分支。
Sources: jidu_flasher.cpp
Bin 文件分支(域控):通过 CVehicleConfig::getEcuFileInfo 获取刷写文件配置列表,对每个文件依据 strFileType 进行包类型映射(SBL→SBL、APP→App、DATA→Data、PBL→PBL 且含版本跳过逻辑、ESS/EXE→对应类型),然后以文件路径形式调用 manager->addECUFile。
VBF 文件分支(非域控):逻辑类似但存在以下差异化处理:
PBL类型:若当前车辆 SWPN 已包含 PBL 版本号,则跳过该 PBL(避免重复刷写已存在的 Bootloader)DATA类型 +POTECU:根据DataCenter::m_vehicleInfo.empennage进行高低配过滤——尾翼高配(0x02)跳过12开头的低配数据包,尾翼低配(0x01)跳过13开头的高配数据包- VBF 数据通过
parser->GetVBFData()提取二进制内容,以VBFParserSmall*形式传入manager->addECUFile
Sources: jidu_flasher.cpp
最终触发:整个遍历完成后,若存在状态为 Ecu_FotaStatus_Expect 的 ECU,调用 manager->download() 启动并行下载。
路径二:jd_ecuFlash_start2(工程版单 ECU 刷写)
该路径接受外部指定的 filepath、pinCode、keyInfo 等参数,适用于诊断仪手动选择单个文件刷写的场景。与标准路径的关键差异:
- 清除已有
mapEcuFlasherTask后仅注册单一 ECU - 域控(
TCAM或带_SOC后缀的 ECU)走 Bin 文件分支,以文件大小 4MB(0x400000)为阈值判定 PBL vs App - 非域控走 VBF 解析分支,通过
VBFParserSmall::ParseVBFFile验证文件合法性,从getSWPartType()自动判定包类型 ACU/BGM/CDC(不带_SOC后缀)直接标记失败——工程版不支持对域控主芯片直接刷写
Sources: jidu_flasher.cpp, jidu_flasher.cpp
路径三:jd_ecuFlash_start3(桥文件刷写)
专为桥文件(Bridge File)刷写场景设计。它通过 bridgeId 从 CVehicleConfig 获取桥 ECU 名称及对应的刷写文件配置(getBridgeEcuFileInfo),域控桥文件走 Bin 分支(ID 硬编码 0x6011/0x3011/0x2011/0x1011),其他 ECU 走 VBF 分支并包含 POT 高低配过滤逻辑。
Sources: jidu_flasher.cpp, jidu_flasher.cpp
线程安全与防重入
三条路径均在各自线程函数入口使用 static bool isRunning 标志防止重入——若前次刷写未结束,新的调用将直接返回并记录 error 日志。同时,mapEcuFlasherTask 的所有读写操作均受到 std::mutex mLock 保护。
Sources: jidu_flasher.cpp, jidu_flasher.cpp
诊断命令获取:jd_ecuflash_getCmd
jd_ecuflash_getCmd 是一个辅助接口,通过 manager->getcmd(evEcuName, mVec) 获取指定 ECU 的全部刷写诊断命令列表,用 \r\n 拼接后返回给调用方,用于诊断脚本层的命令预览或日志记录。ECU 名称会自动添加 EV_ 前缀(若尚未包含)。
Sources: jidu_flasher.cpp
模块依赖全景
graph TD
subgraph "jidu_flasher.cpp"
F_API["OTX_API 接口层<br/>jd_ecuflash_*"]
F_TASK["EcuFlasherTask +<br/>mapEcuFlasherTask"]
F_EVT["DefaultEventHandler"]
F_THREAD["pthread_download<br/>pthread_download2<br/>pthread_download3"]
end
subgraph "otxFlash 子系统"
MGR["ECUFlasherManager"]
PAR["ParallelECUFlasher"]
SEQ["SequenceECUFlasher"]
SGL["SingleECUFlasher"]
end
subgraph "配置与数据"
VCFG["CVehicleConfig<br/>整车包配置"]
DC["DataCenter<br/>安全常量/车辆信息"]
MACRO["jidu_macro.h<br/>类型定义"]
end
F_API --> F_TASK
F_API --> F_THREAD
F_THREAD --> MGR
F_THREAD --> VCFG
F_THREAD --> DC
MGR --> PAR
MGR --> SEQ
MGR --> SGL
F_EVT --> F_TASK
F_TASK --> MACRO延伸阅读
- 刷写引擎的核心入口类是
ECUFlasherManager,详见 ECUFlasherManager:ECU 注册、刷写文件绑定与优先级调度 - 并行刷写的底层机制由
ParallelECUFlasher和TaskPool驱动,详见 ParallelECUFlasher:基于 TaskPool 的多 ECU 并行刷写引擎 - 整车配置
CVehicleConfig负责刷写文件匹配与 ECU 映射,详见 CVehicleConfig:整车软件包配置解析与刷写文件匹配策略 - 安全常量
keySwdl等来源于DataCenter的统一缓存,详见 DataCenter:车辆订单、安全常量、证书信息的统一缓存中心 - VBF 文件解析器
VBFParserSmall是实现刷写文件验证与数据提取的基础组件,详见 VBF 文件解析器:VBFHeader、VBFBlock 与 RSA 签名验证 - 刷写生命周期的事件发布订阅模型,详见 事件驱动模型:刷写生命周期事件的发布与订阅机制
- DTC 故障码管理与刷写后的诊断验证,详见 DTC 故障码管理:读取、白名单过滤与掩码重载