# Trajectory Planning Visualization

`TrajectoryPlanningVisualization` 是一个独立的 `netstandard2.0` 轨迹观察类库。它只接受通用、不可变的可视化快照，并在本机浏览器中显示中文科研风图表；它不引用 EMPlanner、MDCS、Painter、ClumsyCore 或 MovementTest。

## 安全与运行边界

此库固定为 `OBSERVE_ONLY`：浏览器没有控制路由，任何网页事件都不能回流到规划、车辆或执行器。服务仅使用 `TcpListener` 绑定 `127.0.0.1`，不是 `localhost` 的局域网别名，也不会监听外网地址。它只处理受限的 HTTP/1.1 `GET`；请求头最大为 16 KiB、读取总时限为 2 秒。

`Start` 为每个会话生成随机 256-bit 小写十六进制 token，并在完整 URI 中返回它。页面、`/app.css`、`/app.js`、`/api/bootstrap` 与 `/api/events` 都要求该 token。服务不使用 Cookie，最多服务两个 SSE 客户端。网页地址只适合运行类库的本机用户；不要把带 token 的地址转发给其他人。

## 最小用法

调用方负责把自己的领域数据转换为通用的静态和动态快照；类库不理解任何规划器专有类型。

```csharp
using var visualization = new PlanningVisualizationSession(
    new PlanningVisualizationOptions { Port = 0, RefreshRateHz = 10d, HistoryCycleLimit = 60 });
PlanningVisualizationSessionInfo info = visualization.Start(staticSnapshot);
visualization.Publish(dynamicSnapshot);
visualization.Stop();
```

`Start` 只传输一次静态快照。`Publish` 只校验并以原子交换写入最新动态帧，绝不序列化、等待 socket、浏览器或绘图；旧帧会被丢弃，因此最新帧槽的 **capacity 1** 是有意的。默认刷新率为 **10 Hz**，周期摘要默认保留 60 条；完整轨迹只属于当前最新帧和调用方快照，不进入历史。

重复 `Start` 会返回已有会话信息；重复 `Stop` 安全。正常 `Stop` 最多尝试 100 ms 发送 `event: end`（“会话已结束”），之后关闭 SSE 和监听端口。关闭或断开浏览器只会丢弃网页帧，不会停止宿主观察；由 MovementTest/宿主在停止时统一回收 HTTP/SSE、端口和可选 Painter。

## 路由与性能模型

- `/?token=…`：嵌入式 `index.html`，并将同一 token 放入 CSS 和 JavaScript 的 URL。
- `/app.css?token=…`、`/app.js?token=…`：程序集嵌入资源，无 CDN、Node.js、包管理器或运行时网页文件查找。
- `/api/bootstrap?token=…`：一次返回静态快照和有效刷新率。
- `/api/events?token=…`：以 SSE 推送最新动态帧及有界周期摘要。

慢客户端使用容量为 1 的发送槽，因此不会对 `Publish` 形成反压；没有 SSE 客户端时服务不会重复序列化动态帧。占用栅格是行优先 LSB-first bitset，浏览器用单个 Canvas 解码和绘制，SVG 只覆盖路径、车辆和标记。

## 图表约定

页面包含“路径总览”、“LS / ST”、“曲率与运动学”和“周期历史与生效配置”四个页签。八个稳定 chart ID 为 `ls`、`st`、`curvature-s`、`curvature-t`、`velocity-t`、`acceleration-t`、`jerk-t`、`yaw-rate-t`。说明文本为中文；轴标签保留输入快照提供的科学变量、数学符号和 SI 单位。

| 图表 | X 轴 | Y 轴 |
| --- | --- | --- |
| `ls` | `ReferenceS (m)` | `l (m)` |
| `st` | `t (s)` | `PathS (m)` |
| `curvature-s` | `s (m)` | `κ (m⁻¹)` |
| `curvature-t` | `t (s)` | `κ (m⁻¹)` |
| `velocity-t` | `t (s)` | `v (m/s)` |
| `acceleration-t` | `t (s)` | `a (m/s²)` |
| `jerk-t` | `t (s)` | `j (m/s³)` |
| `yaw-rate-t` | `t (s)` | `ω (rad/s)` |

`st` 的纵轴是真实 `PathS (m)`，`ls` 的横轴是活动方向段的共享 `ReferenceS (m)`；两者不混用。jerk 的 `j[i]` 仅表示真实区间 `[tᵢ,tᵢ₊₁)`，数量为轨迹点数减一，最后一个轨迹点不补造 `j=0`，页面明确标注“末点后无时间区间”。

路径总览按语义分层：粗路径、Local G2、方向段、上一轮、当前轨迹、`s_end`/换向/终点/车辆标记，并保持世界 `X/Y` 等比例。每张 SVG 支持滚轮以指针为中心缩放、拖拽框选局部放大、重置和全屏；这些操作只改变浏览器本地视口，不修改原始快照，也不向规划器发送参数。

## 适配器责任

适配器应在外部项目中冻结配置、构造 `PlanningVisualizationStaticSnapshot`，并在每个观察周期构造 `PlanningVisualizationDynamicSnapshot`。MovementTest 默认以 `FullDirectionSegment` 提供完整方向段快照；本类库本身不决定规划范围。它可以在启动后使用 `info.Uri` 由宿主决定是否打开浏览器；本类库不会读取桌面环境或启动进程。快照、服务或前端异常应被宿主隔离为一次中文诊断，不得改变规划周期、求解结果或任何车辆控制行为。
