--- tags: [] date created: 星期一, 十一月 24日 2025, 11:25:58 晚上 date modified: 星期一, 十一月 24日 2025, 11:28:29 晚上 --- # 2.5.5 序列化边界与映射策略 (Serialization Boundary & Mapping Strategy) **基线核心宗旨**:**“Strict Boundary, Explicit Mapping (严格边界,显式映射)”**。 严禁在核心计算流水线(Signal/Data Processor)中引入 Protobuf 或 JSON。序列化是 IO 密集型和 CPU 密集型的结合体,必须被隔离在系统的边缘。 ----- ## 1\. 边界定义契约 (Boundary Contracts) 我们确立系统中仅有的两个**合法序列化边界 (Legal Serialization Boundaries)**: ### 1.1 数据面边界:`DisplayController` - **输入**:`TrackDataPacket` (C++ Aligned Vector)。 - **输出**:`TrackDataBatch` (Protobuf Binary)。 - **职责**:高频、批量、单向转换。 - **违规判定**:任何在 `SignalProcessor` 或 `DataProcessor` 中出现 `#include "track_data.pb.h"` 的代码均视为架构违规。 ### 1.2 控制面边界:`ApiCommandService` - **输入**:内部状态(如 `SystemStateMachine::State`)或配置对象。 - **输出**:HTTP Response (JSON/Protobuf)。 - **职责**:低频、按需、双向转换。 ----- ## 2\. 字段映射逻辑 (Mapping Logic) 这是将 **2.5.1 (Internal POD)** 转换为 **2.5.3 (External Proto)** 的具体法则。由于内部使用定长数组 (`float state[8]`) 而外部使用语义化字段 (`pos_x, vel_x`),这里需要显式的转换逻辑。 ### 2.1 映射表 (Mapping Table) | 内部字段 (C++ `TrackData`) | 外部字段 (Proto `TrackMessage`) | 转换逻辑 | | :--- | :--- | :--- | | `track_id` | `track_id` | 直接赋值 | | `status` | `status` | Enum 转换 (C++ Enum -\> Proto Enum) | | `state[0]` | `pos_x` | `state[0]` (单位转换:米 -\> 米) | | `state[1]` | `pos_y` | `state[1]` | | `state[2]` | `pos_z` | `state[2]` | | `state[3]` | `vel_x` | `state[3]` | | `state[4]` | `vel_y` | `state[4]` | | `state[5]` | `vel_z` | `state[5]` | | `state[6.]` (Padding) | **N/A** | **丢弃** (内部对齐填充不导出) | | `covariance_diag[8]` | `covariance` (repeated) | 遍历数组赋值,截断填充部分 | | `_reserved[5]` | **N/A** | **丢弃** (保留字段不导出) | ### 2.2 实现范式:转换器模式 (Converter Pattern) 建议实现一个静态的 `TypeConverter` 类,将转换逻辑集中管理,避免散落在 `DisplayController` 的业务逻辑中。 ```cpp // TypeConverter.h class TypeConverter { public: // 单个对象转换:In-Place 填充以减少内存分配 static void ToProto(const TrackData& src, radar::ipc::TrackMessage* dst) { dst->set_track_id(src.track_id); dst->set_status(static_cast(src.status)); // 展开定长数组 dst->set_pos_x(src.state[0]); dst->set_pos_y(src.state[1]); dst->set_pos_z(src.state[2]); dst->set_vel_x(src.state[3]); dst->set_vel_y(src.state[4]); dst->set_vel_z(src.state[5]); // 协方差:仅拷贝有效数据 for (int i = 0; i < 6; ++i) { dst->add_covariance(src.covariance_diag[i]); } // 其他属性… } // 批量转换 static void ToProtoBatch(const std::vector& src_list, radar::ipc::TrackDataBatch* dst_batch) { // 预分配内存,避免 push_back 导致的扩容 dst_batch->mutable_tracks()->Reserve(src_list.size()); for (const auto& track : src_list) { ToProto(track, dst_batch->add_tracks()); } } }; ``` ----- ## 3\. 数据清洗与脱敏规范 (Sanitization & Scrubbing) 并非所有内部数据都有资格通过“海关”。为了安全和带宽效率,必须执行清洗。 ### 3.1 敏感数据清洗 - **内存地址**:内部调试用的 `void* user_data` 或 `debug_ptr` **绝对禁止**序列化。 - **原始标识符**:如果内部使用了哈希表索引或临时 Slot ID,必须转换为全局唯一的 `UUID` 或 `TrackID`。 ### 3.2 精度截断 (Precision Truncation) - **内部**:为了计算精度,内部可能使用 `double` 或高精度浮点。 - **外部**:若显控仅需显示到厘米级,且带宽紧张,可在转换时进行截断或转为 `float`(Protobuf 中 `double` 占 8 字节,`float` 占 4 字节)。 - *基线决策*:目前 2.5.3 定义为 `double`,暂不截断,保持精度。 ### 3.3 节流感知清洗 (Throttle-Aware Scrubbing) - **机制**:结合 **2.4.4 热节流**,转换器应接受 `throttle_level` 参数。 - **逻辑**: ```cpp static void ToProtoBatch(…, int throttle_level) { if (throttle_level >= 2) { // Level 2: 仅导出确认航迹,且丢弃协方差矩阵以节省带宽 // 这是一个深度优化的例子:在序列化阶段就决定不拷贝某些字段 } } ``` ----- ## 总结:2.5 章节整体基线图谱 至此,**2.5 数据结构定义与序列化规范** 已全部完成。我们构建了一套“内外有别、边界清晰”的数据模型。 | 领域 | 核心基线 | 关键技术点 | | :--- | :--- | :--- | | **2.5.1 内部对象** | **High-Perf POD** | `alignas(32)`, 定长数组,SIMD 友好。 | | **2.5.2 内部事件** | **BaseEvent 继承** | 强制 `TraceID`, 轻量级负载。 | | **2.5.3 外部契约** | **Protobuf v3** | 语义化版本,向后兼容,StationID 支持。 | | **2.5.4 数据容器** | **Move-Only Packet** | `DataPacket`, `unique_ptr` 所有权管理。 | | **2.5.5 转换边界** | **Explicit Converter** | 静态映射类,节流感知,边界清洗。 |