# 还原 V2X 实时路口信号灯与车辆展示 ## Goal 把 `v2x_web` dashboard「实时路口」(`HologramCross`) 的实时数据行为完整搬到本项目的 Cesium 预览中,使 `outputs//…-cesium-preview.html` 在登录后能持续、正确地显示 **信号灯相位灯色与倒计时** 和 **OBU / 目标识别车辆**。 当前实现(Codex 移植)接口地址是对的,但数据处理与渲染层基本失真,实际效果是:灯永远红、车可能一辆都不出现、 出现后也不消失、连接几十秒后被服务端断开。本任务修复这条链路。 ## Source of Truth 参照实现(只读,不修改): `/Users/que01/Project/v2x_web/applications/dashboard/src/views/dashboard/components/Content/Map/HologramCross/` - `components/CrossTrafficLights3D.vue` — 信号灯:link 相位、WS 订阅、灯色/倒计时 - `components/utils.ts` — 灯色状态字典、航向角计算 - `components/CrossCars/hooks/useObuCars.ts` — OBU 车辆 - `components/CrossCars/hooks/useTargetCars.ts` — 目标识别车辆 - `components/CrossCars/type.ts` — 车辆数据结构 ## Requirements ### R1 WebSocket 连接层对齐 - 三个 socket(`signal` / `obuPosition` / `targetPosition`)均按源项目发送心跳 `{"heartBeat":"ping"}`,间隔 30000ms。 - 断线自动重连,重连后重新发送各自的订阅消息(signal 发 `junctionId`,target 发 `deviceId`,obu 发 bounds)。 - 页面 `visibilitychange` 回到可见时行为不得导致重复连接堆积;`dispose()` 必须清掉心跳与重连计时器。 ### R2 信号灯灯色映射修正 - 采用源项目 `utils.ts` 的状态字典:`11=灭灯 21=红 22=黄 23=绿 31=其他`。 - 现有 `lampColor()`(overlay)与 `lampColorName()`(`cesium-preview.js`)中基于 `2`/`3` 的判断必须移除。 - 未知状态码按「其他/灭灯」处理,不得静默落到红色。 ### R3 相位到原生信号灯的映射 - `traffic-signals.json` 中不存在 V2X `phaseNo`(只有 `phaseGroup: 0`),当前回退到 `signal.id` 导致永不匹配。 必须建立一条真实可用的 V2X `phaseNo` → 原生信号灯头的映射。 - 映射结果必须可诊断:面板要能显示「N 个相位已绑定 / M 个未绑定」。 - 允许区域配置提供显式覆盖映射。 ### R4 一个相位可点亮多条进口道 - 同一 `phaseNo` 对应的所有 link / 信号灯头都要被点亮,当前 `Map` 的覆盖行为必须消除。 ### R5 倒计时 - 使用推送中的 `countDown` 字段(源项目 `phaseValue['time'] = signal.countDown`),驱动预览中已有的 countdown 资产或等效显示。 ### R6 车辆订阅正确建立 - OBU socket 连接后必须发送订阅消息(源项目 `send(bounds || '')`),相机视野变化时发送 `{"bounds": ""}`。 - target socket 连接后发送 `{"deviceId": ""}`,无配置时发送 `{"deviceId": null}`。 ### R7 车辆生命周期 - 按源项目实现超时隐藏:超过 `interval * 1.5` 未更新的车辆隐藏并可复用槽位;`interval` 取自推送体, `0` 时回退 `500`。 - socket 断开时清空车辆列表。 - 车辆位置更新使用推送间隔做平滑插值,避免逐帧跳变。 ### R8 车辆模型命名与资产对齐 - OBU 数据层标记为 `car_obu.glb`,但 dashboard 的实际 `CrossCars` 模板固定渲染 `11.glb`;预览必须遵从该最终渲染契约。目标识别车辆使用 `${type}${subType}.glb`。 - 预览包必须携带 dashboard 实际使用的车辆模型;包内缺少未知车型时回退到 `11.glb`,并在面板提示,不得静默显示错误车型。 ### R9 推送体解析健壮性 - 源项目对两个车辆 socket 使用 `saferEval` 而非 `JSON.parse`,说明载荷不保证严格 JSON。 - 解析必须容忍非严格 JSON,且解析失败要计数并在面板可见,不得整条静默丢弃。 ### R10 范围纠正 - `/facilities/api/FlowTravelRatio/queryListWeek`(周流量比)不属于「实时路口」,从实时加载链路移除。 ## Non-Goals - 不移植 AMap 底图 / 矢量瓦片图层(`linkVectorHdMapTileUrl`)。 - 不移植 `CrossDevices/Model3D.vue` 的 3D 杆件模型;设备维持点位显示(可后续独立任务)。 - 不移植 `useV2XEvents` 的 V2X 事件气泡。 - 不改变原生包产物(GLB / metadata / traffic-signals.json 的生成逻辑)。 ## Constraints - 不得提交上游地址、账号、令牌、AMap key;`v2xPreview` 配置保持非敏感。 - 令牌只存 `sessionStorage`,页面始终从登录门进入。 - 坐标契约不变:V2X 数据 GCJ-02,进 Cesium 前转一次 WGS84;原生数据不得二次转换。 - `v2xPreview.enabled` 默认 `false`,关闭时生成页不含 V2X 面板。 - 生成的预览为静态页,无构建步骤,overlay 保持零依赖 IIFE。 ## Acceptance Criteria - [ ] AC1 三个 socket 均可见 30s 心跳;模拟服务端在 60s+ 后连接仍存活。 - [ ] AC2 单测覆盖灯色映射:`21→红 22→黄 23→绿 11→灭 31→其他`,且 `2`/`3` 不再被当作黄/绿。 - [ ] AC3 单测覆盖相位映射:给定 V2X link 相位与 `traffic-signals.json`,输出的绑定数 > 0, 未绑定项被明确列出。 - [ ] AC4 单测覆盖同相位多进口道:一个 `phaseNo` 对应 2 条 link 时,两条都被点亮。 - [ ] AC5 单测覆盖车辆超时:注入两次推送并推进时间超过 `interval*1.5`,车辆转为不可见。 - [ ] AC6 单测覆盖 OBU/target 订阅消息:连接后发出的首帧内容与源项目一致。 - [ ] AC7 单测覆盖非严格 JSON 推送体能被解析,且失败计数可读。 - [ ] AC8 `npm run test:v2x-cesium-preview`、`test:v2x-preview-server`、`test:preview-assets` 全绿。 - [ ] AC9 真机(`npm run serve:v2x-preview` 接真实上游)登录后可见:信号灯随相位变色并显示倒计时、 车辆出现并移动、离开后消失。此项由开发者人工确认。 - [ ] AC10 `v2xPreview.enabled=false` 重新生成后,页面不含 V2X 面板,包产物字节不变。