创建仓库

This commit is contained in:
2025-12-11 07:24:36 +08:00
commit 0d81c1792d
128 changed files with 15104 additions and 0 deletions

View 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,显式定义“序列化边界”。这是架构师视角的管控点,防止开发人员在内部模块(如信号处理)随意进行序列化导致性能劣化。

View File

@@ -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 计算效率,确保数据进入算法时是“最可口”的形态。

View File

@@ -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 节定义的所有控制逻辑提供了坚实的数据结构支撑。

View 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` 文件时,必须像修改法律条文一样谨慎。

View File

@@ -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)。
这一设计确保了数据在“集装箱”里流转时,既安全(不会泄露),又轻快(只有指针在动)。

View File

@@ -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** | 静态映射类,节流感知,边界清洗。 |