Skip to content

CVehicleConfig 是 OTX 代理插件中整车软件包配置的管理中枢,承担三大核心职责:目录结构解析(从本地文件系统加载车型→版本→ECU 的层级关系)、config.json 配置反序列化(将加密的 JSON 配置解析为类型化数据结构)、以及刷写文件精准匹配(根据 ECU 名称/ID + 硬件版本号 + 软件版本号定位到具体的刷写文件及其加密元数据)。该类采用单例模式,通过静态成员 m_vehicleConfig 维护解析后的整车包配置全集。

Sources: jidu_vehicleConfig.h, jidu_vehicleConfig.cpp

数据模型:从 JSON 到类型化结构体

CVehicleConfig 定义并依赖一套层次分明的数据结构体系,该体系在 jidu_macro.h 中声明,完整映射了 config.json 的 JSON Schema。

顶层容器:JD_VEHICLE_CONFIG_t

整车包配置的根结构体,包含基线版本三元组和 ECU 配置列表:

字段类型来源 JSON Key用途
strBaseLinestringbaseLine整车基线版本号,用于刷写前提条件校验
strDisplayBaseLinestringdisplayVersionECOS Display_Baseline_Number_Check 功能所用展示版本号
strBaseLineVerstringbaseLineVer基线版本补充标识
vecListEcuvector<JD_ECU_CONFIG_t>ecu (数组)所有 ECU 的配置项集合

Sources: jidu_macro.h

ECU 配置层:JD_ECU_CONFIG_t

每个 ECU 在整车包中的配置,维系 BOM 名称 → 硬件版本 → 刷写文件列表 的映射链:

字段类型来源 JSON Key说明
nDoipAddrunsigned intdoipAddrDoIP 诊断地址(十六进制字符串解析)
nEcuGroupintecuGroupECU 分组编号,reorder() 据此对订单 ECU 排序
nEcuIdunsigned intecuIdECU 唯一标识,匹配时使用 & 0xFFF0 掩码实现弹性匹配
strBoomEcuNamestringbomEcuNameBOM 编码中的 ECU 名称,用于构建文件系统路径
strEcuNamestringecuNameECU 逻辑名称,对外匹配时的主键
nEcuOrderintecuOrderECU 排序优先级
vecMappingvector<JD_MAPPING_CONFIG_t>mapping (数组)硬件版本到刷写文件列表的映射
vecPreconditionListvector<JD_FLASH_PRECONDITION_t>otherEcuHWCheck (数组)刷写前置条件(依赖的其他 ECU 硬件版本检查)

Sources: jidu_macro.h

映射层:JD_MAPPING_CONFIG_t 与 JD_VSP_FLASH_CONFIG_t

JD_MAPPING_CONFIG_t 以硬件版本号 strHwpn 为键,下挂四类文件列表(App / Data / SBL / OrderList)。其中 vecOderList 最为关键——它定义了该硬件版本下所有可刷写的软件版本及其文件元信息。JD_VSP_FLASH_CONFIG_t 则承载单个刷写文件的完整描述,包含加密元数据(keyId、keyInfo、signature)和本地路径,以及用于 VBF 签名校验的 VBFParserSmall 指针。

mermaid
classDiagram
    class JD_VEHICLE_CONFIG_t {
        +string strBaseLine
        +string strDisplayBaseLine
        +string strBaseLineVer
        +vector~JD_ECU_CONFIG_t~ vecListEcu
    }
    class JD_ECU_CONFIG_t {
        +unsigned int nDoipAddr
        +int nEcuGroup
        +unsigned int nEcuId
        +string strBoomEcuName
        +string strEcuName
        +int nEcuOrder
        +vector~JD_MAPPING_CONFIG_t~ vecMapping
        +vector~JD_FLASH_PRECONDITION_t~ vecPreconditionList
    }
    class JD_MAPPING_CONFIG_t {
        +string strHwpn
        +vector~string~ vecAppList
        +vector~string~ vecDataList
        +vector~string~ vecSblList
        +vector~JD_VSP_FLASH_INFO_t~ vecOderList
    }
    class JD_VSP_FLASH_CONFIG_t {
        +string strFileName
        +string strFileType
        +string strVersion
        +string strkeyId
        +string strKeyInfo
        +string strSignature
        +string strLocalPath
        +uint64_t nFlashFileSize
        +VBFParserSmall* pVBFParser
    }
    class JD_FLASH_PRECONDITION_t {
        +unsigned int nEcuId
        +string strEcuName
        +vector~string~ vecHwPn
    }

    JD_VEHICLE_CONFIG_t "1" --> "*" JD_ECU_CONFIG_t : vecListEcu
    JD_ECU_CONFIG_t "1" --> "*" JD_MAPPING_CONFIG_t : vecMapping
    JD_ECU_CONFIG_t "1" --> "*" JD_FLASH_PRECONDITION_t : vecPreconditionList
    JD_MAPPING_CONFIG_t "1" --> "*" JD_VSP_FLASH_INFO_t : vecOderList
    JD_VSP_FLASH_CONFIG_t ..|> JD_VSP_FLASH_INFO_t : extends

Sources: jidu_macro.h, jidu_macro.h, jidu_macro.h

目录体系:五层路径解析策略

CVehicleConfig 在构造函数中初始化三条基础路径,再结合工厂代码(DMA / PMA)、环境路径(来自 EnvConfig)、车型和软件版本,动态拼接出五层目录结构。核心设计原则:所有业务目录均以 DiagnosisEcuFilesDir(即 Diagnosis/ECU_DATA/JIDUECUFILES/)为锚点向下展开,Bridge ECU 则使用独立的 JIDUECUBRIDGEFILES 分支。

mermaid
graph TD
    A["DiagnosisDir<br/>appPath/Diagnosis/"] --> B["DiagnosisEcuFilesDir<br/>ECU_DATA/JIDUECUFILES/"]
    A --> C["DiagnosisBridgeEcuFilesDir<br/>ECU_DATA/JIDUECUBRIDGEFILES/"]
    
    B --> D["getVehicleTypeFileDir()<br/>[factory]/[env]/"]
    D --> E["getPkgFileDir()<br/>[factory]/[env]/[vehicleType]/"]
    E --> F["getFlashFileDir()<br/>[factory]/[env]/[vehicleType]/[softwareVer]/"]
    F --> G["config.json + ECU 子目录"]
    
    C --> H["getBridgeFileDir(bridgeId)<br/>[factory]/[env]/EcuBridge/[bridgeId]/"]
    H --> I["config.json + ECU 子目录"]

    A --> J["getCertFilepath()<br/>certificate/DiagnosticData.bin"]

目录路径拼接逻辑对 DMA / PMA 工厂做了特殊处理——在这两种工厂代码下,路径中包含 factory 子目录;其他工厂则跳过这一层级直接使用环境路径。这一分支逻辑在 getVehicleTypeFileDirgetPkgFileDirgetFlashFileDirgetBridgeFileDir 四个方法中保持一致。

Sources: jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp

配置加载流程:从文件系统到内存模型

整车包配置的加载遵循 "本地扫描 → 版本选择 → JSON 解析 → 排序归位" 的四阶段流水线。

阶段一:reloadLoacalPkg() —— 版本列表发现

该方法清空 m_mapVehicleTypem_vecVehicleSoftVerm_vehicleConfig.vecListEcu,然后调用 FileUtils::subdirectory() 扫描 getPkgFileDir() 下的所有子目录。每个子目录名称即为一个软件版本号,它们被存入 m_mapVehicleType[m_strVehicleType],构成版本列表。注意:此方法只建立 <车型, 版本列表> 的索引映射,并不解析任何 config.json

Sources: jidu_vehicleConfig.cpp

阶段二:setCurrentVehicleSoftVer() —— 版本校验与触发加载

该方法是外部设置当前软件版本的核心入口,其逻辑严密处理了三种异常场景:

  1. 车型缺失m_mapVehicleType 中找不到当前车型 → 触发 reloadLoacalPkg() 重新扫描
  2. 版本缺失:版本列表中没有目标版本 → 再次触发 reloadLoacalPkg()(可能远端新下发),仍找不到则报错返回 false
  3. 正常命中:直接设置 m_strVehicleSoftVer,调用 reloadConfig() + reorder()

Sources: jidu_vehicleConfig.cpp

阶段三:reloadConfig() —— config.json 解密与反序列化

定位到 getFlashFileDir() + "config.json",通过 CFlashFileUtils::LoadConfig() 完成 Base64 解码 → AES-CBC 解密 → PKCS7 去填充 的解密链后得到明文 JSON。解析过程提取三个基线字段(baseLinedisplayVersionbaseLineVer)并将 ecu 数组委托给 GetEcuList() 进行递归解析。

mermaid
sequenceDiagram
    participant Caller as 调用方
    participant VC as CVehicleConfig
    participant FF as CFlashFileUtils
    participant FS as 文件系统

    Caller->>VC: setCurrentVehicleSoftVer(type, ver)
    VC->>VC: reloadLoacalPkg()
    VC->>FS: FileUtils::subdirectory(pkgDir)
    FS-->>VC: 版本目录列表
    VC->>VC: reloadConfig()
    VC->>FS: getFlashFileDir()/config.json
    FS-->>VC: 加密 JSON 字节流
    VC->>FF: LoadConfig(strConfig, filepath)
    FF->>FF: base64_decode → AES_DecryptConfig → DePKCS7Padding
    FF-->>VC: 明文 JSON 字符串
    VC->>VC: cJSON_Parse → GetEcuList()
    VC-->>Caller: true / false

Sources: jidu_vehicleConfig.cpp, FlashFileUtils.cpp, FlashFileUtils.cpp

阶段四:GetEcuList() —— ECU 配置解析与合并排序

GetEcuList() 是 JSON 反序列化中最复杂的解析器,遍历 ecu 数组中的每个元素,依次处理:

CCP 码过滤:若 ECU 配置中包含 ccpId(二进制位索引)和 ccpCode(期望值),则与车辆 binCCP 中对应位的实际值做比对,不匹配则跳过该 ECU。这意味着同一个 config.json 可包含多种 CCP 配置的 ECU 条目,运行时根据车辆实际 CCP 码做筛选。

字段解析:将 doipAddrecuId 从十六进制字符串转为整数;提取 bomEcuNameecuNameecuGroupecuOrder

Mapping 解析:对每个 mapping 数组元素,解析 hwpnappList / dataList / sblList / orderList 的四类文件列表。其中 orderList 是一个嵌套数组(外层按刷写顺序分组的批次,内层为具体文件),解析为 JD_VSP_FLASH_INFO_t 列表。

前置条件解析otherEcuHWCheck 描述本 ECU 刷写前需检查的其他 ECU 硬件版本,解析为 JD_FLASH_PRECONDITION_t 列表。

合并与排序:相同 nEcuId 的 ECU 条目会合并其 vecMapping(跨条目去重);最终将所有 ECU 按域控优先排序——TCAMBGMCDCACU 四大域控始终排在列表最前面,其余 ECU 保持原顺序追加。

Sources: jidu_vehicleConfig.cpp

刷写文件匹配策略:三级索引链

CVehicleConfig 提供了两组重载的 getEcuFileInfo 方法(按 ECU 名称 + 硬件版本号,或按 ECU ID + 硬件版本号),以及底层的 GetFlashFileInfo,共同构成 ECU 标识 → 硬件版本 → 软件版本 → 刷写文件 的三级匹配链。

匹配流程详解

mermaid
flowchart TD
    A["getEcuFileInfo(ecuName/ecuId, ecuHwpn)"] --> B{"vecListEcu 是否为空?"}
    B -->|是| C["返回 false<br/>记录 'invalid pkg'"]
    B -->|否| D["遍历 m_vehicleConfig.vecListEcu"]
    D --> E{"ECU Name 匹配?<br/>或 (ecuId & 0xFFF0) 匹配?"}
    E -->|否| F["继续遍历下一个 ECU"]
    E -->|是| G["遍历 vecMapping"]
    G --> H{"strHwpn 匹配?<br/>(去除空格后比较)"}
    H -->|否| I["记录 mismatch hwpn 警告"]
    H -->|是| J["遍历 vecOderList<br/>提取所有 SWPN"]
    J --> K["对每个 SWPN 调用<br/>GetFlashFileInfo()"]
    K --> L{"GetFlashFileInfo 成功?"}
    L -->|否| M["返回 false"]
    L -->|是| N["push_back 到 vecFlash"]
    N --> O{"还有 SWPN?"}
    O -->|是| K
    O -->|否| P["返回 true"]

关键设计细节:ECU ID 匹配使用 (ecuId & 0xFFF0) == (configEcuId & 0xFFF0) 而非精确相等。掩码 0xFFF0 屏蔽了低 4 位,使得同一 ECU 家族的不同变体(如 0x5051 BECM1_75 和 0x5052 BECM1_102)能命中同一条配置记录,实现了 ECU 家族的弹性匹配

Sources: jidu_vehicleConfig.cpp

GetFlashFileInfo() —— 单文件元数据加载与 VBF 校验

此方法是匹配链的终端,负责加载单个 SWPN 对应的 .json 描述文件。文件路径构成为:getFlashFileDir()/boomEcuName/hwpn/swpn.json。加载后解析以下字段:

JSON 字段映射目标说明
fileTypeoneFlashInfo.strFileType文件类型标识
fileNameoneFlashInfo.strFileName文件名
version / softNameoneFlashInfo.strVersion版本号(优先取 version,fallback 到 softName
diagnoseEncryptionInfo.sizeoneFlashInfo.nFlashFileSize文件大小
diagnoseEncryptionInfo.localPathoneFlashInfo.strLocalPath本地相对路径
diagnoseEncryptionInfo.keyIdoneFlashInfo.strkeyId加密密钥 ID
diagnoseEncryptionInfo.keyInfooneFlashInfo.strKeyInfoRSA 加密的密钥信息
diagnoseEncryptionInfo.signatureoneFlashInfo.strSignature数字签名

加载完成后存在一条重要的短路分支:若 ECU 名称为 TCAMBGMCDCACU(四大域控),直接返回 true 跳过 VBF 签名校验。其余 ECU 则实例化 VBFParserSmall 并调用 ParseVBFFile() 进行 VBF 文件的 RSA 签名验证,验证通过才返回 true。这意味着域控的刷写文件被信任而无需本地签名校验。

Sources: jidu_vehicleConfig.cpp

getEcuId() —— ECU ID 规范化

当外部传入的 ecuId 不精确时,getEcuId() 通过遍历配置列表,使用 (ecuId & 0xFFF0) 掩码匹配 + 硬件版本号精确匹配,返回规范化的 nEcuId。若未匹配到,则原样返回输入值作为 fallback。

Sources: jidu_vehicleConfig.cpp

reorder():订单 ECU 排序与版本优先级

reorder() 承担两项排序任务:

版本列表重排:将 m_strVehicleSoftVer(当前车辆软件版本)移动到 m_vecVehicleSoftVer 的首位,确保当前版本在 UI 或后续遍历中具有最高优先级。

订单 ECU 按组排序:根据 m_vehicleConfig.vecListEcu 中每个 ECU 的 nEcuGroup 值,对 DataCenter::m_VehicleOrderInfo.vecListEcu 进行稳定排序。nEcuGroup 值小的 ECU 排在前面,实现了刷写顺序依赖的声明式控制——ECU Group 0 最先刷写,Group 1 次之,以此类推。

Sources: jidu_vehicleConfig.cpp

Bridge ECU 扩展:桥接刷写文件匹配

Bridge ECU 是针对需要通过桥接设备进行刷写的 ECU 的独立配置通道,与常规 ECU 配置完全隔离——使用独立的目录树 JIDUECUBRIDGEFILES 和独立的 config.json 结构。

Bridge 的 config.json 结构与主配置不同:它不使用 ecu 数组,而是顶层直接包含 ecuNameListecuIdListmapping 数组。getBridgeFlashFileInfo() 加载单个 Bridge ECU 的刷写文件(同样跳过四大域控的 VBF 校验),getBridgeEcuFileInfo() 则按 hardware version 从 mapping 中遍历 orderList 获取完整的刷写文件列表。

getBridgeEcuName()getBridgeEcuId() 提供便捷的元数据查询,均从 config.json 的数组首元素取值,假定一个 bridgeId 对应单一 ECU。

Sources: jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp

IsCompleted() 与 IsLatestBatch():整车包状态标记

两个方法共享相同的数据源——ecu.txt 文件(位于 getFlashFileDir()getBridgeFileDir() 目录下)。该文件为 UTF-8 BOM 编码的 JSON,包含两个布尔标记:

JSON Key对应方法语义
IsCompletedIsCompleted()该版本整车包是否已完成(所有 ECU 文件就绪)
isLastVersionIsLatestBatch()是否为最新批次(暗示无需继续升级)

IsCompleted(vehicleType, vehicleSoftVer) 有一个值得注意的副作用处理:若传入的 vehicleSoftVer 与当前版本不同,它会临时调用 setCurrentVehicleSoftVer() 切换版本读取 ecu.txt,再恢复原版本。这是典型的临时上下文切换模式,确保查询不影响当前全局状态。

Bridge 版本的 IsCompleted(uint16_t bridgeId) 直接使用 getBridgeFileDir() 定位,不涉及版本切换。

Sources: jidu_vehicleConfig.cpp

单例与全局状态管理

CVehicleConfig 采用经典的懒汉式单例模式,通过静态指针 instancegetInstance() 提供全局唯一访问点。其静态成员承担全局状态角色:

静态成员类型生命周期语义
m_strVehicleSoftVerstring当前选定的车辆软件版本,跨请求保持不变
m_strVehicleTypestring当前车型,默认值为 "MarsOne"
m_vecVehicleSoftVervector<string>当前车型下所有可用版本列表
m_vehicleConfigJD_VEHICLE_CONFIG_t已解析的整车包配置(单次加载后缓存)
m_mapVehicleTypemap<string, vector<string>>所有车型→版本列表的索引

这种设计使 CVehicleConfig 成为贯穿刷写流程的配置中枢——jidu_flasher.cppjidu.cpp 等核心模块均通过 CVehicleConfig::getInstance() 获取配置数据,无需重复解析。

Sources: jidu_vehicleConfig.h, jidu_vehicleConfig.cpp, jidu_flasher.cpp, jidu.cpp

与其他模块的关系

mermaid
graph LR
    DC["DataCenter<br/>车辆订单/CCP/工厂信息"] -->|提供 m_VehicleOrderInfo<br/>factory / binCCP| VC["CVehicleConfig"]
    EC["EnvConfig"] -->|提供 getEnvPath()<br/>getAppPath()| VC
    VC -->|JD_VSP_FLASH_CONFIG_t<br/>(含 VBF 解析结果)| FL["jidu_flasher<br/>刷写任务编排"]
    VC -->|ECU 配置查询| JD["jidu.cpp<br/>诊断流程"]
    VC -->|LoadConfig| FF["CFlashFileUtils<br/>AES+RSA 解密"]
    VC -->|ParseVBFFile| VP["VBFParserSmall<br/>VBF 签名验证"]
    VC -->|subdirectory| FU["FileUtils<br/>目录扫描"]
  • DataCenter 为 CVehicleConfig 提供车辆订单信息(用于 reorder() 排序)、工厂代码(决定目录路径分支)以及 CCP 码(用于 GetEcuList() 过滤)
  • EnvConfig 提供环境路径(Dev/Test/Staging/Prod)和应用根路径,决定从哪个环境目录加载配置
  • CFlashFileUtils 提供 AES-CBC 解密能力,所有 config.json.json 刷写文件描述均经过 Base64 + AES + PKCS7 的解密链
  • VBFParserSmall 在非域控 ECU 的刷写文件加载时被触发,用于校验 VBF 文件的 RSA 签名
  • 下游消费者 jidu_flasher 通过 getEcuFileInfo() 获取刷写文件列表,jidu.cpp 通过 getEcuConfig() 查询 ECU 配置

Sources: jidu_vehicleConfig.cpp, jidu_flasher.cpp, jidu.cpp

阅读指引

理解 CVehicleConfig 后,建议继续阅读以下关联文档以建立完整的刷写配置知识链: