创建仓库
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
---
|
||||
tags: []
|
||||
aliases:
|
||||
- 2.4 外部目标数据分发协议 (External Target Data Distribution Protocol)
|
||||
date created: 星期一, 十一月 24日 2025, 11:40:08 晚上
|
||||
date modified: 星期一, 十一月 24日 2025, 11:40:45 晚上
|
||||
---
|
||||
|
||||
# 2.4 外部目标数据分发协议 (External Target Data Distribution Protocol)
|
||||
|
||||
- **覆盖范围**:定义核心处理服务器(通过 `DisplayController`)向外部独立显控终端分发高实时性业务数据(如航迹、点迹)的**网络通信契约**。鉴于显控端采用轻量级 2D 渲染,本协议不再包含针对 UI 交互的流控逻辑,而是专注于**全速、单向、无阻塞**的数据推送,仅在接收到系统级热保护指令时执行被动节流。
|
||||
- **2.4.1 传输层拓扑与套接字模型 (Transport Layer Topology & Socket Model)**
|
||||
- **核心指向**:定义数据传输的物理载体。采用 **UDP 单播 (Unicast)** 模式,由服务器作为发送方,向单一客户端推送。强制使用 **非阻塞 (Non-blocking) Socket** 配合 `epoll` 边缘触发模式。鉴于已移除 UI 抢占逻辑,Socket 发送缓冲区 (`SO_SNDBUF`) 应配置为**最大可用值**(如 8MB+),以吸收网络抖动,确保在计算核心全速运转时网络层不成为瓶颈。
|
||||
- **2.4.2 业务数据序列化规范 (Business Data Serialization Specification)**
|
||||
- **核心指向**:定义跨网络二进制格式。继续强制使用 **Google Protobuf (v3)**。数据包根对象 `TrackDataBatch` 必须包含**全链路追踪 ID (`TraceID`)**。由于取消了任务切分,数据包的生成频率将与雷达脉冲处理周期(CPI)严格同步,不再出现因被抢占而导致的“微批次(Micro-batch)”碎片化数据包。
|
||||
- **2.4.3 丢包检测与时序完整性机制 (Packet Loss Detection & Sequencing Integrity)**
|
||||
- **核心指向**:定义数据一致性策略。协议头包含单调递增的 **`batch_sequence_id`**。客户端对于乱序包执行**立即丢弃**策略。由于后端不再因 UI 操作而暂停,客户端应预期收到**极其平稳**的数据流;任何超过 2 个周期的静默都应被客户端判定为“网络故障”而非“后端繁忙”,并触发重连告警。
|
||||
- **2.4.4 热节流响应与流量整形 (Thermal Throttling Response & Traffic Shaping)**
|
||||
- **核心指向**:**(基于 ECN 修正)** 定义在系统过热时的降级行为。当 `DisplayController` 收到 `SetComputeThrottleEvent`(热保护指令)时,必须在网络发送层执行**主动丢包**或**发送间隔插入(Gap Insertion)**,以减少网卡中断和总线功耗。例如,在 `Level 2` 节流状态下,仅发送关键航迹数据(Confirmed Tracks),丢弃所有点迹(Plots)和调试数据,从而降低系统整体热负荷。
|
||||
- **2.4.5 端到端延迟遥测 (End-to-End Latency Telemetry)**
|
||||
- **核心指向**:定义性能监控闭环。数据包必须携带 **“数据生成时间戳”**。客户端计算 **Glass-to-Glass Latency** 并回传。此指标现在主要用于监控网络链路质量和散热系统的有效性(即观察热节流是否导致了延迟显著增加),而非用于调节 UI 渲染优先级。
|
||||
|
||||
---
|
||||
|
||||
**变更说明 (基于 ECN-2025-001):**
|
||||
1. **移除**:移除了所有关于“为了 UI 响应性而暂停数据发送”的描述。
|
||||
2. **新增**:**2.4.4 热节流响应**。这是新架构下唯一合法的“主动降速”场景。
|
||||
3. **调整**:在 **2.4.1** 中强调了 Socket 缓冲区配置为“最大值”,因为不再需要担心缓冲区积压影响 UI 线程(UI 线程已与计算/发送线程物理解耦且互不干扰)。
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 4:14:06 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 4:48:23 下午
|
||||
---
|
||||
|
||||
# 2.4.1 传输层拓扑与套接字模型 (Transport Layer Topology & Socket Model)
|
||||
|
||||
## 1. 传输拓扑:UDP 单播直连 (UDP Unicast Direct)
|
||||
|
||||
- **基线决策**:采用 **UDP 单播 (Unicast)**,由 `DisplayController` 绑定本地网卡,直接向配置的显控终端 IP 发送数据。
|
||||
- **设计论证**:
|
||||
- **去中心化**:避免了组播(Multicast)对交换机 IGMP Snooping 配置的依赖,降低部署复杂度。
|
||||
- **无状态**:相比 TCP,UDP 无需维护连接状态(三次握手/四次挥手),消除 `TIME_WAIT` 资源占用,最适合“发后即忘(Fire-and-Forget)”的遥测场景。
|
||||
- **隔离性**:点对点发送天然隔离了网络风暴,若存在多个显控终端,建议通过应用层复制分发(Application-Layer Fanout),而非网络层组播,以实现对不同终端的差异化流控(例如:给指挥屏发全量数据,给监控屏发降级数据)。
|
||||
|
||||
## 2. 套接字模型:非阻塞 + 边缘触发 (Non-blocking + Epoll ET)
|
||||
|
||||
为了支撑高频率(如 100Hz+)的数据刷新而不阻塞业务线程,必须采用全异步 I/O 模型。
|
||||
|
||||
- **模式基线**:**Non-blocking Socket** + **Epoll Edge Triggered (ET)**。
|
||||
- **核心策略:“写优先,等其次” (Write-First, Wait-Later)**
|
||||
- **传统误区**:很多设计是先 `epoll_wait` 等到 `EPOLLOUT` 再写。这在高吞吐场景下是低效的,因为 99% 的时间网络都是可写的,无谓的 `epoll_wait` 增加了系统调用开销。
|
||||
- **基线逻辑**:
|
||||
|
||||
1. **直接发送**:业务线程或发送线程直接调用 `sendto()`。
|
||||
2. **处理拥塞**:仅当 `sendto()` 返回 `EAGAIN` / `EWOULDBLOCK`(内核缓冲区满)时,才将 Socket 加入 `epoll` 监听 `EPOLLOUT` 事件。
|
||||
3. **恢复发送**:当 `epoll` 通知可写时,继续发送剩余数据,并立即移除监听,回归“直接发送”模式。
|
||||
|
||||
## 3. 关键配置参数深度解析 (Parameter Tuning)
|
||||
|
||||
这里我们不硬定“8MB”,而是给出**基于场景的计算逻辑**。
|
||||
|
||||
### 3.1 发送缓冲区 (`SO_SNDBUF`):突发吸收器
|
||||
|
||||
- **设计目标**:吸收 **微突发 (Micro-burst)**。雷达数据通常是脉冲式的,一个 CPI 处理完会瞬间产生大量点迹/航迹数据。Socket 缓冲区必须足够大,能完全容纳“最大可能的单次突发数据量”,让网卡以物理线速慢慢发出去,而不是让应用程序阻塞。
|
||||
- 计算公式:
|
||||
|
||||
$$
|
||||
\text{Target Buffer} = \text{Max\_Burst\_Size} \times \text{Safety\_Factor}$$
|
||||
|
||||
- _Max_Burst_Size_:假设最大工况下,一帧(CPI)产生的点迹 + 航迹 + 回波切片总大小。例如:1000 个点迹 (x 64B) + 200 航迹 (x 128B) + 回波切片 (256KB) $\approx$ 350KB。
|
||||
- _Safety_Factor_:考虑到 OS 调度抖动,可能连续积压 2-3 帧数据。取系数 4。
|
||||
- _计算结果_:$350\text{KB} \times 4 \approx 1.4\text{MB}$。
|
||||
|
||||
- **配置建议**:
|
||||
- **基线值**:动态计算或设置为 **4MB - 8MB**(留有充足余量)。对于现代服务器内存而言,这个开销极低,但收益(零阻塞)巨大。
|
||||
- **实施**:`setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, …)`。注意 Linux 内核会将设置值翻倍以用于元数据,实际可用空间即为设定值。
|
||||
|
||||
### 3.2 服务质量标记 (`IP_TOS` / DSCP):网络特权
|
||||
|
||||
- **设计目标**:告诉交换机和路由器,这股数据流是“VIP”,在网络拥塞时优先转发,最后丢弃。
|
||||
- **参数选择**:
|
||||
- **DSCP (Differentiated Services Code Point)**:推荐使用 **EF (Expedited Forwarding, 0x2E)** 或 **CS6 (0x30)**。这是仅次于网络控制信令(CS7)的最高优先级,通常用于 VoIP 语音或实时控制数据。
|
||||
- **实施**:
|
||||
|
||||
```cpp
|
||||
int tos = 0xB8; // DSCP EF (101110) << 2
|
||||
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
|
||||
```
|
||||
|
||||
### 3.3 路径 MTU 发现 (PMTU Discovery):避免分片
|
||||
|
||||
- **现状**:虽然采集链路用了 JUMBO Frame (9000),但**显控链路通常经过普通交换机或公网,MTU 往往限制在 1500**。
|
||||
- **策略**:
|
||||
- **强制禁止分片 (`IP_MTU_DISCOVER` = `IP_PMTUDISC_DO`)**:让内核在数据包超过 MTU 时直接报错 `EMSGSIZE`,而不是默默分片(IP 分片会严重降低 UDP 可靠性,一个分片丢失导致整个包作废)。
|
||||
- **应用层分包**:`DisplayController` 必须根据实际 MTU(如 1472 字节 Payload)在应用层进行切片(Fragmentation)。这与我们后续 2.4.2 的“Atomic Batch”设计不冲突,一个 Batch 可以被切成多个 UDP 包发送,只要包含相同的 `BatchID` 和 `SequenceID` 即可。
|
||||
|
||||
## 4. 高效线程模型 (The "Pumper" Thread)
|
||||
|
||||
为了彻底隔离网络抖动对计算核心的影响,我们确立 **独立发送线程** 模型。
|
||||
|
||||
- **架构**:
|
||||
- **生产者**:`SignalProcessor` 计算完毕,将 `DetectionResult` 对象放入一个 **SPSC (单生产单消费) 无锁队列**。
|
||||
- **消费者 (DisplayController IO Thread)**:
|
||||
- 绑定到非关键 CPU 核心(如 Core 14)。
|
||||
- 死循环从队列取数据。
|
||||
- 执行 Protobuf 序列化。
|
||||
- 执行 `sendto()`。
|
||||
- 若 `EAGAIN`,则 `epoll_wait()`。
|
||||
- **收益**:
|
||||
- **序列化卸载**:Protobuf 序列化是 CPU 密集型操作,将其移出信号处理主线程,保护了核心计算资源。
|
||||
- **故障隔离**:即使网络断开、Socket 缓冲区塞满,影响的也只是这个 IO 线程(队列满后丢弃新数据),绝不会阻塞 FFT/CFAR 运算。
|
||||
|
||||
---
|
||||
|
||||
## 总结:2.4.1 基线图谱
|
||||
|
||||
|**配置项**|**核心基线**|**设计意图**|
|
||||
|---|---|---|
|
||||
|**拓扑**|**UDP Unicast**|简单、高效、无状态。|
|
||||
|**I/O 模型**|**Non-blocking + Epoll ET**|" 写优先 " 策略,最大化吞吐,最小化系统调用。|
|
||||
|**发送缓冲**|**动态计算 (Max Burst x 4)**|约 4MB-8MB。吸收脉冲式突发,防止应用层阻塞。|
|
||||
|**QoS**|**DSCP EF / CS6**|确保在交换机侧的转发优先级。|
|
||||
|**分片策略**|**禁止 IP 分片 + 应用层切分**|避免 IP 分片重组风险,提升丢包恢复能力。|
|
||||
|**线程模型**|**独立 IO 线程 + SPSC 队列**|隔离网络抖动与序列化开销,保护计算核心。|
|
||||
|
||||
**提问**:您是否确认 **“非阻塞 +Epoll ET”** 模型以及 **“独立线程序列化与发送”** 的架构基线?确认后我们将进入 **2.4.2 业务数据序列化规范**,讨论数据具体怎么“打包”。
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
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 字段。 |
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 4:25:06 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 4:29:22 下午
|
||||
---
|
||||
|
||||
# 2.4.3 丢包检测与时序完整性机制 (Packet Loss Detection & Sequencing Integrity)
|
||||
|
||||
这是显控终端的“网络听诊器”。在单向 UDP 传输模式下,显控端不仅是被动的数据消费者,更是链路质量的唯一评判者。它需要利用 2.4.2 定义的协议头字段,实时诊断出“谁丢包了”、“延迟多少”、“服务器是否过热”,并将这些隐性故障转化为显性的运维指标。
|
||||
|
||||
## 一、 约束输入与对齐 (Constraints & Alignment)
|
||||
|
||||
基于 TDP-2.4-DIST(分布式补丁)和 ECN-2025-001(显控扁平化),我们需对齐以下硬性约束:
|
||||
|
||||
1. **多源独立性**:丢包检测必须**按站点 (StationID) 隔离**。站点 A 的网络抖动绝不能触发站点 B 的告警。
|
||||
2. **时钟假设**:显控终端作为系统的一部分,假设已通过 NTP/PTP 与总控服务器同步。因此,`timestamp_us` 可用于计算绝对的端到端延迟(Glass-to-Glass Latency)。
|
||||
3. **处理策略**:显控端对于乱序包执行**立即丢弃**策略,以保证态势图的实时性。
|
||||
|
||||
---
|
||||
|
||||
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
|
||||
|
||||
### 议题 1:丢包判决逻辑 (Loss Judgment Logic)
|
||||
|
||||
|**选项**|**A. 简单间隙检测 (Simple Gap)**|**B. 统计窗口平滑 (Sliding Window Stats) (推荐)**|
|
||||
|---|---|---|
|
||||
|**机制**|只要 `Seq != Last + 1` 就报警。|维护 1 秒滑动窗口。统计窗口内的丢包率 (PLR)。仅当 `PLR > 阈值` 时报警。|
|
||||
|**灵敏度**|**极高**。偶发的单个丢包也会导致界面闪烁告警,造成“狼来了”效应。|**适中**。过滤掉偶发的网络毛刺,关注持续性的链路质量恶化。|
|
||||
|**适用性**|调试模式。|**生产环境标准解**。|
|
||||
|
||||
### 议题 2:时序异常处理 (Timing Anomaly)
|
||||
|
||||
|**选项**|**A. 仅基于序列号 (Seq Only)**|**B. 序列号 + 时间戳双重校验 (Seq + Time) (推荐)**|
|
||||
|---|---|---|
|
||||
|**机制**|序列号递增即接收。|序列号递增 **且** `Timestamp > LastTimestamp`。|
|
||||
|**风险**|无法检测“服务器时钟回跳”或“重放攻击”。|**高健壮性**。防止因服务器端时钟故障导致的逻辑混乱。|
|
||||
|
||||
---
|
||||
|
||||
## 三、 基线确立与实施规范
|
||||
|
||||
为了构建一个“可诊断”的显控终端,我们确立 **B. 统计窗口平滑** + **B. 双重校验** 为基线。
|
||||
|
||||
### 1. 诊断上下文状态机 (Diagnostic Context)
|
||||
|
||||
显控端必须为**每一个**检测到的 `station_id` 维护一个独立的诊断上下文:
|
||||
|
||||
```cpp
|
||||
struct StationDiagnosticContext {
|
||||
uint32_t station_id;
|
||||
|
||||
// 序列跟踪
|
||||
uint64_t last_seq_id = 0;
|
||||
uint64_t last_timestamp_us = 0;
|
||||
|
||||
// 统计窗口 (1s)
|
||||
uint32_t expected_packets = 0;
|
||||
uint32_t lost_packets = 0;
|
||||
|
||||
// 链路状态
|
||||
bool connected = false;
|
||||
uint64_t last_seen_local_us = 0; // 用于检测静默/断连
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 核心诊断逻辑基线
|
||||
|
||||
显控端收到 `TrackDataBatch` 后的标准处理流水线:
|
||||
|
||||
- **步骤 1:源识别**
|
||||
- 读取 `station_id`。
|
||||
- 在 Map 中查找对应的 `StationDiagnosticContext`。若无,则创建新上下文(视为新站点上线)。
|
||||
- **步骤 2:时序完整性检查 (Sequencing)**
|
||||
- **乱序/重复**:`if (batch.seq <= ctx.last_seq)` -> **立即丢弃**。记录 `Out-of-Order` 计数。
|
||||
- **丢包**:`if (batch.seq > ctx.last_seq + 1)` -> **判定为丢包**。
|
||||
- 丢包数 `loss = batch.seq - ctx.last_seq - 1`。
|
||||
- 更新统计:`ctx.lost_packets += loss`。
|
||||
- **更新**:`ctx.last_seq = batch.seq`。
|
||||
- **步骤 3:端到端延迟遥测 (Latency Telemetry)**
|
||||
- **计算**:`latency = Now() - batch.timestamp_us`。
|
||||
- **诊断**:
|
||||
- 若 `latency > 200ms`(典型阈值):标记为 **"High Latency"**。
|
||||
- 若 `latency < 0`:标记为 **"Clock Skew"**(时钟不同步警告)。
|
||||
- **步骤 4:服务器健康感知 (Server Health)**
|
||||
- 读取 `throttle_level`。
|
||||
- **可视化**:
|
||||
- `Level 0`:绿色(正常)。
|
||||
- `Level 1`:黄色(轻微发热)。显控端右上角显示小图标提示。
|
||||
- `Level 2`:红色(严重过热)。显控端弹出 Toast 提示“数据已降级”。
|
||||
|
||||
### 3. 断连判定机制 (Dead Link Detection)
|
||||
|
||||
- **机制**:显控端需运行一个独立的 1Hz 定时器,遍历所有 `StationDiagnosticContext`。
|
||||
- **判据**:`if (Now() - ctx.last_seen_local_us > 2000ms)`。
|
||||
- **动作**:
|
||||
- 将该 Station 标记为 **"Disconnected"**。
|
||||
- 在态势图上灰置或清除该站点的所有目标。
|
||||
- 触发声音告警。
|
||||
|
||||
---
|
||||
|
||||
## 总结:2.4.3 基线图谱
|
||||
|
||||
|**诊断维度**|**字段来源**|**判定逻辑**|**显控端行为**|
|
||||
|---|---|---|---|
|
||||
|**丢包 (Loss)**|`batch_sequence_id`|`Current > Last + 1`|统计丢包率,不补发,仅告警。|
|
||||
|**乱序 (Reorder)**|`batch_sequence_id`|`Current <= Last`|**静默丢弃**,防止画面回跳。|
|
||||
|**延迟 (Latency)**|`timestamp_us`|`LocalTime - Timestamp`|超过阈值(如 200ms)提示“数据滞后”。|
|
||||
|**断连 (Dead)**|(本地接收时间)|`Silence > 2s`|标记站点离线,清空目标。|
|
||||
|**过热 (Thermal)**|`throttle_level`|`Level > 0`|UI 显性提示用户“服务器过热/数据降级”。|
|
||||
|
||||
下一步行动:
|
||||
|
||||
至此,我们已经完整定义了数据如何发(2.4.1/2.4.2)以及如何收(2.4.3)。2.4.4 热节流响应 在之前的讨论中已经涵盖(作为 Server 端行为),2.4.5 的核心也在本节的延迟遥测中定义了。
|
||||
|
||||
**建议**:我们可以结束 **2.4 外部目标数据分发协议** 的讨论,进入 **2.5 数据结构定义与序列化规范**(虽然我们在 2.4.2 中已经定义了 Proto,但可能还需要补充 C++ 内部结构体定义),或者直接进入 **第三章:异构计算架构与资源调度**。
|
||||
|
||||
鉴于我们在 2.4.2 中已经非常详细地定义了 `.proto`,且 `02_核心数据结构.md` 已经提供了 C++ 结构体定义,我建议**跳过 2.5**(除非您有特定结构体需要深究),直接进入 **第三章**,去解决最硬核的计算资源分配问题。
|
||||
|
||||
**提问**:您是否确认 **2.4.3 的多站独立诊断基线**?确认后我们是否直接进入 **第三章**?
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
tags: []
|
||||
aliases:
|
||||
- 2.4.4 热节流响应与流量整形 (Thermal Throttling Response & Traffic Shaping)
|
||||
date created: 星期一, 十一月 24日 2025, 4:33:19 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 4:38:23 下午
|
||||
---
|
||||
|
||||
# 2.4.4 热节流响应与流量整形 (Thermal Throttling Response & Traffic Shaping)
|
||||
|
||||
这是数据网关的“被动防御机制”。不同于 2.3.5 中 `SignalProcessor` 的**主动降速**(通过 `sleep` 减少计算热量),`DisplayController` 在此处的任务是通过**减少数据发送量**和**降低发送频率**,来降低 CPU 的序列化开销、PCIe 总线功耗以及网卡的中断频率,从而辅助系统整体降温。
|
||||
|
||||
## 一、 约束输入与对齐 (Constraints & Alignment)
|
||||
|
||||
基于 ECN-2025-001 和之前的讨论,我们需对齐以下硬性约束:
|
||||
|
||||
1. **被动执行**:`DisplayController` 不感知温度,只响应 `SetComputeThrottleEvent` 指令。
|
||||
2. **原子性保持**:即使在节流状态下,发送出去的 `TrackDataBatch` 仍然必须是**原子批次**。严禁将一个 CPI 的数据拆成两半发送(例如发一半航迹),这会破坏显控端的一致性。
|
||||
3. **优先级明确**:
|
||||
|
||||
- **P0 (保命)**:确认航迹 (Confirmed Tracks)。这是雷达存在的意义。
|
||||
- **P1 (辅助)**:未确认航迹 (Tentative)、系统状态。
|
||||
- **P2 (冗余)**:点迹 (Plots)、调试日志、原始回波切片。
|
||||
|
||||
---
|
||||
|
||||
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
|
||||
|
||||
### 议题:流量整形策略 (Shaping Strategy)
|
||||
|
||||
|**选项**|**A. 仅内容剪裁 (Content Pruning)**|**B. 仅频率抽稀 (Frame Skipping)**|**C. 混合降级 (Hybrid Degradation) (推荐)**|
|
||||
|---|---|---|---|
|
||||
|**机制**|保持 100Hz 发送频率,但把包里的点迹删掉,包变小了。|保持包内容完整,但每隔一帧发一帧(降为 50Hz)。|**轻度过热**时剪裁内容;**重度过热**时既剪裁内容又抽稀频率。|
|
||||
|**热收益**|**中**。减少了序列化开销和带宽,但网卡中断频率没变(依然是 100Hz)。|**高**。网卡中断减半,序列化开销减半。|**极高**。全方位降低负载。|
|
||||
|**显控体验**|**流畅但空洞**。画面依然丝滑,只是点迹没了。|**卡顿但丰富**。画面有顿挫感,但信息量全。|**平滑过渡**。优先保流畅,实在不行再保生存。|
|
||||
|
||||
---
|
||||
|
||||
## 三、 基线确立与实施规范
|
||||
|
||||
为了在“保护硬件”和“维持态势感知”之间取得最佳平衡,我们确立 **C. 混合降级** 为基线。
|
||||
|
||||
### 1. 节流等级映射表 (Throttling Mapping Table)
|
||||
|
||||
我们定义各级节流状态下,`DisplayController` 的具体行为规范:
|
||||
|
||||
|**节流等级 (Throttle Level)**|**发送频率 (Frequency)**|**内容策略 (Content Policy)**|**显控端表现**|
|
||||
|---|---|---|---|
|
||||
|**L0 (NO_THROTTLE)**|**100% (全速)**|**全量** (航迹 + 点迹 + 状态 + 调试)|正常显示。|
|
||||
|**L1 (LIGHT)**|**100% (全速)**|**剪裁 P2** (丢弃点迹、调试信息)。保留所有航迹。|画面流畅,但背景点迹消失。提示 "Data Pruned"。|
|
||||
|**L2 (HEAVY)**|**50% (二抽一)**|**剪裁 P1+P2** (仅保留**确认航迹**)。|画面帧率减半 (50Hz),仅显示核心目标。提示 "Low Rate"。|
|
||||
|**L3 (SUSPEND)**|**0% (暂停)**|**停止发送** (仅发心跳包维持连接)。|画面静止/黑屏。提示 "Server Overheat"。|
|
||||
|
||||
### 2. 实现规范:发送间隔插入 (Gap Insertion)
|
||||
|
||||
为了实现 L2 级别的降频,我们不能依赖上游 `SignalProcessor` 的降速(虽然它也在降,但我们要在网络层做双重保险)。
|
||||
|
||||
- **位置**:`DisplayController` 的独立 IO 线程循环中。
|
||||
- **逻辑**:维护一个 `frame_counter`。
|
||||
- **代码范式**:
|
||||
|
||||
```cpp
|
||||
void DisplayController::ioLoop() {
|
||||
uint64_t frame_counter = 0;
|
||||
|
||||
while (running_) {
|
||||
// 1. 从队列取数据
|
||||
TrackDataPacket packet;
|
||||
if (!queue_.pop(packet)) continue; // Non-blocking pop or wait
|
||||
|
||||
// 2. 获取当前节流等级 (原子读取,无锁)
|
||||
auto level = current_throttle_level_.load(std::memory_order_relaxed);
|
||||
|
||||
// 3. L3 级熔断:直接丢弃,仅维持心跳
|
||||
if (level == Level::SUSPEND) {
|
||||
sendHeartbeat();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. L2 级抽稀:Gap Insertion (每2帧丢1帧)
|
||||
frame_counter++;
|
||||
if (level == Level::HEAVY && (frame_counter % 2 != 0)) {
|
||||
continue; // Drop this frame completely
|
||||
}
|
||||
|
||||
// 5. L1/L2 级内容剪裁 (在序列化前执行,节省CPU)
|
||||
if (level >= Level::LIGHT) {
|
||||
packet.plots.clear(); // 丢弃点迹
|
||||
packet.debug_info.clear();
|
||||
}
|
||||
if (level >= Level::HEAVY) {
|
||||
// 仅保留确认航迹
|
||||
packet.tracks.erase(
|
||||
std::remove_if(packet.tracks.begin(), packet.tracks.end(),
|
||||
[](const Track& t){ return t.status != CONFIRMED; }),
|
||||
packet.tracks.end());
|
||||
}
|
||||
|
||||
// 6. 序列化并发送 (Protobuf Encode -> sendto)
|
||||
serializeAndSend(packet);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 流量整形对丢包检测的影响 (2.4.3 联动)
|
||||
|
||||
- **问题**:如果在 L2 级别主动抽稀(丢弃偶数帧),显控端会不会误判为“丢包”?
|
||||
- **解决方案**:
|
||||
- **序列号连续性**:`batch_sequence_id` 是在**序列化步骤(第 6 步)**生成的。
|
||||
- **结论**:被主动丢弃的帧(第 4 步)根本不会获得序列号。因此,发送出去的数据包序列号**依然是连续的**。显控端不会误报丢包,只会感知到数据刷新变慢(两次更新的时间间隔变大)。这是非常优雅的设计。
|
||||
|
||||
---
|
||||
|
||||
## 总结:2.4.4 基线图谱
|
||||
|
||||
|**策略维度**|**核心基线**|**设计意图**|
|
||||
|---|---|---|
|
||||
|**响应模式**|**被动执行**|听从 `ResourceCoordinator` 指挥,不独立决策。|
|
||||
|**L1 策略**|**内容剪裁 (Pruning)**|牺牲次要数据,保流畅度,降序列化开销。|
|
||||
|**L2 策略**|**频率抽稀 (Skipping)**|牺牲刷新率,保核心数据,降中断和总线功耗。|
|
||||
|**序列号**|**后置生成**|确保主动丢弃不破坏传输层的序列连续性。|
|
||||
111
系统基座文件/2/2.4/2.4.5 端到端延迟遥测 (End-to-End Latency Telemetry).md
Normal file
111
系统基座文件/2/2.4/2.4.5 端到端延迟遥测 (End-to-End Latency Telemetry).md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
tags: []
|
||||
aliases:
|
||||
- 2.4.5 端到端延迟遥测 (End-to-End Latency Telemetry)
|
||||
date created: 星期一, 十一月 24日 2025, 4:49:40 下午
|
||||
date modified: 星期一, 十一月 24日 2025, 4:50:25 下午
|
||||
---
|
||||
|
||||
# 2.4.5 端到端延迟遥测 (End-to-End Latency Telemetry)
|
||||
|
||||
这是系统的“后视镜”。它不再用于实时的流控反馈(因为抢占逻辑已移除),而是作为核心的**运维监控指标 (Observability Metric)**。它回答了一个终极问题:“现在的网络和负载状况下,用户看到的数据到底是多久以前的?”
|
||||
|
||||
## 一、 约束输入与对齐 (Constraints & Alignment)
|
||||
|
||||
基于 TDP-2.4-DIST(分布式补丁)和之前确立的基线,我们需对齐以下硬性约束:
|
||||
|
||||
1. **时钟基准 (Time Reference)**:所有计算必须基于**统一的 UTC 时间**。服务器(发送端)和显控(接收端)均已通过总控服务器授时(NTP/PTP),误差假设在可控范围内(如 < 10ms)。
|
||||
2. **闭环路径 (Feedback Path)**:显控端计算出的延迟数据,需要回传给服务器端的 `MonitoringModule`,以便在统一的监控大屏上展示。
|
||||
3. **统计意义 (Statistical Significance)**:单帧的延迟没有意义(可能是网络抖动),我们关注的是 **P99 延迟** 和 **平均延迟** 的趋势。
|
||||
|
||||
---
|
||||
|
||||
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
|
||||
|
||||
### 议题 1:回传通道选择 (Feedback Channel)
|
||||
|
||||
|**选项**|**A. 双向 UDP (Bidirectional UDP)**|**B. 带外 TCP/HTTP (Out-of-Band HTTP) (推荐)**|
|
||||
|---|---|---|
|
||||
|**机制**|显控端通过 UDP 向服务器的特定端口发回 `Ack/Stats` 包。|显控端调用服务器提供的 REST API 上报统计数据。|
|
||||
|**实时性**|**高**。|**低**(秒级)。|
|
||||
|**耦合度**|**高**。需要在 `DisplayController` 中增加接收逻辑,破坏了其“单向数据泵”的纯粹性。|**低**。直接复用 `API Gateway` 和 `MonitoringModule` 的现有接口。|
|
||||
|**适用性**|实时流控(已废弃)。|**长周期监控与健康分析**。|
|
||||
|
||||
### 议题 2:指标计算策略 (Calculation Strategy)
|
||||
|
||||
|**选项**|**A. 瞬时值上报 (Instant Reporting)**|**B. 客户端聚合 (Client-Side Aggregation) (推荐)**|
|
||||
|---|---|---|
|
||||
|**机制**|每收到一包数据,就计算 `Now - Ts` 并上报。|客户端维护 1 分钟的 Histogram,计算 P99/Avg,定期上报汇总值。|
|
||||
|**带宽开销**|**高**。回传流量巨大,浪费网络。|**极低**。每分钟仅几百字节。|
|
||||
|**价值**|包含噪声,难以分析。|**高价值**。直接反映一段时间内的服务质量 (QoS)。|
|
||||
|
||||
---
|
||||
|
||||
## 三、 基线确立与实施规范
|
||||
|
||||
为了构建一个闭环的监控体系,我们确立 **B. 带外 HTTP 回传** + **B. 客户端聚合** 为基线。
|
||||
|
||||
### 1. 核心指标定义 (Metrics Definition)
|
||||
|
||||
- **E2E Latency (Glass-to-Glass)**: $\text{Latency} = T_{Client\_Recv} - T_{Server\_Gen}$
|
||||
- $T_{Server\_Gen}$: `TrackDataBatch.timestamp_us` (UTC)。
|
||||
- $T_{Client\_Recv}$: 显控端收到 UDP 包时的本地系统时间 (UTC)。
|
||||
- **意义**:
|
||||
- **正常范围**:< 50ms (局域网) / < 200ms (广域网)。
|
||||
- **异常推断**:如果 Latency 突然飙升但没有丢包,说明发生了 **Bufferbloat (缓冲区膨胀)**,可能是 2.4.4 的节流机制介入过晚,或者网络设备队列堵塞。
|
||||
|
||||
### 2. 显控端实现规范 (Client-Side Implementation)
|
||||
|
||||
显控端应启动一个后台任务,负责统计和上报:
|
||||
|
||||
```cpp
|
||||
// 伪代码:客户端遥测聚合器
|
||||
class TelemetryAggregator {
|
||||
// 统计窗口数据 (1Hz 刷新)
|
||||
std::vector<uint64_t> latency_samples;
|
||||
uint64_t packet_loss_count = 0;
|
||||
|
||||
public:
|
||||
void onPacketReceived(const TrackDataBatch& batch) {
|
||||
uint64_t now = GetUtcTimeUs();
|
||||
int64_t latency = now - batch.timestamp_us;
|
||||
|
||||
// 过滤掉因为时钟不同步导致的负值
|
||||
if (latency > 0) latency_samples.push_back(latency);
|
||||
}
|
||||
|
||||
// 每 60 秒执行一次上报
|
||||
void reportLoop() {
|
||||
while (running) {
|
||||
sleep(60);
|
||||
|
||||
// 1. 计算统计值
|
||||
double avg = CalculateAvg(latency_samples);
|
||||
double p99 = CalculateP99(latency_samples);
|
||||
|
||||
// 2. 构建 JSON 报告
|
||||
json report = {
|
||||
{"station_id", station_id},
|
||||
{"latency_p99_ms", p99 / 1000.0},
|
||||
{"latency_avg_ms", avg / 1000.0},
|
||||
{"packet_loss_rate", CalculateLossRate()}
|
||||
};
|
||||
|
||||
// 3. 调用 API 上报 (复用 05_数据完整性与可靠性.md 定义的接口)
|
||||
HttpPost("http://server_ip:8080/api/v1/metrics/client", report);
|
||||
|
||||
// 4. 重置窗口
|
||||
latency_samples.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. 服务端处理基线
|
||||
|
||||
- **接收者**:`API Gateway` 接收 HTTP POST 请求。
|
||||
- **路由**:转换为 `ClientMetricsUpdateEvent` 发布到事件总线。
|
||||
- **消费者**:`MonitoringModule` 订阅该事件,并将指标存储到时序数据库(如 Prometheus/InfluxDB),用于生成 Grafana 仪表盘。
|
||||
- **告警**:如果 `latency_p99 > 500ms` 持续 3 分钟,触发 **"Network Degradation"** 告警。
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user