# 设备状态特效的视觉语言、门控与并发上限 Type: grilling Status: resolved Labels: wayfinder:grilling Blocked by: 02, 05 ## 进度:100% 维护者已逐条裁定问题 1–7(两轮),结论见 `## Answer`,本票收口。**02 与 05 均已 resolved**,本票的问题 1/2/4/5/6 以产物 [`research/device-status-effect-mechanisms.md`](../research/device-status-effect-mechanisms.md) 为可行边界:首选 `BillboardCollection` 逐点 scale/color 动画(200 点仍在安全区)、`Entity`+`CallbackProperty` 只配 1–3 个聚焦设备、`PostProcessStage` 是全屏级不能只影响一点;色值一律要经 `toneColors()` 烘焙;特效面片必须走半透明 pass。 下一步:本票结论作为 [08 原型的设备状态特效轴](08-prototype-anchor-panel-and-device-effects.md)的输入,由原型现场校准「正常/离线无特效是否够可辨」与 30 台降级阈值;实现归本图后续实现票,不在本票落地。**本票只定视觉语言、门控与并发上限,不声明特效已实现**;按 `AGENTS.md`,本轮未运行 build/dev 或浏览器验证。 ## Question 需求⑤原句:「定位到设备时,设备点位周围应产生对应的特效(对应设备状态,报警、预警、工单、正常、离线等……)」。 请裁决: 1. **每个状态是什么特效**:报警 / 预警 / 工单处置 / 正常 / 离线各是什么(扩散脉冲 / 闪烁 / 旋转环 / 描边呼吸 / 无特效)?「正常」要不要有特效 —— 若五个状态全有动画,画面会被特效淹没,优先级语义反而丢掉;请给出明确取舍。 2. **设备状态视觉的单一来源**:先核对后端设备自身状态的词表与语义色;`styles/tokens.css` 仍是色值来源,但现有 `domain/pointStatus.js` 表达的是**监测点**聚合状态,不可直接用它把设备猜成点位状态。设备自己的颜色、形状、文字要怎样形成独立且单一的视觉契约?特效是否需要“脉冲”等新维度?设备状态未发布时如何如实显示,未关联设备既有摘要标记是否保持原样? 3. **触发门控**:特效是「设备点位图层展开即常驻」,还是「只在该设备被定位/选中时出现」?与需求①(点位定位)、需求④(设备选中)的先后关系;关闭详情后特效是否立刻消失。 4. **并发上限与优先级**:一个点位下同时多台报警时怎么呈现(全部脉冲 / 只脉冲最高优先级 / 按屏幕距离抽稀)?同屏上限是多少、超限的降级规则是什么(降级后不能变成「什么都没画」,那会被读成「没有报警」);并发动画数如何与票 05 的独立设备图例及实际已上图数区分(票 09 八行摘要图例不混入设备)。 5. **与限距雾气的共存**:`.scratch/cockpit-primary-area-guardrails` 的 07 与本特效同处一个后处理集合;顺序(`execute` 顺序)、开关(按高度带/按区域挂载)与冲突(雾气把远处特效糊掉是否可接受、特效要不要免雾)。 6. **降噪与可达性**:是否遵守票 09 验收第 3 条「颜色不是唯一线索」与票 32 的生产形态降噪(正式环境不该有动画压满屏)——要不要像票 32 那样给一个配置开关(`map.ui.debug` 或 `map.effects.*`),开关的**默认值**是什么。 7. **可断言口径**:走查怎么证明「特效确实在动、且与状态对应」(票 09 的对抗式复验教训:拿两份 JS 数据互比是恒真断言;要从渲染结果或真实帧差里取证据)。 ## Answer ### 状态 → 特效(问题 1) 设备自身状态共**五态**,取自后端 `deviceStatistics` 的五个计数键,**没有 `EMPTY`**(那是监测点聚合态专有,设备词表不含它): | 状态 | 色键 | 形状 | 特效 | 理由 | | --- | --- | --- | --- | --- | | `ALARM` 报警 | `lv1` | 三角 | **扩散脉冲** | 最高优先级,最显眼 | | `WARNING` 预警 | `lv2` | 菱形 | **闪烁** | 次高,需处置但要与报警区分 | | `WORK_ORDER` 工单处置 | `lv3` | 方块 | **旋转环** | 有人管了,用「进行中」动效 | | `NORMAL` 正常 | `lv4` | 圆 | **无特效** | 常态不该抢注意力 | | `OFFLINE` 离线 | `neutral` | 圆环 | **无特效**(置灰) | 无数据可报,动效反而误导 | **明确取舍:正常与离线不播特效。** 若五态全有动画,画面会被特效淹没、优先级语义反而丢掉;动效只用于标记**需要处置**的三态,把视觉预算集中到该看的地方。正常/离线靠静态色 + 形状区分 —— 这条是否够可辨,由 [08 原型](08-prototype-anchor-panel-and-device-effects.md)现场校准,本票不预先宣布已够。 风险已记录:正常与离线在画面上完全静态,若形状/颜色不足以分辨,需回本票重议(不得由实现票自行给正常态加回动效)。 ### 设备状态的视觉单一来源(问题 2) - **新建 `domain/deviceStatus.js` 作平行表**:设备五态各自 `tone`(色键)+ `shape` + `label` + **新增 `effect` 维度**(动效键)。`effect` 是色 + 形无法表达的新维度,本票正式引入。 - **与 `domain/pointStatus.js` 的关系**:不 import 其状态词表(两套业务概念不共用一张表),只可能共用 `SHAPES` 的图形片段。`pointStatus.js` 仍只表达**监测点**聚合状态。 - **必须修掉的既有越界**:现 `domain/detailFlow.js:12` import `visualForStatus`、`:99-107` 的 `statusView()` 直接拿**点位**表给**设备**配色配形 —— 即「把设备猜成点位状态」在代码里已经发生,实现时改走 `deviceStatus.js`。这是本票对既有代码的明确要求。 - **色值单一来源不变**:色键仍只在 `styles/tokens.css` 声明,服务层经 `toneColors()` 读出烘焙后下传(`mapService.js:634-654`);引擎侧读不到 CSS 变量,不得在 shader/引擎层写死色值。 - **状态未发布**:落 `UNKNOWN`(「状态未发布」),与点位表同一条纪律 —— 宁可如实说不知道,也不猜成 `NORMAL`。未关联设备既有摘要十字标记**保持原样**,不因本票改变。 > **2026-10-08 就地修订(由[蓝湖素材替换票 05](../../lanhu-cockpit-asset-replacement/issues/05-point-device-marker-mapping.md) Q22=1 / Q11 / Q26=A 裁定)**:正式素材替换后,未关联设备的**十字标记被取代**,改为「**镂空徽标母版 `T_MarkPoint_White` + 内层行业图标(沿用其行业素材,如 `Q图标燃气设备`)+ 整体降饱和降亮度**」。因此本行「十字标记保持原样」只在本票当时有效,不再是正式素材下的最终视觉;未关联设备**仍有自身设备状态、仍按设备状态着色**(`CONTEXT.md` 口径不变),降饱和只作用于色相饱和度与亮度,不改变状态语义。本票其余裁定(五态映射、正常/离线无特效、门控、30 台降级、`map.effects`)不变。 - **形状与设备模型口径的分工**:本票只定「状态 → 色/形/特效」的映射;设备「模型」是 glTF 还是图标 + 标签、资产来源与朝向仍归[票 10](10-device-model-asset-definition.md),本票不预设。 ### 触发门控(问题 3) - 特效**随票 05 的关联设备图层常驻**,但不无差别常驻:图层展开后**报警/预警两级**常驻动效,工单处置也常驻旋转环(见下表口径),正常/离线静态。 - **被选中/定位的那台设备额外强化**(光圈更大 / 描边更重),与需求①定位、需求④设备选中对齐;选中不等于「只有它才有特效」。 - **关闭详情立即收图层,特效随图层一起消失** —— 不需要单独的消失逻辑,因为特效就挂在图层上,这是票 05 生命周期契约的直接推论。 - 常驻动画与按需渲染的关系:`CallbackProperty(isConstant=false)` 会持续请求新帧、让 `requestRenderMode` 失效(票 02 已核)。本票把这份代价**限制在异常设备**上(一个点位下通常只有少数几台),不扩展到全部设备。 ### 并发上限与降级(问题 4) - **不做静默截断,全部异常照画**:票 02 已核 `BillboardCollection` 在 10/50/200 点都在安全区(官方 10,000 个动画 billboard 138 FPS,200 点约 0.15 ms 摊派),性能上 200 台不是瓶颈;截断只会丢信息、换不到东西。 - **同屏异常设备超过 30 台时降级**:动画停掉,改为**静态异常标记 + 一行「N 台异常」计数**。 - **降级的红线段**:降级后**仍然看得出有异常**,绝不能变成「什么都没画」—— 那会被值班人员读成「没有报警」。静态异常标记 + 计数正是为守住这条。 - 「30 台」是待现场校准的阈值,由 [08 原型](08-prototype-anchor-panel-and-device-effects.md)做成可调并现场定值。 - **计数口径不与摘要图例混用**:关联监测设备另设独立图例,只统计当前模式**实际已绘制**的设备;[票 09 的八行摘要图例](../../cockpit-shell-panel-layout/issues/09-point-status-legend.md)不混入设备。降级后未逐台动画的设备仍计入关联总数,但不得被谎报为「逐台已上图」——沿用票 05 的互斥计数口径。 - 现实规模备注:现网历史只读记录为 7 个监测点、2 个点带设备、**共 3 条有效关联**,故「多处同时报警」是未来风险而非当下规模;本票按风险设阈值,不按当下规模省掉。 ### 与限距雾气的共存(问题 5) - **直接沿用**.scratch/cockpit-primary-area-guardrails 已 resolved 的[雾票 `## Answer` 第 6 条](../../cockpit-primary-area-guardrails/issues/07-fog-implementation-spec.md):「设备三维特效随雾衰减,不开穿雾例外;面板内报警文字与状态信息保持清晰;后处理特效在雾之前合成,开关独立」。本票**不重开**该裁定。 - **本票补一条实现推论**:本票选定的载体是场景内 `BillboardCollection`(三维渲染),**不是后处理级**,所以「先特效、后雾」已自动成立,不占用 `PostProcessStageCollection`、与雾**无排序冲突**,也不会出现「雾把远处盖住了、特效还亮在雾上面」。 - 因此本票**不新增**后处理级特效,雾级与特效级也**不需要原子对齐开关**(票 02 §3.3 那条约束只对「特效走 post-process」的路线成立)。 - 已知限制如实保留:透明对象的颜色不保证有匹配深度,故「特效随雾正确衰减」的远景观感仍需原型/实现走查确认,不能凭本节断言已达标。 ### 降噪与可达性(问题 6) - **新增 `map.effects` 配置域**,与票 32 已交付的 `map.ui.debug` **完全同构**:`cockpitConfig.js:78` 的 `ui: { debug: null }` 与 `:267-269` 的读取归一化就是模板。 - **默认值 `null` = 跟随构建模式**:开发构建 = 特效开,生产构建 = 特效关(干净形态)。与票 32 同一条纪律 —— 正式环境不该有动画压满屏。 - 现场若要在生产包里看特效,显式写 `true` 覆盖(沿用票 32 的理由:地产现场可能要在生产包里看诊断信息)。脏值退回默认并记 issue,不静默吞掉。 - **关闭特效 ≠ 关闭状态显示**:关掉的是动效,静态色 + 形状 + 文字仍然在位 —— 故不违反「颜色不是唯一线索」(票 09 验收第 3 条),关特效不会让状态变得不可读。 ### 可断言口径(问题 7) 维护者选择 **A + 人工走查**:三层证据加人工目视,缺一不可。 1. **领域纯逻辑断言**:状态 → 特效映射唯一;未知状态落 `UNKNOWN`;`NORMAL`/`OFFLINE` 的 `effect` 为空。这层便宜但要防「拿两份 JS 数据互比」的恒真断言 —— 它只证明映射表对,不证明画面在动,故不能单独作为验收。 2. **场景图快照**(关键层):走查读回 billboard 数量,并断言**同一设备的逐点 `scale`/`color` 跨帧确实变化** —— 这才是「真在跑」而非「已配置」的证据,正面避开恒真断言。可复用 `readPointMarkers(viewer)`(`cesiumEngine.js:376`)的读取模式。 3. **真实帧差**:对同一视角截两帧算像素差异,证明画面确实变了。这一层顺带补上票 02 明确留下的空白(票 02 写「没有任何在本仓库实际场景下的实测帧率数据」,真数留实现票实测)。 4. **人工走查**:观感(脉冲强度是否合适、正常/离线是否够可辨、30 台降级是否仍读得出「有异常」)由维护者目视裁定。 按 `AGENTS.md`,本轮**不自动运行**任何 build/dev/走查;上述走查脚本作为**实现票的验收手段**交付,不在本票执行。 ### 未由本票裁定 / 边界 - 设备「模型」是 glTF 还是图标 + 标签、资产来源与朝向:归[票 10](10-device-model-asset-definition.md)。 - 面板选设备与三维设备点的对应反应(hover、双向联动、拾取优先级):归[票 06](06-panel-to-scene-device-correspondence.md)。 - 同坐标/极近设备的画法与拾取:归[票 11](11-overlapping-device-points.md)。 - 全图模式下的图层可见矩阵(基础设施模式隐藏设备图层等):票 05 已定设备图层自身的模式规则,其余图层归[票 13](13-mode-layer-visibility-matrix.md)。 - 本票**只产决策,不实现功能**;`deviceStatus.js`、`map.effects` 配置与特效渲染均属后续实现票。 ## Comments ### 第 1 轮:状态特效、单一来源与门控 维护者选择:Q1=A(报警/预警/工单有特效,正常/离线无)、Q2=A(新建独立 `deviceStatus.js` 平行表并新增 `effect` 维度)、Q3=A(常驻但限最高两级,选中强化)。 只读核实(本轮实际读到的):后端设备状态五态可由 `deviceStatistics` 的五个计数键反推(`totalDeviceCount/alarmDeviceCount/warningDeviceCount/workOrderDeviceCount/normalDeviceCount/offlineDeviceCount`,见 `domain/pointDetail.js:39-66`),**没有 EMPTY**;设备卡片的 `deviceStatus` 字段确在 `domain/pointDetail.js:24` 读取,但**前端从未声明过它的枚举** —— 换言之 Q2 的「借用点位表」不是推测,是当前代码的实际行为(`detailFlow.js:12`、`:99-107`)。 ### 第 2 轮:并发、雾、配置与可断言口径 维护者选择:Q4=A(不截断;超 30 台降级为静态 + 计数)、Q5=A(沿用雾票已成文裁定)、Q6=A(新增 `map.effects.*`,`null` 跟随构建形态)、**Q7=A + 人工走查**(维护者加要求人工目视)。 只读核实:票 32 的 `map.ui.debug` 先例确已交付(`services/cockpitConfig.js:78`、`:267-269`;`32-production-ui-cleanup.md:19,28,34-35`、`docs/cockpit-deployment.md` 2.8 节),故 Q6 是照搬而非新发明。雾票 `.scratch/cockpit-primary-area-guardrails/issues/07-fog-implementation-spec.md` 的 `## Answer` 第 6 条已写「设备三维特效随雾衰减,不开穿雾例外……后处理特效在雾之前合成,开关独立」,且该票 `Status: resolved`、进度 100% —— 故 Q5 是沿用已 resolved 的裁定,不重开。 ### 收口确认 维护者答复「按B,先尝试对07进行收口,确认之后再动工」。七个问题已由两轮答复全部裁定,无遗留未决项,据此把结论写入 `## Answer`、本票改为 `Status: resolved`、进度 100%。 **收口前后的状态差异已如实记录**:本会话启动时磁盘上本票为 `Status: open` / 进度 0% / `## Answer` 为「(待填)」,`map.md` 的任务清单为未勾选;维护者面板当时显示本票已收口,本会话未能复现该状态(`git log` 对本文件**零提交**,全盘搜索只有一个副本,仅一个 worktree)。按维护者指示,本会话先完成收口再动工 08;该差异照录,不当作本票曾收口的证据。 本票结论与 [08 原型](08-prototype-anchor-panel-and-device-effects.md)的关系:本票给的是**拟议契约**,原型负责现场校准「正常/离线无特效是否够可辨」与「30 台降级阈值」两个数值/观感问题;校准结果若推翻本票任一条,回本票重议,不由实现票或原型自行改掉。 ### 2026-09-24 走查校准结果(来自票 08,本票契约未被推翻) 原型真机走查对本票两条留白给出了**场景图跨帧快照**证据(即本票可断言口径的第 2 层,非恒真断言): 1. **正常/离线无特效够不够可辨**:4 台异常设备(报警×3、预警×1)的逐点 `scale`/`color` 跨帧确实在变;3 台正常/离线**完全静止**;违规 0 项。即「动效只给需处置的三态」在真实画面上成立,正常/离线靠静态色+形仍可辨。本票 Answer 的「正常/离线无特效」**未被推翻,维持**。 2. **30 台降级阈值**:40 台设备(23 台异常)、上限调到 10 → `已降级: true`、`动效台数: 0`(动画全停),而 `异常台数: 23` 仍在 —— **降级后仍看得出有异常**,守住本票划的红线。「30 台」作为初值成立,仍属可现场再调的阈值。 ### 维护者新增方向:特效需更有科技感(实现时的视觉方向,不改本票已锁语义) 维护者在票 08 验收时给出一张参考图并提出「实际的特效还可以更加炫酷一点,需要增添科技感」。参考图的目标观感:**多层同心扩散环**(3–4 层、由内向外扩张渐隐)、**中心辉光/光晕**、以及能量感描边。同时参考图里出现的是**监测点级别**的大型报警扩散环(红色同心环罩住整个点位),而本票 Answer 只裁定了**设备级**特效 —— **监测点标记要不要也有自己的状态特效(尤其报警)是本票尚未裁定的一条**,实现前需回本票补一轮裁决,不得由实现票自行发明。 边界:本票已锁的语义全部维持——五态映射、正常/离线无特效、门控(随图层常驻+选中强化)、30 台降级、`map.effects` 配置、billboard 载体与半透明纪律。新方向只落在**视觉表现力**上(环的层数、辉光、节奏),由实现票按参考图现场调,帧耗仍受票 02 的预算纪律与 `≤15%` 雾票性能约束管辖。