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 | 用途 |
|---|---|---|---|
strBaseLine | string | baseLine | 整车基线版本号,用于刷写前提条件校验 |
strDisplayBaseLine | string | displayVersion | ECOS Display_Baseline_Number_Check 功能所用展示版本号 |
strBaseLineVer | string | baseLineVer | 基线版本补充标识 |
vecListEcu | vector<JD_ECU_CONFIG_t> | ecu (数组) | 所有 ECU 的配置项集合 |
Sources: jidu_macro.h
ECU 配置层:JD_ECU_CONFIG_t
每个 ECU 在整车包中的配置,维系 BOM 名称 → 硬件版本 → 刷写文件列表 的映射链:
| 字段 | 类型 | 来源 JSON Key | 说明 |
|---|---|---|---|
nDoipAddr | unsigned int | doipAddr | DoIP 诊断地址(十六进制字符串解析) |
nEcuGroup | int | ecuGroup | ECU 分组编号,reorder() 据此对订单 ECU 排序 |
nEcuId | unsigned int | ecuId | ECU 唯一标识,匹配时使用 & 0xFFF0 掩码实现弹性匹配 |
strBoomEcuName | string | bomEcuName | BOM 编码中的 ECU 名称,用于构建文件系统路径 |
strEcuName | string | ecuName | ECU 逻辑名称,对外匹配时的主键 |
nEcuOrder | int | ecuOrder | ECU 排序优先级 |
vecMapping | vector<JD_MAPPING_CONFIG_t> | mapping (数组) | 硬件版本到刷写文件列表的映射 |
vecPreconditionList | vector<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 指针。
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 : extendsSources: jidu_macro.h, jidu_macro.h, jidu_macro.h
目录体系:五层路径解析策略
CVehicleConfig 在构造函数中初始化三条基础路径,再结合工厂代码(DMA / PMA)、环境路径(来自 EnvConfig)、车型和软件版本,动态拼接出五层目录结构。核心设计原则:所有业务目录均以 DiagnosisEcuFilesDir(即 Diagnosis/ECU_DATA/JIDUECUFILES/)为锚点向下展开,Bridge ECU 则使用独立的 JIDUECUBRIDGEFILES 分支。
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 子目录;其他工厂则跳过这一层级直接使用环境路径。这一分支逻辑在 getVehicleTypeFileDir、getPkgFileDir、getFlashFileDir 和 getBridgeFileDir 四个方法中保持一致。
Sources: jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp, jidu_vehicleConfig.cpp
配置加载流程:从文件系统到内存模型
整车包配置的加载遵循 "本地扫描 → 版本选择 → JSON 解析 → 排序归位" 的四阶段流水线。
阶段一:reloadLoacalPkg() —— 版本列表发现
该方法清空 m_mapVehicleType、m_vecVehicleSoftVer 和 m_vehicleConfig.vecListEcu,然后调用 FileUtils::subdirectory() 扫描 getPkgFileDir() 下的所有子目录。每个子目录名称即为一个软件版本号,它们被存入 m_mapVehicleType[m_strVehicleType],构成版本列表。注意:此方法只建立 <车型, 版本列表> 的索引映射,并不解析任何 config.json。
Sources: jidu_vehicleConfig.cpp
阶段二:setCurrentVehicleSoftVer() —— 版本校验与触发加载
该方法是外部设置当前软件版本的核心入口,其逻辑严密处理了三种异常场景:
- 车型缺失:
m_mapVehicleType中找不到当前车型 → 触发reloadLoacalPkg()重新扫描 - 版本缺失:版本列表中没有目标版本 → 再次触发
reloadLoacalPkg()(可能远端新下发),仍找不到则报错返回false - 正常命中:直接设置
m_strVehicleSoftVer,调用reloadConfig()+reorder()
Sources: jidu_vehicleConfig.cpp
阶段三:reloadConfig() —— config.json 解密与反序列化
定位到 getFlashFileDir() + "config.json",通过 CFlashFileUtils::LoadConfig() 完成 Base64 解码 → AES-CBC 解密 → PKCS7 去填充 的解密链后得到明文 JSON。解析过程提取三个基线字段(baseLine、displayVersion、baseLineVer)并将 ecu 数组委托给 GetEcuList() 进行递归解析。
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 / falseSources: jidu_vehicleConfig.cpp, FlashFileUtils.cpp, FlashFileUtils.cpp
阶段四:GetEcuList() —— ECU 配置解析与合并排序
GetEcuList() 是 JSON 反序列化中最复杂的解析器,遍历 ecu 数组中的每个元素,依次处理:
CCP 码过滤:若 ECU 配置中包含 ccpId(二进制位索引)和 ccpCode(期望值),则与车辆 binCCP 中对应位的实际值做比对,不匹配则跳过该 ECU。这意味着同一个 config.json 可包含多种 CCP 配置的 ECU 条目,运行时根据车辆实际 CCP 码做筛选。
字段解析:将 doipAddr、ecuId 从十六进制字符串转为整数;提取 bomEcuName、ecuName、ecuGroup、ecuOrder。
Mapping 解析:对每个 mapping 数组元素,解析 hwpn → appList / dataList / sblList / orderList 的四类文件列表。其中 orderList 是一个嵌套数组(外层按刷写顺序分组的批次,内层为具体文件),解析为 JD_VSP_FLASH_INFO_t 列表。
前置条件解析:otherEcuHWCheck 描述本 ECU 刷写前需检查的其他 ECU 硬件版本,解析为 JD_FLASH_PRECONDITION_t 列表。
合并与排序:相同 nEcuId 的 ECU 条目会合并其 vecMapping(跨条目去重);最终将所有 ECU 按域控优先排序——TCAM、BGM、CDC、ACU 四大域控始终排在列表最前面,其余 ECU 保持原顺序追加。
Sources: jidu_vehicleConfig.cpp
刷写文件匹配策略:三级索引链
CVehicleConfig 提供了两组重载的 getEcuFileInfo 方法(按 ECU 名称 + 硬件版本号,或按 ECU ID + 硬件版本号),以及底层的 GetFlashFileInfo,共同构成 ECU 标识 → 硬件版本 → 软件版本 → 刷写文件 的三级匹配链。
匹配流程详解
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 字段 | 映射目标 | 说明 |
|---|---|---|
fileType | oneFlashInfo.strFileType | 文件类型标识 |
fileName | oneFlashInfo.strFileName | 文件名 |
version / softName | oneFlashInfo.strVersion | 版本号(优先取 version,fallback 到 softName) |
diagnoseEncryptionInfo.size | oneFlashInfo.nFlashFileSize | 文件大小 |
diagnoseEncryptionInfo.localPath | oneFlashInfo.strLocalPath | 本地相对路径 |
diagnoseEncryptionInfo.keyId | oneFlashInfo.strkeyId | 加密密钥 ID |
diagnoseEncryptionInfo.keyInfo | oneFlashInfo.strKeyInfo | RSA 加密的密钥信息 |
diagnoseEncryptionInfo.signature | oneFlashInfo.strSignature | 数字签名 |
加载完成后存在一条重要的短路分支:若 ECU 名称为 TCAM、BGM、CDC 或 ACU(四大域控),直接返回 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 数组,而是顶层直接包含 ecuNameList、ecuIdList 和 mapping 数组。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 | 对应方法 | 语义 |
|---|---|---|
IsCompleted | IsCompleted() | 该版本整车包是否已完成(所有 ECU 文件就绪) |
isLastVersion | IsLatestBatch() | 是否为最新批次(暗示无需继续升级) |
IsCompleted(vehicleType, vehicleSoftVer) 有一个值得注意的副作用处理:若传入的 vehicleSoftVer 与当前版本不同,它会临时调用 setCurrentVehicleSoftVer() 切换版本读取 ecu.txt,再恢复原版本。这是典型的临时上下文切换模式,确保查询不影响当前全局状态。
Bridge 版本的 IsCompleted(uint16_t bridgeId) 直接使用 getBridgeFileDir() 定位,不涉及版本切换。
Sources: jidu_vehicleConfig.cpp
单例与全局状态管理
CVehicleConfig 采用经典的懒汉式单例模式,通过静态指针 instance 和 getInstance() 提供全局唯一访问点。其静态成员承担全局状态角色:
| 静态成员 | 类型 | 生命周期语义 |
|---|---|---|
m_strVehicleSoftVer | string | 当前选定的车辆软件版本,跨请求保持不变 |
m_strVehicleType | string | 当前车型,默认值为 "MarsOne" |
m_vecVehicleSoftVer | vector<string> | 当前车型下所有可用版本列表 |
m_vehicleConfig | JD_VEHICLE_CONFIG_t | 已解析的整车包配置(单次加载后缓存) |
m_mapVehicleType | map<string, vector<string>> | 所有车型→版本列表的索引 |
这种设计使 CVehicleConfig 成为贯穿刷写流程的配置中枢——jidu_flasher.cpp、jidu.cpp 等核心模块均通过 CVehicleConfig::getInstance() 获取配置数据,无需重复解析。
Sources: jidu_vehicleConfig.h, jidu_vehicleConfig.cpp, jidu_flasher.cpp, jidu.cpp
与其他模块的关系
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 后,建议继续阅读以下关联文档以建立完整的刷写配置知识链:
- DataCenter:车辆订单、安全常量、证书信息的统一缓存中心 — 了解 CVehicleConfig 所依赖的 CCP 码、工厂代码和订单 ECU 列表的来源
- EnvConfig:多环境(Dev/Test/Staging/Prod)配置切换机制 — 理解目录路径中
getEnvPath()的动态切换逻辑 - VBF 文件解析器:VBFHeader、VBFBlock 与 RSA 签名验证 — 深入
GetFlashFileInfo()中 VBF 签名校验的底层实现 - jidu_flasher:刷写任务编排、硬件版本检查与 FOTA 状态跟踪 — 查看 CVehicleConfig 产出的刷写文件信息如何被消费和编排