DataCenter 是集度 OTX 代理插件中的全局单例数据中心,作为跨模块共享状态的唯一汇聚点,统一管理从云端服务器获取的车辆订单信息、ECU 安全常量、证书密钥、EOL 密钥、诊断数据、DTC 白名单等核心运行时数据。它采用静态成员变量 + 懒汉单例的设计模式,确保所有模块通过一致的访问路径读写同一份数据副本,避免了数据冗余和状态不一致问题。
Sources: jidu_dataCenter.h
架构设计:单例模式与全局静态存储
DataCenter 类采用经典的懒汉式单例(Lazy Singleton)模式,通过 getInstance() 静态方法返回全局唯一实例。其核心设计特点在于:绝大多数数据成员被声明为 static,这意味着即使不通过 getInstance() 获取实例指针,任何包含 jidu_dataCenter.h 的编译单元都可以直接通过 DataCenter::memberName 访问静态数据。这种设计在便利性和封装性之间做了权衡——它为 jidu_orderInfo.cpp、jidu_vehicleInfo.cpp、jidu_client.cpp 等大量直接读取 DataCenter 静态成员的模块提供了零开销的访问路径。
Sources: jidu_dataCenter.h, jidu_dataCenter.h
classDiagram
class DataCenter {
+static JD_VEHICLE_ORDER_t m_VehicleOrderInfo
+map~string,JD_ECU_SAFETY_CONSTANT_t~ m_EcuSafetyConstant
+static JD_EOL_KEY_t m_EolKey
+static JD_OTA_CERT_t m_OtaCert
+static map~string,string~ m_aesKey
+static JD_SummaryData_t m_summaryData
+static JD_EcuCertInfo_t m_EcuCertInfo
+static string m_deviceSN
+static map~string,JD_EcuStatusInfo_t~ m_EcuStatus
+static string errMsg
+static string factory
+static string language
+static VehicleInfo m_vehicleInfo
+static JD_ECU_DIAGNOSTIC_INFO_t m_EcuBncmCloudInfo
+static vector~JD_ECU_DIAGNOSTIC_INFO_t~ m_vecEcuDiagnosticInfo
+static map~string,vector~JIDU_DTC_INFO_t~~ m_mapDtcDatabase
+static map~int,CallServerRecord~ m_callServerRecord
+static vector~EbocInfo~ m_vecEbocInfo
-static DataCenter* instance
+static getInstance() DataCenter*
+reset() void
+setEcuSafetyConstant(string, JD_ECU_SAFETY_CONSTANT_t) bool
+getEcuSafetyConstant(string, JD_ECU_SAFETY_CONSTANT_t&) bool
+getCarConfig(CBinary, VehicleCfg&) bool
+getCarConfig(CBinary) int
+getCarConfig(string) int
}
class VehicleInfo {
+CBinary binCCP
+vector~string~ vecEcuName
+vector~uint16_t~ vecEcuId
+string baseLine
+map~uint16_t,JD_VehicleEcuInfo_t~ mapEcuInfo
+string tpmsId
+string batteryRemain
+string vehicleType
+int sshStaus
+int chargingIsOpen
+int gloveboxIsOpen
+int powerType
+int carType
+int carTypeId
+int empennage
}
class VehicleCfg {
+float iBatteryCapacity
+string strMotor
+string strWpcEcuName
+string strBecmEcuName
+string strWpcConfig
+uint8_t iEmpennage
+int iPlatformVoltage
+string strIemEcuName
}
class CallServerRecord {
+string host
+string uri
+string method
+string body
+string response
}
DataCenter --> VehicleInfo
DataCenter --> CallServerRecord
DataCenter ..> VehicleCfg核心数据域详解
DataCenter 的 19 个静态/实例成员覆盖了从云端通信到本地诊断、从刷写前置校验到报告生成的完整数据需求。下面按功能域分类阐述。
车辆订单信息:m_VehicleOrderInfo
类型为 JD_VEHICLE_ORDER_t,是整个插件中最重要的数据结构之一。它存储了从服务器接口 callServer(26)(工厂模式)或 callServer(54)(售后模式)获取的完整车辆订单,包含 VIN、MixNo、CCP 编码、车型、基线版本、软件版本号、EBOC 编码、车辆环境标识 以及 ECU 清单(vecListEcu)。每个 ECU 条目(JD_ECU_INFO_t)又包含 ECU ID(十六进制)、ECU 名称、硬件版本号(HWPN)、序列号(DN)以及软件版本号列表(SWPN List)。vecListEcuNew 则是通过 callServer(88) 从车辆软件应用接口获取的新版 ECU 清单,按相同结构存储但来源不同。jidu_orderInfo.cpp 中的所有 get_jd_orderInfo_* 系列函数均以该字段为数据源。
Sources: jidu_macro.h, [jidu_client.cpp](src/jidu_client.cpp#L26-L88 中的 case 26 回调), jidu_orderInfo.cpp
ECU 安全常量:m_EcuSafetyConstant
类型为 map<string, JD_ECU_SAFETY_CONSTANT_t>,以 ECU 名称(如 "BGM"、"CDC"、"TCAM"、"ACU")为键,存储每个 ECU 的四类安全常量:keySwdl(软件下载密钥)、keyImmo(防盗密钥)、keySal(盐值密钥)、keyCommon(通用密钥)。这些常量在诊断安全访问(Security Access)流程中用于 SeedToKey 计算。数据通过 callServer(6)(工厂)或 callServer(59)(售后)从服务器获取 JSON 响应中的 swdl、immo、sal、common 四个数组解析填充。注意 m_EcuSafetyConstant 是 DataCenter 中唯一非 static 的 map 型数据成员——它通过 setEcuSafetyConstant() 和 getEcuSafetyConstant() 实例方法访问,而非直接操作。
Sources: jidu_macro.h, [jidu_client.cpp](src/jidu_client.cpp#L6 与 L59 case), jidu_dataCenter.cpp
EOL 密钥:m_EolKey
类型为 JD_EOL_KEY_t,存储下线检测(End-of-Line)所需的密钥集合:各 ECU 的防盗密钥(immoKeyEcm、immoKeyIem、immoKeyMgm、immoKeyRemote)、BNCM/BGM 域控密钥(dkBncmBgm)以及电源根密钥(powerRootKey、powerRootKeyId)。数据通过 callServer(19)(工厂)或 callServer(58)(售后)获取。
Sources: jidu_macro.h, [jidu_client.cpp](src/jidu_client.cpp#L19 与 L58 case)
OTA 证书:m_OtaCert
类型为 JD_OTA_CERT_t,存储 OTA 远程升级所需的 CA 证书(caCert) 和 客户端证书(clientCert),均为 Base64 编码字符串。通过 callServer(27)(工厂)或 callServer(64)(售后)获取。在 jidu_orderInfo.cpp 中,get_jd_otaCert_ca() 和 get_jd_otaCert_client() 对其进行 Base64 解码后输出原始二进制。
Sources: jidu_macro.h, jidu_orderInfo.cpp
ECU 证书信息:m_EcuCertInfo
类型为 JD_EcuCertInfo_t,是域控 ECU(TCAM、BGM、CDC、ACU)的证书信息集合。由三部分组成:cls_strEcuCa(ECU CA 证书,Base64)、cls_strVid(车辆 VID,十六进制字符串)以及 ecuCertInfo(以 ECU 类型名为键的 JD_EcuCertItem_t map,每个条目包含 P12 证书、P12 密钥、AES 密钥、SOA 密钥和域控密钥 dvcKey)。数据通过 callServer(20/23)(工厂)或 callServer(56/65)(售后)获取。
Sources: jidu_macro.h, [jidu_client.cpp](src/jidu_client.cpp#L20/23/56/65 case)
AES 密钥映射:m_aesKey
类型为 map<string, string>,以 ECU 名为键存储 AES 密钥字符串。通过 callServer(21)(工厂)或 callServer(55)(售后)获取。特别地,当这些接口返回错误码 9002/9004/9005 时,系统会自动回退到硬编码的默认 AES 密钥(\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF 的 Base64 编码),写入 ACU、BGM、CDC、TCAM 四个域控 ECU,确保基础通信能力不中断。
Sources: [jidu_client.cpp](src/jidu_client.cpp#L21/55 case), [jidu_client.cpp](src/jidu_client.cpp#L901-L907 middle fallback logic)
车辆实时信息:m_vehicleInfo
类型为 VehicleInfo,存储从车辆实时读取的状态信息,包括:CCP 原始二进制数据(binCCP)、ECU 名称与 ID 列表、基线版本号、胎压 ID(tpmsId)、电池剩余电量(batteryRemain,默认 "未知")、车型(vehicleType,默认 "MarsOne")、SSH 状态、充电口状态、手套箱状态、高压平台电压类型(powerType,来自 CCP#962)、车型编码(carType 来自 CCP#950、carTypeId 来自 CCP#966/967)以及尾翼配置(empennage 来自 CCP#564)。这些数据通过 set_jd_vehicleInfo_* 系列 API 从 OTX 脚本层写入。
Sources: jidu_dataCenter.h
ECU 状态跟踪:m_EcuStatus
类型为 map<string, JD_EcuStatusInfo_t>,以 ECU 名称为键,存储每个 ECU 的三维状态:ECU 状态(未配置/离线/在线/需升级/硬件不匹配/软件不匹配/刷写条件不满足)、FOTA 状态(无结果/离线/硬件错误/升级失败/升级成功/选装件)以及 配置状态(未安装/高配/低配)。这些状态在刷写前置检查和报告生成中被大量引用。
Sources: jidu_macro.h
其他全局状态
| 字段 | 类型 | 用途 | 填充来源 |
|---|---|---|---|
m_deviceSN | string | 诊断设备序列号 | set_jd_deviceSn() API 或 pver 配置文件 |
errMsg | string | 最近一次 callServer 的错误消息 | JiduClient::callServer() 失败路径 |
factory | string | 工厂标识("PMA" 或 "DMA"),决定服务器环境路由 | setFactory() API |
language | string | 界面语言(默认 "zh-CN"),作为 API 请求参数 | setLanguage() API |
m_summaryData | JD_SummaryData_t | 工位测试汇总结果 | callServer(28) |
m_callServerRecord | map<int, CallServerRecord> | 按请求类型索引的服务器调用完整记录(host/uri/method/body/response) | 每次 callServer 自动记录 |
m_vecEbocInfo | vector<EbocInfo> | EBOC 特性配置信息列表 | callServer(79) |
m_EcuBncmCloudInfo | JD_ECU_DIAGNOSTIC_INFO_t | BNCM 云端诊断指令信息 | callServer(29/67) 上传后缓存 |
m_vecEcuDiagnosticInfo | vector<JD_ECU_DIAGNOSTIC_INFO_t> | 各 ECU 诊断指令信息集合 | jd_add_ecuDiagnosticInfo() 累积 |
m_mapDtcDatabase | map<string, vector<JIDU_DTC_INFO_t>> | 以 ECU 名为键的 DTC 故障码数据库 | 配置文件加载 |
Sources: jidu_dataCenter.h, jidu.cpp
车辆配置解析:getCarConfig 方法
getCarConfig 是 DataCenter 中最具业务逻辑深度的方法,它从原始 CCP(Car Configuration Parameter)二进制数据中解析出车辆的完整硬件配置。提供三个重载版本:
| 重载 | 签名 | 返回值含义 |
|---|---|---|
| 完整配置版 | getCarConfig(CBinary ccp, VehicleCfg& cfg) | 成功返回 true,填充完整的 VehicleCfg 结构 |
| 电池容量版 | getCarConfig(CBinary ccp) | 返回电池容量(kWh),失败返回 -1 |
| 字符串适配版 | getCarConfig(string ccp) | 将十六进制 CCP 字符串转为 CBinary 后委托给电池容量版 |
完整配置版的解析逻辑分五个维度,所有解析前提是 CCP 数据长度超过 1500 字节:
电池容量与电机类型:通过
CCP[2](索引从 0 开始,对应 CCP#3)判定电机配置(0x81= 单电机,0x80= 双电机),通过CCP[565](对应 CCP#566)判定电池容量(0x17= 75kWh,0x10= 102kWh,0x18= 76kWh,0x19= 98kWh)。三种车型组合分别为 标续版(单电机 + 75kWh)、长续版(单电机 + 102kWh)、性能版(双电机 + 102kWh)。尾翼配置:
CCP[563](对应 CCP#564),0x01= 低配无主动尾翼,0x02= 高配有尾翼。无线充电配置:
CCP[449](对应 CCP#450),1–5 对应五种配置组合(无 NFC 无 WPC / 仅 NFC / 仅 WPC / NFC+WPC / NFC+双 WPC),并据此确定 WPC ECU 名称为"WPC"或"WPC2"。高压平台电压:
CCP[961](对应 CCP#962),0x00= 400V,0x02= 800V。IEM 电机控制器型号:遍历
m_VehicleOrderInfo.vecListEcu,根据 IEM 的 ECU ID 判定——0x5071= IEM_IGBT,0x5072= IEM_SIC,其他 = IEM2。
电池容量版重载额外处理了 0x18(76kWh)和 0x19(98kWh)两种扩展电池类型——这些在完整配置版中会落入 else 分支返回默认值 98kWh。
Sources: jidu_dataCenter.cpp
flowchart TD
A[CCP 二进制数据] --> B{长度 > 1500?}
B -->|否| C[Tracer::error 并返回 false/-1]
B -->|是| D[CCP#3: 电机类型]
D --> E[CCP#566: 电池容量]
E --> F{组合判断}
F -->|0x81 + 0x17| G[标续版: 75kWh 单电机]
F -->|0x81 + 0x10| H[长续版: 102kWh 单电机]
F -->|0x80 + 0x10| I[性能版: 102kWh 双电机]
F -->|其他| J[默认: 98kWh 双电机]
G & H & I & J --> K[CCP#564: 尾翼配置]
K --> L[CCP#450: WPC 充电配置]
L --> M[CCP#962: 高压平台电压]
M --> N[遍历 vecListEcu 判定 IEM 型号]
N --> O[填充 VehicleCfg 并返回]ECU 安全常量查找机制:命名回退链
getEcuSafetyConstant() 方法实现了一个多级 ECU 名称回退(fallback)策略。当传入的 ECU 名称在 m_EcuSafetyConstant map 中精确匹配失败时,方法会依次应用以下规则:
| 规则 | 触发条件 | 回退为 |
|---|---|---|
| IEM 前缀匹配 | 名称以 "IEM" 开头 | "IEM" |
| BECM 前缀匹配 | 名称以 "BECM" 开头 | "BECM1" |
| WPC 前缀匹配(排除 WPC3) | 名称以 "WPC" 开头且不等于 "WPC3" | "WPC" |
| RCML 前缀匹配 | 名称以 "RCML" 开头 | "RCML" |
| RCMR 前缀匹配 | 名称以 "RCMR" 开头 | "RCMR" |
| RCMM 前缀匹配 | 名称以 "RCMM" 开头 | "RCMM" |
| MGM 前缀匹配 | 名称以 "MGM" 开头 | "MGM" |
| 电压后缀剥离 | 名称以 "_400V" 或 "_800V" 结尾 | 去掉后缀(如 "BECM1_400V" → "BECM1") |
这种设计使得调用方可以传入带有配置后缀的 ECU 名称(如 "IEM_IGBT"、"BECM1_102"),而 DataCenter 内部自动将其映射到基础 ECU 类型的安全常量条目,避免为每个变体重复存储相同的安全密钥。注意 setEcuSafetyConstant() 方法始终返回 false(疑似未完成实现),但数据实际已被写入 map 中。
Sources: jidu_dataCenter.cpp
数据填充流程:JiduClient 后处理回调
DataCenter 中绝大多数数据并非主动拉取,而是通过 JiduClient::callServer_affterHandler() 在服务器请求成功后被动填充。该函数内部有一个巨大的 switch (iType) 分支,根据请求类型编号将 JSON 响应解析并写入 DataCenter 的对应静态字段。下表梳理主要映射关系:
| iType | 前置清理(preHandler) | 后置填充目标(affterHandler) | 说明 |
|---|---|---|---|
| 1 | — | m_VehicleOrderInfo.strCCPCode / strVehicleType | 获取 CCP 编码或车型 |
| 4 | 清空 strTpmsId、strTpmsSupplierId | m_VehicleOrderInfo.strTpmsId / strTpmsSupplierId | 获取胎压 ID |
| 6 / 59 | 清空 m_EcuSafetyConstant | m_EcuSafetyConstant(逐 ECU 填充四类密钥) | 安全常量(工厂/售后) |
| 11 / 19 / 57 / 58 | 清空 m_EolKey 各字段 | m_EolKey(七个子密钥) | EOL 密钥 |
| 20 / 23 / 56 / 65 | 清空 m_EcuCertInfo | m_EcuCertInfo(ecuCa、vid、ecuCertInfo) | ECU 证书信息 |
| 21 / 55 | 清空 m_aesKey | m_aesKey(逐 ECU 填充 AES 密钥) | AES 密钥 |
| 26 | 部分清空 m_VehicleOrderInfo | m_VehicleOrderInfo(完整订单 + clearDataCenterCache()) | 工厂订单信息 |
| 27 / 64 | 清空 m_OtaCert | m_OtaCert(caCert + clientCert) | OTA 证书 |
| 28 | — | m_summaryData(完整工位测试结果树) | 工位测试汇总 |
| 51 | 清空 strSwpn | m_VehicleOrderInfo.strSwpn | 售后软件版本 |
| 53 | — | m_VehicleOrderInfo.strCCPCode | 售后 CCP |
| 54 | 清空 vecListEcu | m_VehicleOrderInfo.vecListEcu(含 ECU ID→名称映射) | 售后 ECU 清单 |
| 68 | — | m_VehicleOrderInfo.strCCPCode | CCP 任务号查询附带 |
| 79 | 清空 m_vecEbocInfo | m_vecEbocInfo(完整 EBOC 列表) | EBOC 特性 |
| 88 | 清空 vecListEcuNew | m_VehicleOrderInfo.vecListEcuNew | 新版 ECU 清单 |
关键设计原则:每个 callServer 请求类型在 preHandler 中会先清空对应的 DataCenter 字段,然后发起 HTTP 请求,成功后在 affterHandler 中重新填充。这确保了每次调用都基于最新的服务器数据,避免残留旧数据造成业务逻辑错误。同时 m_callServerRecord[iType] 会在请求过程中自动记录完整的请求/响应信息,为问题排查提供审计追踪能力。
Sources: [jidu_client.cpp](src/jidu_client.cpp#L650-L820 preHandler), [jidu_client.cpp](src/jidu_client.cpp#L822-L1120 affterHandler)
跨模块交互关系
DataCenter 作为全局数据中枢,与几乎所有核心模块都存在数据依赖关系:
flowchart LR
subgraph 云端通信层
JC[JiduClient]
end
subgraph DataCenter
DC[DataCenter 单例]
end
subgraph 数据消费层
OI[jidu_orderInfo.cpp<br/>订单信息查询 API]
VI[jidu_vehicleInfo.cpp<br/>车辆信息查询 API]
FL[jidu_flasher.cpp<br/>刷写任务编排]
DT[jidu_dtc.cpp<br/>DTC 故障码管理]
RP[jidu_report.cpp<br/>诊断报告生成]
end
subgraph 基础设施层
EV[EnvConfig<br/>环境配置]
VC[CVehicleConfig<br/>整车软件包配置]
end
JC -->|callServer_affterHandler 填充| DC
JC -->|读取 factory/language/deviceSN| DC
OI -->|读取 m_VehicleOrderInfo / m_vecEbocInfo / m_EcuCertInfo / m_OtaCert / m_EolKey / m_aesKey / m_deviceSN| DC
VI -->|读取 m_vehicleInfo / m_VehicleOrderInfo| DC
FL -->|读取 m_EcuStatus / m_vehicleInfo / m_VehicleOrderInfo| DC
DT -->|读取 m_mapDtcDatabase| DC
RP -->|读取 m_summaryData / m_callServerRecord| DC
EV -->|读取 factory| DC
VC -->|读取 m_VehicleOrderInfo| DC- JiduClient 是数据的写入者:每次
callServer成功后通过affterHandler填充 DataCenter 的对应字段,同时也从 DataCenter 读取factory(用于服务器环境路由选择)、language(作为 API 查询参数)、m_deviceSN(用于请求签名和设备标识)、m_vehicleInfo.powerType和m_vehicleInfo.carTypeId(用于 PKI 接口请求参数补充)。 - jidu_orderInfo.cpp 是最大的读取者:其所有
get_jd_orderInfo_*API 函数均直接引用DataCenter::m_VehicleOrderInfo的静态成员,提供 VIN、CCP、ECU 清单、软件版本、EBOC 等查询能力。 - jidu_vehicleInfo.cpp 同时依赖 DataCenter 的
m_vehicleInfo(实时车辆状态)和m_VehicleOrderInfo(服务器订单),并在get_jd_ecuName_byIpAddr()中调用getCarConfig()来解析车辆配置以确定正确的 ECU 名称变体。 - jidu_flasher.cpp 通过
DataCenter::m_EcuStatus跟踪每个 ECU 的刷写状态(在线/离线/需升级等),通过m_vehicleInfo判断高压平台,通过m_VehicleOrderInfo获取订单 ECU 清单。 setFactory()是 DataCenter 初始化的关键入口:它不仅设置DataCenter::factory,还触发JiduClient::loadEnvirment()和loadParam()以重新加载对应工厂环境的服务器地址和证书,以及初始化CVehicleConfig单例。
Sources: jidu.cpp, [jidu_client.cpp](src/jidu_client.cpp#L293-L310 diag URL 构造), jidu_vehicleInfo.cpp
设计评注与潜在改进点
DataCenter 的设计体现了典型的全局状态容器(God Object) 模式,在嵌入式诊断工具的场景下具有快速开发和低访问成本的优点,但也存在几个需要注意的设计特征:
- 静态成员与实例成员混用:
m_EcuSafetyConstant是唯一的非 static 数据成员,需要通过getInstance()->setEcuSafetyConstant()访问,而其他所有静态成员可直接通过DataCenter::访问。这种不一致可能导致新开发者困惑。 setEcuSafetyConstant()返回值语义异常:方法体内执行了赋值操作后始终return false,这可能是一个未完成的实现或故意的标记。reset()的覆盖范围有限:当前仅清理m_vehicleInfo.mapEcuInfo、vecEcuName和vecEcuNameOrderByInitial,但clearDataCenterCache()在callServer(26)时调用reset()后还需结合preHandler中的字段级清理来完成完整的数据刷新。
Sources: jidu_dataCenter.cpp, jidu_dataCenter.cpp
延伸阅读
理解 DataCenter 的数据来源后,建议按以下路径深入:
- JiduClient:与集度服务器的 HTTPS 双向认证通信客户端 — DataCenter 数据的主要写入者,了解服务器通信机制
- callServer 接口设计:请求类型路由、预处理/后处理与脱敏机制 — 深入理解 iType 路由与 DataCenter 的数据填充时序
- CVehicleConfig:整车软件包配置解析与刷写文件匹配策略 — 与 DataCenter 协同完成 ECU 版本匹配
- EnvConfig:多环境(Dev/Test/Staging/Prod)配置切换机制 — 理解
factory字段如何影响环境路由 - 证书管理:P12 解析、OTA 证书与 ECU 级安全常量获取 —
m_EcuCertInfo和m_OtaCert的消费端