--- tags: [] date created: 星期一, 十一月 24日 2025, 4:21:43 下午 date modified: 星期一, 十一月 24日 2025, 4:38:46 下午 --- # 2.4.2 业务数据序列化规范 (Business Data Serialization Specification) ## 1. 技术选型基线:Protocol Buffers V3 - **基线决策**:继续沿用 **Google Protobuf v3**。 - **论证**:Protobuf 提供的 **Schema Evolution(模式演进)** 能力对于雷达这种生命周期长达数年的系统至关重要。它允许我们在未来添加新的字段(如“目标分类置信度”或“干扰源类型”)而不破坏旧版显控软件的兼容性。 ## 2. 根消息结构定义 (Root Schema Definition) 基于 **TDP-2.4-DIST**(分布式补丁)和 **ECN-2025-001**(显控扁平化),我们需要对 `04_序列化与网络协议.md` 中定义的 `TrackDataBatch` 进行**重大升级**。 以下是最终确立的 `.proto` 契约: ```Protocol Buffers syntax = "proto3"; package radar.ipc; // 2.4.2 核心契约:原子业务数据批次 message TrackDataBatch { // --- 传输层元数据 --- // [分布式核心] 站点/阵面唯一标识 (Station ID) // 必须由配置管理模块注入,用于显控端区分数据源 uint32 station_id = 1; // [丢包检测] 批次序列号 (单调递增) // 仅在单个 Station 内连续。显控端需按 StationID 分别统计丢包率。 uint64 batch_sequence_id = 2; // [完整性] 全链路校验和 (CRC32c) // 覆盖除本字段外的所有数据。 uint32 checksum = 3; // --- 业务层元数据 --- // [时序基准] 数据生成时间戳 (UTC Microseconds) // 必须基于总控授时校正后的软时钟,严禁使用本地 Uptime。 // 显控端据此进行多站数据的时空对齐。 uint64 timestamp_us = 4; // [可观测性] 全链路追踪 ID // 继承自处理该 CPI 数据的 TraceContext uint64 trace_id = 5; // [流控状态] 当前节流等级 (反馈给显控端) // 0=全速, 1=轻微, 2=严重。显控端可据此提示用户"当前数据已降级"。 uint32 throttle_level = 6; // --- 业务负载 (Atomic Payload) --- // 确认航迹 (高价值,节流时优先保留) repeated TrackMessage tracks = 7; // 原始点迹 (低价值,量大,节流时优先丢弃) repeated PlotMessage plots = 8; // 系统状态/心跳信息 (可选,用于带外健康监测) SystemStatusMessage system_status = 9; } // 单个航迹定义 (精简版,详细物理含义见 02_核心数据结构.md) message TrackMessage { uint64 track_id = 1; TrackStatus status = 2; // TENTATIVE, CONFIRMED, COAST, LOST // 状态向量 (位置+速度) // 建议统一使用 WGS84 (经纬高) 或 地心笛卡尔坐标 (ECEF) // 以避免极坐标在分布式融合时的歧义 double pos_x = 3; double pos_y = 4; double pos_z = 5; double vel_x = 6; double vel_y = 7; double vel_z = 8; // 协方差矩阵 (可选,用于高级融合) repeated float covariance = 9; } ``` ## 3. 序列化策略:原子批次 (Atomic Batch) Vs 微批次 (Micro-batch) - **旧设计遗留问题**:在之前的设计讨论中(未实施),为了配合“抢占式调度”,曾考虑将一个 CPI 的数据切碎发送。 - **新基线 (ECN-2025-001)**: - **策略**:**原子批次 (Atomic Batch)**。 - **定义**:一个 Protobuf `TrackDataBatch` 消息 **严格对应一个雷达处理周期 (CPI)** 的所有产出物。 - **行为**:`DisplayController` 等待 `DataProcessor` 输出完整的航迹列表后,一次性打包。 - **收益**:显控端逻辑大幅简化。收到一个包,就是一帧完整的态势图,无需处理“半帧”数据或进行复杂的跨包重组。 ## 4. C++ 与 Protobuf 的映射效率优化 虽然 Protobuf 很快,但从 C++ `struct TrackData` 到 Protobuf `message TrackMessage` 的转换仍然涉及内存拷贝和遍历。为了在 100Hz+ 的刷新率下不成为瓶颈: - **预分配 (Reserve)**:在构建 `TrackDataBatch` 时,根据 C++ `vector` 的大小,预先调用 Protobuf 的 `MutableRepeatedPtrField::Reserve()`,避免多次内存重分配。 - **移动语义 (Move Semantics)**:Protobuf v3 在 C++ 中支持 `std::move`。如果序列化是在独立的 I/O 线程中进行(见 2.4.1),我们可以考虑直接接管数据所有权,但通常 Protobuf 生成的代码需要 Setter 拷贝。 - _优化技巧_:对于包含大量数据的字段(如点迹列表),如果在 C++ 端也使用了类似 Protobuf 的内存布局(连续内存),可以使用 `Arena Allocation` 或自定义反射来加速,但在本系统中,**简单的字段赋值配合编译器优化(-O3)通常已足够,不必过度优化**。 ## 5. 节流与数据剪裁 (Throttling & Pruning) 基于 **2.3.5 热节流控制**,序列化模块必须实现**内容感知剪裁**: ```cpp void DisplayController::serializeAndSend(const TrackDataPacket& packet) { radar::ipc::TrackDataBatch pb_batch; // 1. 填充元数据 (StationID, TraceID, Timestamp…) // 2. 获取当前节流等级 (通过原子变量读取) auto level = throttle_level_.load(); pb_batch.set_throttle_level(level); // 3. 内容剪裁逻辑 if (level < 2) { // Level 0/1: 发送全量数据 for (const auto& plot : packet.plots) { /* 转换点迹 */ } } // 始终发送航迹 (除非 Level 3 暂停) for (const auto& track : packet.tracks) { /* 转换航迹 */ } // 4. 序列化并发送 // … } ``` --- # 总结:2.4.2 基线图谱 | **规范项** | **核心基线** | **变更说明 (vs 旧文档)** | | ------- | ----------------------- | ------------------- | | **格式** | **Protobuf v3** | 保持不变。 | | **标识** | **StationID + TraceID** | 新增 StationID 支持分布式。 | | **时序** | **UTC Timestamp** | 取代本地时间,支持总控同步。 | | **粒度** | **Atomic CPI Batch** | 废弃微批次,简化接收端逻辑。 | | **流控** | **内容剪裁 (Pruning)** | 根据节流等级动态丢弃 Plot 字段。 |