创建仓库
This commit is contained in:
29
系统基座文件/2/2.5/2.5 数据结构定义与序列化规范.md
Normal file
29
系统基座文件/2/2.5/2.5 数据结构定义与序列化规范.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
tags: []
|
||||
aliases:
|
||||
- 2.5 数据结构定义与序列化规范 (Data Structure Definition & Serialization Specification)
|
||||
date created: 星期一, 十一月 24日 2025, 5:21:10 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 5:21:21 下午
|
||||
---
|
||||
|
||||
# 2.5 数据结构定义与序列化规范 (Data Structure Definition & Serialization Specification)
|
||||
|
||||
- **覆盖范围**:定义系统内外部数据交互的**静态契约**。该规范严格区分 **“内部原生对象(In-Memory Native Objects)”** 与 **“外部传输契约(On-Wire Contracts)”**,并界定两者之间的**转换边界**。内部关注极致的计算性能(SIMD 对齐、零拷贝),外部关注跨语言/跨平台的互操作性(Protobuf)。
|
||||
- **2.5.1 内部高性能业务对象模型 (Internal High-Performance Business Object Model)**
|
||||
- **核心指向**:定义在 `DataReceiver` -> `SignalProcessor` -> `DataProcessor` 流水线中流转的 C++ 原生结构体(DTO)。涵盖 `DetectionResult`(点迹)和 `TrackData`(航迹)的内存布局设计,强制使用 **POD (Plain Old Data)** 类型,并应用 `alignas(16/32)` 以适配 **SIMD (AVX/NEON)** 向量化指令优化,严禁在核心计算路径上使用虚函数或复杂对象。
|
||||
- **2.5.2 内部控制事件模式定义 (Internal Control Event Schema Definition)**
|
||||
- **核心指向**:定义在 `EventBus` 上流转的控制信令结构。所有事件必须继承自 `BaseEvent`,并强制包含 **全链路追踪 ID (`TraceID`)** 和 **高精度时间戳**。事件负载(Payload)必须保持轻量(通常仅包含状态码、配置键值对或对象 ID),严禁携带大块业务数据(如 I/Q 波形),以保障控制平面的低延迟响应。
|
||||
- **2.5.3 外部数据交换契约 (External Data Exchange Contract)**
|
||||
- **核心指向**:定义系统向外部(显控终端、API 网关)输出数据的接口定义语言 (IDL)。强制选用 **Google Protobuf (v3)** 作为唯一标准。涵盖 `.proto` 文件的版本管理规范(语义化版本控制),以及字段的 **向前/向后兼容性** 设计原则(如使用 `optional` 字段,保留 `reserved` 标识符),确保前后端可独立演进。
|
||||
- **2.5.4 零拷贝数据容器规范 (Zero-Copy Data Container Specification)**
|
||||
- **核心指向**:定义承载内部业务对象的通用包装器 `DataPacket<T>`。涵盖其 **Header** 的标准化元数据(序列号、源模块、TraceID),以及 **Payload** 的所有权管理机制——必须使用 `std::unique_ptr` 配合 **自定义删除器 (Custom Deleter)**,以实现内存块在生命周期结束时的自动归还(回收到 `MemoryPool`),彻底消除内存泄漏风险。
|
||||
- **2.5.5 序列化边界与映射策略 (Serialization Boundary & Mapping Strategy)**
|
||||
- **核心指向**:定义“内部对象”转换为“外部格式”的**唯一合法位置**。明确规定 **仅在 `DisplayController`(数据网关)** 和 **`ApiCommandService`(API 响应)** 处进行序列化操作。涵盖从 C++ Struct 到 Protobuf Message 的字段映射逻辑(Mapping Logic),以及在边界处进行 **数据清洗与脱敏** 的安全规范。
|
||||
|
||||
---
|
||||
|
||||
**核心变更点解析:**
|
||||
|
||||
1. **分离定义**:明确将 2.5.1 (内部高性能 C++) 与 2.5.3 (外部 Protobuf) 分开。旧设计可能在内部也混用了序列化对象,这是性能杀手,新设计予以纠正。
|
||||
2. **容器规范**:新增 2.5.4,将 `DataPacket<T>` 和智能指针管理提升为核心规范,这是“零拷贝”落地的关键载体。
|
||||
3. **边界管控**:新增 2.5.5,显式定义“序列化边界”。这是架构师视角的管控点,防止开发人员在内部模块(如信号处理)随意进行序列化导致性能劣化。
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 5:32:36 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 10:39:00 晚上
|
||||
---
|
||||
|
||||
# 2.5.1 内部高性能业务对象模型 (Internal High-Performance Business Object Model)
|
||||
|
||||
基线核心宗旨:“Hardware-First (硬件优先)”。
|
||||
|
||||
内部数据对象的设计不再是为了“方便编程”,而是为了适配 CPU 缓存行 (Cache Line)、迎合 SIMD 指令集 (AVX/NEON) 以及 最小化内存带宽消耗。
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心设计契约 (Design Contracts)
|
||||
|
||||
在定义具体结构体之前,必须确立以下不可逾越的“红线”:
|
||||
|
||||
1. **严格 POD (Plain Old Data)**:
|
||||
|
||||
- 所有业务对象必须是 **Trivial** 和 **Standard-Layout** 的。
|
||||
- **禁止**:虚函数 (`virtual`)、智能指针成员 (`std::shared_ptr`)、堆内存容器成员 (`std::vector`, `std::string`)。
|
||||
- **理由**:确保对象可以被 `memcpy` 直接拷贝(虽然我们用零拷贝,但在某些边界仍需落盘或调试),且在内存中是**连续**的,对 CPU 预取器(Prefetcher)友好。
|
||||
|
||||
2. **强制对齐 (Forced Alignment)**:
|
||||
|
||||
- 利用 C++11 `alignas` 关键字,强制结构体大小为 **16 字节 (128-bit)** 或 **32 字节 (256-bit)** 的倍数。
|
||||
- **理由**:适配 ARM NEON (128-bit) 和 x86 AVX2 (256-bit) 寄存器宽度,允许编译器生成向量化加载/存储指令 (`vld1q`, `vmovaps`),而非逐个标量读写。
|
||||
|
||||
---
|
||||
|
||||
## 2. 业务对象定义基线
|
||||
|
||||
### 2.1 点迹对象:`DetectionResult`
|
||||
|
||||
这是信号处理流水线产出的最高频数据(每秒可能数万个),是 CPU(数据处理模块)的数据密集型计算对象。
|
||||
|
||||
**C++ 结构体定义 (基线)**:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 单个检测点迹 (Plot/Detection)
|
||||
* @note 强制 16 字节对齐,适配 SSE/NEON 128位寄存器
|
||||
* @size 48 Bytes (3个 128-bit 块)
|
||||
*/
|
||||
struct alignas(16) DetectionResult {
|
||||
// Block 0: 空间坐标 (16 Bytes) -> 可以直接加载到一个向量寄存器
|
||||
float range; // 距离 (m)
|
||||
float azimuth; // 方位角 (rad)
|
||||
float elevation; // 俯仰角 (rad)
|
||||
float velocity; // 径向速度 (m/s)
|
||||
|
||||
// Block 1: 信号质量与辅助信息 (16 Bytes)
|
||||
float snr; // 信噪比 (dB)
|
||||
float power; // 信号功率
|
||||
float noise; // 噪声底
|
||||
uint32_t channel_id;// 通道/波束ID (用于区分多源)
|
||||
|
||||
// Block 2: 索引与标识 (16 Bytes)
|
||||
uint32_t range_idx; // 距离门索引
|
||||
uint32_t doppler_idx; // 多普勒索引
|
||||
uint32_t batch_id; // 所属的 CPI 批次 ID
|
||||
uint32_t padding; // [显式填充] 保持 16B 对齐
|
||||
};
|
||||
```
|
||||
|
||||
**设计决策辩护**:
|
||||
|
||||
- **Float vs Double**:选用 `float` (32-bit)。对于雷达点迹处理,单精度浮点数精度通常已足够,且相比 `double` 能让 SIMD 吞吐量翻倍(一个 AVX2 寄存器处理 8 个 float vs 4 个 double)。
|
||||
- **Block 0 布局**:将 `r, azi, ele, v` 紧凑排列,使得坐标转换算法(极坐标转笛卡尔坐标)可以一次加载 `Block 0` 并执行向量运算。
|
||||
- **Explicit Padding**:末尾增加 `padding` 字段,不仅为了凑齐 48 字节,更是为了防止隐式填充导致的内存脏数据风险。
|
||||
|
||||
### 2.2 航迹对象:`TrackData`
|
||||
|
||||
这是数据处理模块的核心状态对象,用于卡尔曼滤波等矩阵运算。此处的改动最大:**我们废弃了旧文档中 `std::vector` 的设计,改为定长数组**,以符合 POD 约束。
|
||||
|
||||
**C++ 结构体定义 (基线)**:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 单个航迹状态 (Track)
|
||||
* @note 强制 32 字节对齐,适配 AVX2 256位寄存器
|
||||
* @note 假设使用 6维状态向量 (x, y, z, vx, vy, vz)
|
||||
*/
|
||||
struct alignas(32) TrackData {
|
||||
// --- Header (32 Bytes) ---
|
||||
uint64_t track_id; // 全局唯一航迹 ID
|
||||
uint64_t timestamp_us; // 更新时间戳
|
||||
uint32_t status; // 状态枚举 (Tentative/Confirmed)
|
||||
uint32_t hit_count; // 关联次数
|
||||
uint32_t miss_count; // 丢失次数
|
||||
uint32_t station_id; // 源站 ID (分布式新增)
|
||||
|
||||
// --- State Vector (32 Bytes) ---
|
||||
// 6维状态向量,最后补 2 个 float padding 凑齐 8 个 float (256-bit)
|
||||
// 便于 AVX2 一次加载整个状态向量
|
||||
float state[8]; // [x, y, z, vx, vy, vz, pad, pad]
|
||||
|
||||
// --- Covariance Matrix (Diagonal/Simplified) ---
|
||||
// 这是一个设计折中。完整的 6x6 协方差矩阵需要 36*4 = 144 Bytes。
|
||||
// 为了保持对象轻量,这里仅存储对角线元素 (方差),
|
||||
// 或者如果内存允许,展开存储下三角矩阵。
|
||||
// 此处演示存储完整对角线 + 填充 (32 Bytes)
|
||||
float covariance_diag[8]; // [var_x, var_y, var_z, …, pad, pad]
|
||||
|
||||
// --- Quality & Classification (32 Bytes) ---
|
||||
float prob_target; // 目标存在概率
|
||||
float maneuver_indicator;// 机动指示器
|
||||
uint32_t type_label; // 分类标签
|
||||
uint32_t _reserved[5]; // 预留空间,保持缓存行对齐
|
||||
};
|
||||
```
|
||||
|
||||
**设计决策辩护**:
|
||||
|
||||
- **定长数组 (`float state[8]`)**:替代 `std::vector`。
|
||||
- **收益**:内存绝对连续。卡尔曼滤波预测步骤中,CPU 预取器可以直接将 `state` 和 `covariance` 拉入 L1 缓存,消除指针跳转(Pointer Chasing)开销。
|
||||
- **对齐技巧**:6 维向量补齐到 8 维,完美契合 AVX2 (256-bit = 8 floats)。这意味着向量加减法只需一条指令。
|
||||
- **分块对齐**:每个数据块(Header, State, Covariance)都对齐到 32 字节。这确保了字段不会跨越缓存行(Cache Line Split),消除了跨行访问的惩罚。
|
||||
|
||||
---
|
||||
|
||||
## 3. 容器与内存管理策略
|
||||
|
||||
定义了“原子”结构体后,还需要定义装载它们的“容器”。
|
||||
|
||||
### 3.1 容器选型:`std::vector` + 对齐分配器
|
||||
|
||||
在 `DataPacket<T>` 的 Payload 中,我们不存储单个对象,而是存储对象的数组。
|
||||
|
||||
```cpp
|
||||
// 定义专属分配器,确保 vector 的底层内存首地址也是 32 字节对齐的
|
||||
template <typename T>
|
||||
using AlignedVector = std::vector<T, boost::alignment::aligned_allocator<T, 32>>;
|
||||
|
||||
// 最终在 DataPacket 中的 Payload 类型
|
||||
using DetectionList = AlignedVector<DetectionResult>;
|
||||
using TrackList = AlignedVector<TrackData>;
|
||||
```
|
||||
|
||||
- 为什么需要 Aligned Allocator?
|
||||
|
||||
普通的 std::vector 只保证元素大小对齐,不保证数组首地址对齐(尽管现代 glibc 通常对齐到 16B)。为了让第一条 AVX 指令(vmovaps)安全执行,首地址必须严格对齐。
|
||||
|
||||
### 3.2 内存布局图示 (AoS 布局)
|
||||
|
||||
在内存中,一批点迹的物理视图如下。这种 **Array of Structures (AoS)** 布局对于“逐个点迹处理”的逻辑(如卡尔曼滤波的 Update 步骤)是非常缓存友好的。
|
||||
|
||||
```plaintext
|
||||
| <--- Cache Line 64B ---> | <--- Cache Line 64B ---> |
|
||||
[ Det 0 (48B) ] [ Det 1 (48B) ] [ Det 2 (48B) ] [ … ]
|
||||
^ ^ ^
|
||||
Align(16) Align(16) Align(16)
|
||||
```
|
||||
|
||||
- **注意**:由于 48B 不是 64B 的约数,`Det 1` 会跨越缓存行边界。
|
||||
- **优化选项 (SoA)**:如果在某些极端性能场景下(如纯粹的坐标转换),我们可以考虑 **Structure of Arrays (SoA)**,即 `vector<float> range_list, azimuth_list…`。但考虑到代码复杂度和卡尔曼滤波的逻辑特性(通常需要同时访问一个点的所有属性),**AoS (上述结构体)** 是当前工程最佳平衡点。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**2.5.1 章节基线** 已确立为:
|
||||
|
||||
1. **Strict POD**:摒弃 `std::vector` 成员,全面拥抱定长数组。
|
||||
2. **SIMD Alignment**:`DetectionResult` 16B 对齐,`TrackData` 32B 对齐。
|
||||
3. **Explicit Padding**:显式填充所有空隙,消除未定义行为。
|
||||
4. **Aligned Container**:使用带对齐分配器的 Vector 承载数据。
|
||||
|
||||
这套模型直接服务于 **DataProcessor** 的 CPU 计算效率,确保数据进入算法时是“最可口”的形态。
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
tags: []
|
||||
aliases:
|
||||
- 2.5.2 内部控制事件模式定义 (Internal Control Event Schema Definition)
|
||||
date created: 星期一, 十一月 24日 2025, 10:39:09 晚上
|
||||
date modified: 星期一, 十一月 24日 2025, 10:56:20 晚上
|
||||
---
|
||||
|
||||
# 2.5.2 内部控制事件模式定义 (Internal Control Event Schema Definition)
|
||||
|
||||
**基线核心宗旨**:**“Type-Safe & Traceable (类型安全与可追溯)”**。
|
||||
内部控制事件不跨越进程边界(不涉及 Protobuf 序列化),它们是纯粹的 C++ 运行时对象。设计重心在于利用 C++ 类型系统防止误用,并强制携带全链路追踪上下文。
|
||||
|
||||
-----
|
||||
|
||||
## 1\. 核心设计契约 (Design Contracts)
|
||||
|
||||
1. **强制继承 (Mandatory Inheritance)**:
|
||||
|
||||
- 所有在 `EventBus` 上流转的事件 **必须** 继承自 `BaseEvent`。
|
||||
- **理由**:确保所有事件天然携带 `TraceID` 和 `Timestamp`,实现无死角的审计与追踪。
|
||||
|
||||
2. **轻量级负载 (Lightweight Payload)**:
|
||||
|
||||
- **禁止**:在事件对象中直接嵌入大块内存(如 `vector<float>` 原始波形数据)。
|
||||
- **允许**:状态码、配置参数、资源句柄(智能指针)、元数据摘要。
|
||||
- **理由**:控制平面应保持低延迟。大块数据应通过数据面(DataQueue)流转,控制面仅传递“指向数据的指针”或“操作指令”。
|
||||
|
||||
3. **值语义与移动优化 (Value Semantics & Move Semantics)**:
|
||||
|
||||
- 事件对象默认按**值传递**(Copy/Move)。
|
||||
- 必须支持移动构造(Move Constructor),以便高效地在 `EventBus` 的异步队列中转移。
|
||||
|
||||
-----
|
||||
|
||||
## 2\. 根模式定义 (Root Schema)
|
||||
|
||||
这是所有控制信令的“父类契约”。
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 所有内部控制事件的基类
|
||||
* @note 即使是空事件,大小也至少为 16 字节 (TraceID + Timestamp)
|
||||
*/
|
||||
struct BaseEvent {
|
||||
// --- 上下文核心 (Context Core) ---
|
||||
|
||||
// 全链路追踪 ID (从 TraceContext 自动捕获或继承)
|
||||
uint64_t trace_id;
|
||||
|
||||
// 事件生成时的精确时间 (UTC Microseconds)
|
||||
// 用于计算控制指令在队列中的排队延迟 (Queueing Latency)
|
||||
uint64_t timestamp_us;
|
||||
|
||||
// --- 构造基线 ---
|
||||
BaseEvent() {
|
||||
// 自动注入上下文,确保业务代码无需手动填写
|
||||
trace_id = TraceContext::getCurrentId();
|
||||
timestamp_us = Clock::nowInUs();
|
||||
}
|
||||
|
||||
virtual ~BaseEvent() = default; // 允许作为基类指针传递(如果需要统一日志处理)
|
||||
};
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 3\. 核心事件分类定义 (Concrete Schemas)
|
||||
|
||||
基于 **2.3 章节** 确立的各个控制子系统,我们定义以下四类核心事件。
|
||||
|
||||
### 3.1 生命周期与编排类 (Lifecycle & Orchestration)
|
||||
|
||||
服务于 **2.3.3** 和 **2.3.4**。这类事件对**可靠性**要求最高。
|
||||
|
||||
```cpp
|
||||
// 指令:启动模块
|
||||
struct StartModuleEvent : public BaseEvent {
|
||||
std::string target_module; // 目标模块名
|
||||
// 可选:携带启动参数或配置覆盖
|
||||
// std::map<string, string> overrides;
|
||||
};
|
||||
|
||||
// 回执:模块运行中
|
||||
struct ModuleRunningEvent : public BaseEvent {
|
||||
std::string module_name;
|
||||
};
|
||||
|
||||
// 告警:模块故障 (Rich Context from 2.3.4)
|
||||
struct ModuleFailedEvent : public BaseEvent {
|
||||
std::string module_name;
|
||||
ErrorCode error_code; // 标准化错误码
|
||||
bool is_hardware_fault; // 是否硬件故障 (决定是否熔断)
|
||||
std::string debug_info; // 现场快照 (堆栈、显存状态等)
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 资源保护与仲裁类 (Resource & Safety)
|
||||
|
||||
服务于 **2.3.5**。这类事件对**响应速度**要求最高。
|
||||
|
||||
```cpp
|
||||
// 告警:系统过载 (由监控模块发出)
|
||||
struct SystemOverloadEvent : public BaseEvent {
|
||||
enum class Type { THERMAL, POWER, MEMORY };
|
||||
enum class Level { WARNING, CRITICAL };
|
||||
|
||||
Type type;
|
||||
Level level;
|
||||
float current_value; // e.g., 95.5 (Celsius)
|
||||
};
|
||||
|
||||
// 指令:执行节流 (由调度器发出)
|
||||
struct SetComputeThrottleEvent : public BaseEvent {
|
||||
enum class Level { NO_THROTTLE, LIGHT, HEAVY, SUSPEND };
|
||||
Level level;
|
||||
// 接收方收到后,需立即调整 sleep 策略或丢包策略
|
||||
};
|
||||
```
|
||||
|
||||
### 3.3 配置热更新类 (Configuration Transaction)
|
||||
|
||||
服务于 **2.3.6**。这类事件需要承载**复杂负载**(配置数据)。
|
||||
|
||||
```cpp
|
||||
// 事务:配置变更补丁
|
||||
// 采用 RCU 模式,负载通过 shared_ptr 传递,避免深拷贝
|
||||
struct ValidateConfigEvent : public BaseEvent {
|
||||
uint64_t config_version;
|
||||
// 使用智能指针传递复杂的配置树,确保事件本身轻量
|
||||
std::shared_ptr<const ConfigPatch> patch;
|
||||
};
|
||||
|
||||
// 提交:确认变更
|
||||
struct CommitConfigEvent : public BaseEvent {
|
||||
uint64_t config_version; // 必须匹配 Validate 中的版本
|
||||
};
|
||||
```
|
||||
|
||||
### 3.4 性能遥测类 (Telemetry)
|
||||
|
||||
服务于 **2.3.7**。这类事件**吞吐量最大**,必须极致精简。
|
||||
|
||||
```cpp
|
||||
// 遥测:指标快照
|
||||
struct MetricsUpdateEvent : public BaseEvent {
|
||||
// 使用扁平化 Map 传输快照
|
||||
// Key 命名规范: "metric_name{tag=val,…}"
|
||||
std::unordered_map<std::string, double> metrics;
|
||||
|
||||
// 优化建议:
|
||||
// 如果 std::unordered_map 造成频繁 malloc,
|
||||
// 可考虑使用预分配的 vector<MetricPair> 或类似 SmallVector 的优化容器
|
||||
};
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 4\. 模式设计自检 (Design Checklist)
|
||||
|
||||
| 检查项 | 符合标准 | 设计意图 |
|
||||
| :--- | :--- | :--- |
|
||||
| **继承关系** | ✅ Yes | 所有事件均继承自 `BaseEvent`,保证 TraceID 存在。 |
|
||||
| **内存所有权** | ✅ Yes | 复杂对象(如 ConfigPatch)使用 `shared_ptr` 传递,避免总线拷贝大对象。 |
|
||||
| **类型安全** | ✅ Yes | 避免使用 `void*` 或 `EventId` 整数流,利用 C++ 类型系统进行分发。 |
|
||||
| **序列化无关** | ✅ Yes | 结构体中包含 `std::string` 等非 POD 类型,明确不直接用于网络传输(需经由 2.5.3 转换)。 |
|
||||
|
||||
-----
|
||||
|
||||
**总结**:
|
||||
**2.5.2** 确立了控制平面的“通用语言”。这套定义确保了控制指令在进程内传输时:
|
||||
|
||||
1. **可追踪**(自带 TraceID)。
|
||||
2. **低延迟**(避免大内存拷贝)。
|
||||
3. **强类型**(编译期检查错误)。
|
||||
|
||||
这为 2.3 节定义的所有控制逻辑提供了坚实的数据结构支撑。
|
||||
209
系统基座文件/2/2.5/2.5.3 外部数据交换契约 (External Data Exchange Contract).md
Normal file
209
系统基座文件/2/2.5/2.5.3 外部数据交换契约 (External Data Exchange Contract).md
Normal file
@@ -0,0 +1,209 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 11:09:27 晚上
|
||||
date modified: 星期一, 十一月 24日 2025, 11:09:40 晚上
|
||||
---
|
||||
|
||||
# 2.5.3 外部数据交换契约 (External Data Exchange Contract)
|
||||
|
||||
**基线核心宗旨**:**“Contract First, Backwards Compatible (契约优先,向后兼容)”**。
|
||||
外部接口定义语言 (IDL) 一旦发布,即视为法律条文,严禁随意修改。任何变更必须遵循严格的版本控制和兼容性法则,以确保服务器升级不会导致显控终端崩溃。
|
||||
|
||||
-----
|
||||
|
||||
## 1\. 核心设计契约 (Design Contracts)
|
||||
|
||||
1. **唯一标准 (Single Standard)**:
|
||||
|
||||
- 强制使用 **Google Protocol Buffers v3 (proto3)**。
|
||||
- **理由**:proto3 移除了 `required` 字段,天然支持向后兼容(缺失字段使用默认值),极其适合迭代快速的分布式系统。
|
||||
|
||||
2. **包版本化 (Package Versioning)**:
|
||||
|
||||
- 所有 `.proto` 文件必须定义在带版本的包命名空间下,例如 `package radar.external.v1;`。
|
||||
- **理由**:当发生破坏性变更(Breaking Change)时,可以通过引入 `v2` 包来实现并存,而不是破坏现有 `v1` 客户端。
|
||||
|
||||
3. **显式类型 (Explicit Typing)**:
|
||||
|
||||
- 时间戳必须显式注明单位(后缀 `_us` / `_ms`)。
|
||||
- 枚举值(Enum)的第一个字段必须是 `UNKNOWN` 或 `UNSPECIFIED`(值为 0),作为默认零值安全网。
|
||||
|
||||
-----
|
||||
|
||||
## 2\. 协议文件组织 (File Organization)
|
||||
|
||||
为了规范管理,`.proto` 文件应按以下目录结构组织,并作为独立的构建目标(Target):
|
||||
|
||||
```text
|
||||
project_root/
|
||||
└── protos/
|
||||
└── radar/
|
||||
└── external/
|
||||
└── v1/
|
||||
├── common.proto # 共享基础类型 (Point, Vector3)
|
||||
├── track_data.proto # 核心业务数据 (Track, Plot, Batch)
|
||||
└── system_status.proto # 系统健康状态
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 3\. 核心模式定义基线 (Schema Baseline)
|
||||
|
||||
基于 **2.4.2** 确立的逻辑结构,将其固化为工程级的 Protobuf 定义。
|
||||
|
||||
### 3.1 基础类型定义 (`common.proto`)
|
||||
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
package radar.external.v1;
|
||||
|
||||
// WGS84 地理坐标 (用于多站融合)
|
||||
message GeoPoint {
|
||||
double latitude = 1; // 纬度 (度)
|
||||
double longitude = 2; // 经度 (度)
|
||||
double altitude = 3; // 海拔 (米)
|
||||
}
|
||||
|
||||
// 笛卡尔状态向量 (用于高精度跟踪)
|
||||
message StateVector {
|
||||
// ECEF 或 站心坐标系,具体由配置约定
|
||||
double pos_x = 1;
|
||||
double pos_y = 2;
|
||||
double pos_z = 3;
|
||||
double vel_x = 4;
|
||||
double vel_y = 5;
|
||||
double vel_z = 6;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 核心业务数据 (`track_data.proto`)
|
||||
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
package radar.external.v1;
|
||||
|
||||
import "radar/external/v1/common.proto";
|
||||
import "radar/external/v1/system_status.proto";
|
||||
|
||||
// 2.4.2 确立的原子批次
|
||||
message TrackDataBatch {
|
||||
// --- Header ---
|
||||
uint32 station_id = 1; // 站台ID
|
||||
uint64 batch_sequence_id = 2; // 批次序号
|
||||
uint32 checksum = 3; // CRC32c 校验和
|
||||
uint64 timestamp_us = 4; // UTC 时间戳 (微秒)
|
||||
uint64 trace_id = 5; // 全链路追踪ID
|
||||
uint32 throttle_level = 6; // 当前热节流等级 (0-3)
|
||||
|
||||
// --- Payload ---
|
||||
repeated TrackMessage tracks = 7; // 确认航迹
|
||||
repeated PlotMessage plots = 8; // 原始点迹 (可被剪裁)
|
||||
|
||||
// 系统状态快照 (可选,通常低频发送或仅在变更时发送)
|
||||
SystemStatusMessage system_status = 9;
|
||||
}
|
||||
|
||||
message TrackMessage {
|
||||
uint64 track_id = 1;
|
||||
|
||||
enum Status {
|
||||
STATUS_UNSPECIFIED = 0;
|
||||
STATUS_TENTATIVE = 1;
|
||||
STATUS_CONFIRMED = 2;
|
||||
STATUS_COAST = 3;
|
||||
STATUS_LOST = 4;
|
||||
}
|
||||
Status status = 2;
|
||||
|
||||
StateVector state = 3; // 运动状态
|
||||
repeated float covariance = 4; // 协方差矩阵 (对角线或下三角)
|
||||
|
||||
// 扩展属性
|
||||
float probability = 5; // 存在概率
|
||||
int32 classification = 6; // 目标分类 ID
|
||||
}
|
||||
|
||||
message PlotMessage {
|
||||
uint32 batch_id = 1; // 关联的 CPI ID
|
||||
uint32 range_idx = 2;
|
||||
uint32 doppler_idx = 3;
|
||||
float snr = 4;
|
||||
float azimuth_rad = 5;
|
||||
float range_m = 6;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 系统状态定义 (`system_status.proto`)
|
||||
|
||||
这是显控端感知服务器“健康度”的依据。
|
||||
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
package radar.external.v1;
|
||||
|
||||
message SystemStatusMessage {
|
||||
enum HealthState {
|
||||
HEALTH_UNKNOWN = 0;
|
||||
HEALTH_OK = 1;
|
||||
HEALTH_DEGRADED = 2; // 降级 (如热节流中)
|
||||
HEALTH_CRITICAL = 3; // 严重故障 (部分模块离线)
|
||||
}
|
||||
HealthState overall_health = 1;
|
||||
|
||||
// 关键资源指标
|
||||
float gpu_temp_celsius = 2;
|
||||
float gpu_util_percent = 3;
|
||||
float mem_usage_percent = 4;
|
||||
|
||||
// 模块级状态 (简报)
|
||||
map<string, string> module_states = 5; // e.g. {"SignalProcessor": "RUNNING"}
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 4\. 演进与兼容性法则 (Evolution Laws)
|
||||
|
||||
在项目全生命周期中,**严禁**违反以下法则:
|
||||
|
||||
1. **禁止修改 Tag (Never Change Tags)**:
|
||||
|
||||
- 一旦字段 `uint32 station_id = 1;` 发布,Tag `1` 永远属于 `station_id`。即使该字段被废弃,Tag `1` 也不能分配给新字段。
|
||||
|
||||
2. **保留机制 (Reserved Strategy)**:
|
||||
|
||||
- 当删除字段时,必须使用 `reserved` 关键字锁定 Tag 和 Name,防止未来误用。
|
||||
- *示例*:
|
||||
|
||||
```protobuf
|
||||
message TrackMessage {
|
||||
// uint32 old_field = 5; // Deleted
|
||||
reserved 5;
|
||||
reserved "old_field";
|
||||
}
|
||||
```
|
||||
|
||||
3. **默认值假设 (Default Value Assumption)**:
|
||||
|
||||
- 接收端(显控)**不能**依赖字段的存在性(`has_field`),必须处理字段缺失(即值为 0/false/empty)的情况。
|
||||
- *设计推论*:如果 `0` 具有特殊业务含义(如 `range = 0` 表示雷达正中心),则必须小心。通常建议业务值的有效范围避开 `0`,或者使用 `oneof` 包装以获得存在性检查能力(但这会增加开销,非必要不推荐)。
|
||||
|
||||
-----
|
||||
|
||||
## 5\. 构建集成规范 (Build Integration)
|
||||
|
||||
- **代码生成**:使用 CMake 的 `protobuf_generate_cpp` 命令,在构建时自动生成 `.pb.h` 和 `.pb.cc`。
|
||||
- **不可修改**:生成的 C++ 代码严禁人工修改,且不应提交到 Git 仓库(应作为构建产物)。
|
||||
- **库依赖**:对外发布 SDK 时,应提供编译好的 `.proto` 文件或预编译的静态库,而不是让第三方依赖具体的 Protobuf 版本(尽量减少 DLL Hell)。
|
||||
|
||||
-----
|
||||
|
||||
## 总结
|
||||
|
||||
**2.5.3 章节基线** 已确立为:
|
||||
|
||||
1. **Standard**: Protobuf v3, Semantic Versioning (`v1`).
|
||||
2. **Schema**: `TrackDataBatch` (Atomic), `GeoPoint` (WGS84).
|
||||
3. **Compatibility**: Strict `reserved` policy, Explicit Zero Values.
|
||||
|
||||
这套契约不仅定义了数据格式,更定义了**团队协作的边界**——后端开发人员在修改 `.proto` 文件时,必须像修改法律条文一样谨慎。
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 11:19:42 晚上
|
||||
date modified: 星期一, 十一月 24日 2025, 11:20:43 晚上
|
||||
---
|
||||
|
||||
# 2.5.4 零拷贝数据容器规范 (Zero-Copy Data Container Specification)
|
||||
|
||||
**基线核心宗旨**:**“RAII for Ownership, Header for Context (RAII 管资产,Header 管上下文)”**。
|
||||
容器必须是 **Movable-Only (仅可移动)** 的,严禁拷贝。它利用 C++ 类型系统强制执行所有权的单一性,并通过标准化的 Header 确保全链路的可观测性。
|
||||
|
||||
-----
|
||||
|
||||
## 1\. 核心设计契约 (Design Contracts)
|
||||
|
||||
1. **仅移动语义 (Move-Only Semantics)**:
|
||||
|
||||
- `DataPacket<T>` 的拷贝构造函数和拷贝赋值操作符必须被 **显式删除 (`= delete`)**。
|
||||
- **理由**:防止开发者无意中触发深拷贝,破坏零拷贝原则;防止多个对象持有同一块物理内存的所有权,导致 Double Free。
|
||||
|
||||
2. **统一元数据 (Unified Metadata)**:
|
||||
|
||||
- 所有流转的数据包,无论其负载是原始波形还是航迹,都必须携带相同的 Header 结构。
|
||||
- **理由**:使得中间件(如调度器、监控探针)可以在不解析 Payload 的情况下读取 `TraceID` 和 `Timestamp`。
|
||||
|
||||
3. **自动回收 (Self-Reclamation)**:
|
||||
|
||||
- 容器析构时,必须自动触发 Payload 的资源释放(归还内存池或释放堆内存)。
|
||||
- **理由**:消除内存泄漏风险,简化模块内的错误处理逻辑(异常安全)。
|
||||
|
||||
-----
|
||||
|
||||
## 2\. 通用容器定义 (Generic Definition)
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 通用零拷贝数据容器
|
||||
* @tparam PayloadType 负载类型 (必须支持移动语义)
|
||||
*/
|
||||
template <typename PayloadType>
|
||||
struct DataPacket {
|
||||
// --- 1. 标准化头部 (Fixed Header) ---
|
||||
struct Header {
|
||||
// 全链路追踪 ID (从 TraceContext 继承)
|
||||
uint64_t trace_id;
|
||||
|
||||
// 数据生成时间戳 (UTC Microseconds, 总控授时)
|
||||
uint64_t timestamp_us;
|
||||
|
||||
// 流水线序列号 (用于检测内部丢包/乱序)
|
||||
uint64_t sequence_id;
|
||||
|
||||
// 源模块标识 (用于调试拓扑)
|
||||
uint32_t source_module_id;
|
||||
|
||||
// 标志位 (e.g., END_OF_STREAM, CONFIG_UPDATE_BARRIER)
|
||||
uint32_t flags;
|
||||
} header;
|
||||
|
||||
// --- 2. 业务负载 (Flexible Payload) ---
|
||||
PayloadType payload;
|
||||
|
||||
// --- 3. 构造与移动语义 ---
|
||||
|
||||
// 默认构造
|
||||
DataPacket() = default;
|
||||
|
||||
// 显式移动构造
|
||||
DataPacket(DataPacket&& other) noexcept
|
||||
: header(other.header), payload(std::move(other.payload)) {}
|
||||
|
||||
// 显式移动赋值
|
||||
DataPacket& operator=(DataPacket&& other) noexcept {
|
||||
if (this != &other) {
|
||||
header = other.header;
|
||||
payload = std::move(other.payload);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 严禁拷贝 !!!
|
||||
DataPacket(const DataPacket&) = delete;
|
||||
DataPacket& operator=(const DataPacket&) = delete;
|
||||
};
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 3\. 核心特化与所有权管理 (Specializations & Ownership)
|
||||
|
||||
根据 **2.5.1** 定义的业务对象,我们需要针对两类不同性质的数据定义具体的容器行为。
|
||||
|
||||
### 3.1 原始回波数据包 (`RawDataPacket`)
|
||||
|
||||
- **场景**:`DataReceiver` -\> `SignalProcessor`。数据量极大(MB 级),必须使用页锁定内存池。
|
||||
- **Payload 类型**:`std::unique_ptr<DataObject, MemoryPoolDeleter>`。
|
||||
- **所有权逻辑**:
|
||||
- `RawDataPacket` 持有 `unique_ptr`。
|
||||
- 当 Packet 在队列中移动时,`unique_ptr` 随之移动。
|
||||
- 当 Packet 在消费端(`SignalProcessor`)析构时,`MemoryPoolDeleter` 被调用,将底层的 `void*` 归还给 `PinnedMemoryPool`。
|
||||
|
||||
<!-- end list -->
|
||||
|
||||
```cpp
|
||||
// 定义特定的删除器
|
||||
struct MemoryPoolDeleter {
|
||||
IMemoryPool* pool;
|
||||
void operator()(void* ptr) const {
|
||||
if (pool && ptr) pool->release(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// 别名定义
|
||||
using RawPayload = std::unique_ptr<DataObject, MemoryPoolDeleter>;
|
||||
using RawDataPacket = DataPacket<RawPayload>;
|
||||
```
|
||||
|
||||
### 3.2 结果数据包 (`DetectionPacket` / `TrackPacket`)
|
||||
|
||||
- **场景**:`SignalProcessor` -\> `DataProcessor` -\> `DisplayController`。数据量较小(KB 级),使用堆内存或对齐分配器。
|
||||
- **Payload 类型**:`std::vector<DetectionResult>` (Aligned)。
|
||||
- **所有权逻辑**:
|
||||
- `std::vector` 本身就是 RAII 容器。
|
||||
- `DataPacket` 的移动会自动触发 `vector` 的移动(仅交换内部指针 `begin/end/capacity`),成本极低(3 个指针大小)。
|
||||
- **注意**:此处不需要自定义删除器,除非我们想引入“对象池”来复用 vector 的内存(对于 100Hz 的频率,`std::vector` 的默认分配器性能通常可以接受,暂不引入对象池以降低复杂度)。
|
||||
|
||||
<!-- end list -->
|
||||
|
||||
```cpp
|
||||
// 别名定义
|
||||
// 使用 2.5.1 定义的对齐容器
|
||||
using DetectionPayload = AlignedVector<DetectionResult>;
|
||||
using DetectionPacket = DataPacket<DetectionPayload>;
|
||||
|
||||
using TrackPayload = AlignedVector<TrackData>;
|
||||
using TrackPacket = DataPacket<TrackPayload>;
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 4\. 辅助工厂方法 (Factory Helpers)
|
||||
|
||||
为了简化 Header 的填充(防止开发者忘记填 TraceID),应提供工厂函数。
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
DataPacket<T> MakePacket(T&& payload, uint64_t seq_id) {
|
||||
DataPacket<T> packet;
|
||||
|
||||
// 自动捕获当前上下文
|
||||
packet.header.trace_id = TraceContext::getCurrentId();
|
||||
packet.header.timestamp_us = Clock::nowInUs();
|
||||
packet.header.sequence_id = seq_id;
|
||||
packet.header.source_module_id = ModuleContext::getCurrentModuleId();
|
||||
packet.header.flags = 0;
|
||||
|
||||
// 移动负载
|
||||
packet.payload = std::move(payload);
|
||||
|
||||
return packet;
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 总结
|
||||
|
||||
**2.5.4 章节基线** 已确立为:
|
||||
|
||||
1. **Generic Envelope**:统一的 `DataPacket<T>` 模板,强制包含 Header。
|
||||
2. **Move-Only**:通过删除拷贝构造函数,物理上杜绝深拷贝。
|
||||
3. **Hybrid Payload Strategy**:
|
||||
- 大内存(Raw):`unique_ptr` + `CustomDeleter` (Pool)。
|
||||
- 小内存(Result):`std::vector` (Move Semantics)。
|
||||
|
||||
这一设计确保了数据在“集装箱”里流转时,既安全(不会泄露),又轻快(只有指针在动)。
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
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<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`,暂不截断,保持精度。
|
||||
|
||||
### 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<T>`, `unique_ptr` 所有权管理。 |
|
||||
| **2.5.5 转换边界** | **Explicit Converter** | 静态映射类,节流感知,边界清洗。 |
|
||||
Reference in New Issue
Block a user