6.1 KiB
6.1 KiB
tags, date created, date modified
| tags | date created | date modified |
|---|---|---|
| 星期一, 十一月 24日 2025, 4:21:43 下午 | 星期一, 十一月 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 契约:
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)通常已足够,不必过度优化。
- 优化技巧:对于包含大量数据的字段(如点迹列表),如果在 C++ 端也使用了类似 Protobuf 的内存布局(连续内存),可以使用
5. 节流与数据剪裁 (Throttling & Pruning)
基于 2.3.5 热节流控制,序列化模块必须实现内容感知剪裁:
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 字段。 |