创建仓库

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,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 线程已与计算/发送线程物理解耦且互不干扰)。

View File

@@ -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 配置的依赖,降低部署复杂度。
- **无状态**:相比 TCPUDP 无需维护连接状态(三次握手/四次挥手),消除 `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 业务数据序列化规范**,讨论数据具体怎么“打包”。

View File

@@ -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 字段。 |

View File

@@ -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 的多站独立诊断基线**?确认后我们是否直接进入 **第三章**

View File

@@ -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)**|牺牲刷新率,保核心数据,降中断和总线功耗。|
|**序列号**|**后置生成**|确保主动丢弃不破坏传输层的序列连续性。|

View 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"** 告警
---