Skip to content

jidu_flasher 模块是集度 OTX 代理插件中刷写业务流程的顶层编排层,向上暴露一组 C 风格的 OTX_API 接口供 OTX 诊断脚本调用,向下聚合 ECUFlasherManager(刷写引擎)、CVehicleConfig(整车配置)和 DataCenter(数据中心)三大子系统,完成从 ECU 任务注册、硬件版本校验、安全常量绑定到并行下载执行与 FOTA 状态热跟踪的完整链路。

核心数据结构:刷写任务与状态枚举

模块以 EcuFlasherTask 结构体作为单个 ECU 刷写任务的内存画像。该结构体记录了刷写进度(progress)、优先级(priority)、FOTA 状态机当前值(status),以及三个秒级时间戳——下载开始(startTimeStamp)、下载结束(endTimeStamp)和安装完成(InstallTimeStamp),同时还缓存了 ecuNameecuIdevNamelogicalLink 等路由信息。全局 map<uint16_t, EcuFlasherTask> mapEcuFlasherTaskecuId 为键聚合所有待刷写任务,贯穿整个刷写生命周期。

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 通过字符串匹配(SBLAPPDATAPBLESSEXE 前缀)将 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_750x5052 BECM1_102)共享同一高 12 位基础地址,掩码匹配可容忍此类子地址差异。

Sources: jidu_flasher.cpp

下表比较两种注册路径的关键差异:

维度jd_ecuflash_addEcujd_ecuflash_addEcu_byEcuId
输入标识订单 ECU 名称字符串ECU ID 数值
名称转换getEcuName_byOrderEcuName 映射直接从 vecListEcustrEcuName
ID 匹配策略精确匹配 nEcuId掩码匹配 (id & 0xFFF0)
适用场景标准多 ECU 并行刷写指定 ID 的针对性刷写(如桥文件刷写)

硬件版本检查:前置条件验证的双重路径

jd_ecuflash_hwpnCheckjd_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,这意味着配置中的允许列表可以包含部分版本号前缀,为版本兼容性提供了一定的弹性。

mermaid
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_orderCheckjd_ecuflash_orderCheck_byEcuId 在刷写执行前进行订单级别的版本一致性核验。对于订单中的每个 ECU,依次执行:第一层(硬件)——比对车辆实时 HWPN 与订单 HWPN,不匹配则记录日志并返回 false第二层(软件)——通过 CVehicleConfig::getEcuFileInfo 获取刷写文件列表,遍历每个文件的 strFileTypestrVersion,与订单中的 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。

Sources: jidu.cpp, jidu.cpp

安全访问模式:PinCode 与换件刷写

jd_ecuflash_setSecurityAccessMode(int mode) 设置模块级静态变量 mEcurityAccessMode,控制刷写时的安全访问策略:模式 0(默认)优先使用从 DataCenter 获取的 ECU 安全常量 keySwdl 作为 PinCode;模式 1(换件刷写)则优先使用 FFFFFFFF 作为 PinCode,用于 ECU 更换后首次刷写场景。该模式值最终在 pthread_download 中通过 manager->addECUsecurityAccessMode 参数传递给刷写引擎。

Sources: jidu_flasher.cpp, jidu_flasher.cpp

FOTA 状态热跟踪:DefaultEventHandler 事件驱动模型

DefaultEventHandler 是模块内部定义的关键子类,多重继承自 otx::flash::ECUFlasherEventHandler,覆盖了六个核心事件回调,全部以 std::lock_guard<std::mutex> lock(mItemLock) 保证线程安全:

事件回调触发动机mapEcuFlasherTask 的更新
onECUDownloadStartECU 开始下载status → Executing,记录 startTimeStamp
onECUDownloadEndECU 下载结束根据 arg.getStatus() 设置 SuccessFail,记录 endTimeStamp
onECUResetStartECU 复位开始status → ResetStart
onECUResetEndECU 复位结束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 的下载进度百分比,未注册返回 -1
  • jd_ecuflash_getStatus / _byEcuId:返回 EcuFotaStatus_t 枚举值,未注册返回 Ecu_FotaStatus_Fail
  • jd_ecuflash_getRemainder / _byEcuId:返回预估剩余秒数
  • jd_ecuflash_getInstallRemainder / _byEcuId:基于 InstallTimeStamp 和传入的 expectedTime 计算安装剩余时间。算法为 expectedTime - (timeNow - InstallTimeStamp),若已超时则返回 1(保底值,避免返回 0 或负数)
  • jd_ecuflash_getStartTimestamp / _byEcuIdjd_ecuflash_getEndTimestamp / _byEcuId:返回秒级 Unix 时间戳
  • jd_ecuflash_ecuCount(int status):遍历 mapEcuFlasherTask,统计处于指定状态的 ECU 数量

Sources: jidu_flasher.cpp

刷写执行引擎:三种启动路径的统一架构

模块通过三个顶层入口函数暴露不同的刷写启动模式,三者共享相同的架构骨架(创建 ECUFlasherManager → 注册事件处理器 → 构造任务 → 启动独立线程 → detach),但在任务构造和文件注册策略上存在差异。

mermaid
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。特殊处理:BGMCDCACU 后缀 _SOC,因为域控芯片的安全常量以 SOC 维度管理。

步骤 2——安全常量绑定:调用 DataCenter::getEcuSafetyConstant(pkiName, safetyConstant) 获取刷写安全常量,提取 keySwdl 作为 PinCode。获取失败则标记该 ECU 为 Ecu_FotaStatus_Fail 并跳过。

步骤 3——域控与非域控分流TCAMBGMCDCACU 四个域控制器走 Bin 文件分支,其余 ECU 走 VBF 分支。

Sources: jidu_flasher.cpp

Bin 文件分支(域控):通过 CVehicleConfig::getEcuFileInfo 获取刷写文件配置列表,对每个文件依据 strFileType 进行包类型映射(SBL→SBLAPP→AppDATA→DataPBL→PBL 且含版本跳过逻辑、ESS/EXE→对应类型),然后以文件路径形式调用 manager->addECUFile

VBF 文件分支(非域控):逻辑类似但存在以下差异化处理:

  • PBL 类型:若当前车辆 SWPN 已包含 PBL 版本号,则跳过该 PBL(避免重复刷写已存在的 Bootloader)
  • DATA 类型 + POT ECU:根据 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 刷写)

该路径接受外部指定的 filepathpinCodekeyInfo 等参数,适用于诊断仪手动选择单个文件刷写的场景。与标准路径的关键差异:

  • 清除已有 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)刷写场景设计。它通过 bridgeIdCVehicleConfig 获取桥 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

模块依赖全景

mermaid
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

延伸阅读