# 47: 设备状态视觉特效与门控 Type: implementation Status: resolved Blocked by: 38, 44, 45, 46 Labels: ready-for-agent ## What to build 按决策票 07 在票 44 的独立设备状态词表上实现特效计划,不复用监测点聚合状态。设备五态为 `ALARM`、`WARNING`、`WORK_ORDER`、`NORMAL`、`OFFLINE`,未知状态落 `UNKNOWN`;报警扩散脉冲、预警闪烁、工单旋转环,正常/离线无特效。特效随关联设备图层生命周期门控,选中设备强化,关闭详情随图层销毁;超过 30 台异常设备降级为静态异常标记并显示计数。 ## Acceptance - [x] 消费票 44 已落地的独立 `deviceStatus` 静态词表,并添加 effect 维度;设备状态不从 `pointStatus` 推导,未知不猜正常。 - [x] 三种需处置状态有各自动效,正常/离线无动效;颜色/形状/文字沿设备状态语义。 - [x] 特效随关联设备图层出现/消失,选中设备可强化,详情关闭后无残留动画。 - [x] 超过 30 台异常设备按规则降级为静态异常标记 + 异常计数,不静默删点;降级后仍看得出异常。 - [x] 特效与雾气、透明管线和重叠聚合共存,不能破坏拾取;沿既有雾规则随远景衰减,不开穿雾例外。 - [x] `map.effects` 配置缺省 `null` 跟随构建形态;关闭动效不隐藏静态状态表达。 - [x] 纯逻辑覆盖状态映射、未知、门控和并发降级;真实帧差与人工走查分别记录结果,未获人工确认不关闭本票。 - [x] 对受控的 `coordinateValidation.renderable=true` 设备样例做场景跨帧快照证明逐点动画确实变化;真实环境仍为清单-only 时不得从纯逻辑断言推断现场特效已生效。 > 维护者已确认效果“基本符合我的要求”并明确要求自检、主场景同步、提交推送后关闭;本票已按该授权收口,历史阶段记录留档。 ## 最终自检与主场景同步(当前结论优先于下方历史记录) 维护者确认“基本符合我的要求”,授权再次自检、同步主场景、提交推送后关闭本票,人工确认前置条件已满足。代码提交 `09ef651` 已推送至 `origin/master`,本票已改为 resolved。 ## 进度:100%,已关闭 ## Answer 中心设备图标保持常亮不透明,状态动画由外围四层能量环与辉光表达;设备详情和连接短线随所选设备换锚,返回摘要恢复监测点锚点。默认主场景保留 MP-001 K=2 / MP-002 K=11,并修正太常设备坐标、补齐五态样例。维护者人工确认结合本轮脱浏览器自检完成收口;本轮未运行 build/debug/浏览器。 - 默认 `success` 主场景共用中心图标清晰度、设备详情换锚及外围光环代码,无需 URL 场景参数。MP-001 保持 K=2;MP-002 保持 K=11,设备坐标修至太常点附近,五态分布为 3 报警 / 3 预警 / 1 工单 / 3 正常 / 1 离线,前三种状态播外围动效。 - 新增主场景契约检查 `t47-main-scene.mjs`:真实 mock 传输→地图/详情适配→聚合/动效/锚点计划,验证编码一致、设备自身有效坐标、K2/K11与五态动效。 - `t47-readability-anchor.mjs` 19 项通过,涵盖中心 alpha=1、外围闪烁、展开偏移、图层撤下、关特效停循环、设备换锚、返回点位、无位置与待确认退化。 - 相关 11 组脱浏览器检查全部通过(t05 283/283、t09 38/38、t41 46/46、t42 39/39、t43 40/40、t44 47/47、t45 18/18、t46 29/29、t47 27/27及两个新增脚本)。Vue SFC 解析和修改文件语法检查通过。 - 扩大检查共 39 组,35 组通过,4 组失败保留:t06 素材 JSON 导入要求 import attribute;t11 设备卡片局部 ref 撞旧守卫;t15 WebSocket 刷新断言;t28 Puppeteer 依赖缺失。本票提交不包含并行素材/3D Tiles/其它接口在途变更。 - 本轮按 AGENTS.md 跳过 build/dev、浏览器调试和截图;人工验收依据维护者本次确认,历史浏览器证据不是本次复跑结果。deck 写入通道仍报旧 sandbox 权限,但在用户更新当前文件权限后,已按本地 Markdown tracker 更新本票状态与上游决策票 04;规格 12/15、地图索引及 CONTEXT 已同步用户直接裁定。 ## 本次复验反馈修复(历史记录) 维护者指出中心图标闪烁太暗、设备间距较大时设备详情仍留在父点并遮住所选设备。本次已将中心图标改为不透明、稳定形状且不缩小至正常尺寸以下;闪烁/扩张/旋转仅写外围光环。光环跟随设备显示与 pixelOffset;按需渲染时动态设备主动请求帧。 设备详情及面板连接短线改为锚向所选设备(设备 A→B 换锚,返回摘要恢复监测点)。锚点包含贴地高度、展开偏移和当前图标尺寸;无位置设备显示自身原因,不借父点坐标。维护者直接裁定已记录在决策规格 12/15、决策地图与 CONTEXT.md;上游决策票 04 的 deck 写入被文件权限拒绝,尚待同步。 验证:新增 `verify/t47-readability-anchor.mjs` 覆盖生产动画写帧、锚点身份、设备位置退化与实际投影;相邻特效/锚定/联动/重叠/模式脱浏览器闸门通过。本次依据 AGENTS.md **未执行 build/dev、浏览器调试或截图**;已更新真实走查脚本,尚未复跑,因此下方历史跨帧结果不能当作本次画面验收。票保持 claimed,待人工复验与权限允许后的决策票同步。 ## 历史进度:实施完成,待人工确认(2026-09-30) **未关闭**:本票验收第 7 条明写「真实帧差与人工走查分别记录结果,**未获人工确认不关闭本票**」。 自动证据(纯逻辑 20/20、跨帧快照 9/9、降级 6/6、真实帧差已取)已齐,**缺的只有维护者目视裁定** (脉冲强度是否合适、正常/离线是否够可辨、30 台降级是否仍读得出「有异常」)。 ### 交付物 - `domain/deviceStatus.js`:按决策票 07 给设备独立词表**新增 `effect` 维度** (`ALARM: pulse` / `WARNING: blink` / `WORK_ORDER: spin` / `NORMAL: null` / `OFFLINE: null` / `UNKNOWN: null`)。正常、离线、未知**一律无动效** —— 未知状态更不编一个"正在报警"的样子。 - `domain/deviceEffects.js`(新,纯函数):`deviceEffectOf()` 映射、`effectPhaseAt()` / `pulseStrengthAt()` / `blinkStrengthAt()` / `spinAngleAt()` 相位、`planDeviceEffects()` 门控与降级、 `degradedAbnormalNote()` 计数文案。**「什么算异常」复用票 46 的 `isAttentionDeviceStatus` 单一住址**(`ALARM`/`WARNING`),不另造一份定义。 - `services/cockpitConfig.js`:新增 `map.effects { enabled, abnormalLimit }`,与票 32 的 `map.ui.debug` **完全同构**(`enabled: null`=跟随构建形态;脏值退回默认并记 issue)。 - `services/cesiumEngine.js`:`applyDeviceEffects()` / `stopDeviceEffects()` / `readDeviceEffects()`(只读跨帧读回)。动效**乘在票 43 反应量的基准上**(`deviceBaseStyle`), 不与选中/hover/弱化互相覆盖;载体是场景内 billboard,**不是后处理级** ⇒「先特效、后雾」自动成立,不占 `PostProcessStageCollection`、不开穿雾例外。 - `services/mapService.js` + `stores/deviceLayerStore.js` + `components/PointDetailCard.vue`: 计划随图层下发、图层撤下即停(特效随图层消失)、降级时面板显示「异常设备较多(N 台)· 动效已暂停」。 宿主新增只读字段 `data-map-device-effects`(`on|off|degraded`)、`data-map-device-effects-abnormal`、 `data-map-device-effects-animated`。 - `mocks/contractMocks.js`:两个受控样例 —— `device-effects`(五态各一台,坐标各自错开)与 `device-effects-degraded`(31 台报警)。**受控可上图样例**是决策票 07 的硬要求 (真实环境仍是清单-only 时不得从纯逻辑推断现场特效已生效)。 ### 三层证据(决策票 07 问题 7) 1. **领域纯逻辑**:`verify/t47-device-effects.mjs` **20/20** —— 映射唯一、未知不猜、 门控、恰好 30 台不降级/31 台降级、阈值可调、`map.effects` 默认与脏值。 相位函数按时间采样(脉冲 8 个不同取值、闪烁两段、旋转单调推进),**不是拿两份 JS 数据互比**。 2. **场景图跨帧快照**(关键层):`verify/t47-device-effects-walkthrough.mjs` **9/9** —— 8 帧采样下,报警 `scale`/`alpha` 各 8 个不同取值、预警 `alpha` 2 个取值(闪烁两段)、 工单 `rotation` 8 个取值;而**正常与离线全程只有一个取值**(完全静止)。真实鼠标点开点位。 3. **真实帧差**:同一视角两帧截图**字节不同**(走查新增 `cdp-driver.screenshot()`; 字节级判据,未做逐像素解码 —— 无依赖可解 PNG)。 4. **降级场景**:`device-effects-degraded` **6/6** —— `data-map-device-effects=degraded`、 动效台数 0、8 帧全部无动效、异常计数 31 可见、**31 台静态标记仍画着**(不静默删点)。 ### 一处需要维护者确认的解读(我做的判断,已如实标注) 决策票 07 说「同屏**异常设备**超过 30 台时降级」,而它同时把 `WORK_ORDER` 列为有动效的状态。 我按**票 46 的「需处置」判据**(`ALARM`/`WARNING`)计异常台数,而不是按"所有带动效的设备"计 —— 理由是票 08 的现场校准记录正是「23 台异常 ⇒ 已降级、动效台数 0、异常台数 23 仍在」。 若维护者认为该按"带动效台数"计,这是**回票 07 重议**的事,不由本实现自行改。 ### 下一步(未关闭的原因) 请维护者目视裁定三件事:① 脉冲/闪烁/旋转的强度是否合适(当前幅度 0.34、选中 0.55); ② 正常/离线**完全静止**是否够可辨;③ 31 台降级时是否仍一眼读得出「有异常」。 确认后即可关闭本票。 ### 回归 零依赖闸门 **37 道**(含本票新增 t47)全绿;三处红与本票无关: `t11`(并发会话给 `PointDetailCard.vue` 新增了 `ref(`,撞票 10 的"组件不自己存状态"守卫)、 `t15`(并发会话在改快照/推送语义)、`t28`(需 puppeteer,模块不在本机)。 守卫同步更新 2 处(都是本票**有意变更契约**):`t44` 的 F11 原写「动效留给票 47」,现按新契约 允许 `effect` 并钉住取值域与"正常/离线/未知无动效";`t43` 的 F12 正则允许新增的 `effects` 选项。 **按 `AGENTS.md`:本会话未跑 build;`yarn dev` 未由我启动(8000 端口已有服务在跑)。 浏览器仅用于走查(维护者授权的提权通道)。** ## Out of scope - 监测点级扩散环/监测点自身特效仍未被设备五态决策裁定,需另立决策票,不得在本票自行实现;设备图层触发、资产选择、双向联动、重叠聚合、设施/管线/事件图层和后端状态来源改造。