5.6 KiB
5.6 KiB
tags, date created, date modified
| tags | date created | date modified |
|---|---|---|
| 星期一, 十一月 24日 2025, 11:25:58 晚上 | 星期一, 十一月 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 的业务逻辑中。
// 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<radar::ipc::TrackMessage_Status>(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<TrackData>& 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,暂不截断,保持精度。
- 基线决策:目前 2.5.3 定义为
3.3 节流感知清洗 (Throttle-Aware Scrubbing)
-
机制:结合 2.4.4 热节流,转换器应接受
throttle_level参数。 -
逻辑:
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<T>, unique_ptr 所有权管理。 |
| 2.5.5 转换边界 | Explicit Converter | 静态映射类,节流感知,边界清洗。 |