# 粗路径搜索耗时设计

## 目标

为粗路径规划结果增加独立的“路径搜索耗时”。它用于回答：在规划地图已经可用后，从起点到终点得到可发布最终粗路径实际花费了多久。

现有 `PlanningDiagnostics.Elapsed` 保持不变，继续表示从 `CoarsePathPlanningService.Plan` 入口开始的总耗时。

## 计时边界

`PlanningDiagnostics.PathSearchElapsed` 的边界固定如下：

- 开始：`HybridAStarPlanner` 已完成输入、起终点和初始碰撞检查，即将调用 `HybridAStarSearch.Search`。
- 包含：二维 Dijkstra 启发式预计算、Hybrid A* 节点扩展、路径回溯、路径装配、方向分段和最终碰撞复核。
- 结束：规划器准备返回对应的 `PlanningResult`。
- 不包含：地图来源读取、地图缓存查询、障碍物栅格化、距离场构建，以及门面层在进入规划器前的工作。

因此，此字段表示“地图就绪后的路径求解与发布耗时”，而不是仅 Open List 循环的耗时。

## 数据契约

在 `PlanningDiagnostics` 新增只读 `TimeSpan PathSearchElapsed`：

- 成功时记录完整路径搜索与发布阶段耗时。
- 搜索失败、无解、节点上限、超时、取消、回溯失败、装配失败或最终复核失败时，记录截至返回前已消耗的该阶段时间。
- 在进入搜索阶段前即失败（例如输入、起终点或初始碰撞检查失败）时为 `TimeSpan.Zero`。
- 该字段必须为非负值，并且不超过总耗时 `Elapsed`。

保持构造函数的现有调用兼容：新参数具有 `TimeSpan.Zero` 默认值。`HybridAStarPlanner` 是唯一写入实际计时值的边界。

## 实现方案

推荐方案是在 `HybridAStarPlanner.Plan` 中，于调用 `_search.Search` 前创建本地 `Stopwatch`，并在所有搜索后返回路径将要构造 `PlanningResult` 时读取其 `Elapsed`。`CreateDiagnostics` 接收这个独立耗时，并写入 `PlanningDiagnostics`。

选择该方案的原因：

- 不修改门面的共享总预算和取消/超时语义。
- 不让 `HybridAStarSearch` 暴露计时实现细节。
- 计时覆盖用户定义的完整粗路径产出阶段，而非只覆盖节点扩展循环。

未采用的方案：

1. 直接复用 `PlanningOperationBudget.Elapsed`：会包含建图，不满足需求。
2. 仅在 `HybridAStarSearch` 内计时：会遗漏回溯、装配和最终复核，无法表示最终粗路径产出时间。
3. 为建图、启发式、搜索、复核分别公开多组指标：诊断更细，但超出当前需求。

## 可视化与文档

`MovementTest.CoarsePathTest` 的状态图层和 Toast 同时显示：

```text
总耗时：<Elapsed> ms，路径搜索：<PathSearchElapsed> ms
```

README 明确区分：总耗时覆盖建图和路径规划；路径搜索耗时仅覆盖地图就绪后的最终粗路径搜索、回溯、装配与复核。

## 验证

自动化验证应覆盖：

1. `PlanningDiagnostics` 默认搜索耗时为零，且新字段可由调用方读取。
2. 一个真实可行规划返回非负的路径搜索耗时，且不大于总耗时。
3. 既有总预算、取消、超时和路径状态断言不改变。
4. UI 源码检查确认图层和 Toast 读取并显示新字段。
5. README 包含新字段的计时边界说明。

