07-device-status-effect-visual-language.md 17 KB

设备状态特效的视觉语言、门控与并发上限

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 为可行边界:首选 BillboardCollection 逐点 scale/color 动画(200 点仍在安全区)、Entity+CallbackProperty 只配 1–3 个聚焦设备、PostProcessStage 是全屏级不能只影响一点;色值一律要经 toneColors() 烘焙;特效面片必须走半透明 pass。

下一步:本票结论作为 08 原型的设备状态特效轴的输入,由原型现场校准「正常/离线无特效是否够可辨」与 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 原型现场校准,本票不预先宣布已够。

风险已记录:正常与离线在画面上完全静态,若形状/颜色不足以分辨,需回本票重议(不得由实现票自行给正常态加回动效)。

设备状态的视觉单一来源(问题 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。未关联设备既有摘要十字标记保持原样,不因本票改变。
  • 形状与设备模型口径的分工:本票只定「状态 → 色/形/特效」的映射;设备「模型」是 glTF 还是图标 + 标签、资产来源与朝向仍归票 10,本票不预设。

触发门控(问题 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 原型做成可调并现场定值。
  • 计数口径不与摘要图例混用:关联监测设备另设独立图例,只统计当前模式实际已绘制的设备;票 09 的八行摘要图例不混入设备。降级后未逐台动画的设备仍计入关联总数,但不得被谎报为「逐台已上图」——沿用票 05 的互斥计数口径。
  • 现实规模备注:现网历史只读记录为 7 个监测点、2 个点带设备、共 3 条有效关联,故「多处同时报警」是未来风险而非当下规模;本票按风险设阈值,不按当下规模省掉。

与限距雾气的共存(问题 5)

  • 直接沿用.scratch/cockpit-primary-area-guardrails 已 resolved 的雾票 ## Answer 第 6 条:「设备三维特效随雾衰减,不开穿雾例外;面板内报警文字与状态信息保持清晰;后处理特效在雾之前合成,开关独立」。本票不重开该裁定。
  • 本票补一条实现推论:本票选定的载体是场景内 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。
  • 面板选设备与三维设备点的对应反应(hover、双向联动、拾取优先级):归票 06。
  • 同坐标/极近设备的画法与拾取:归票 11。
  • 全图模式下的图层可见矩阵(基础设施模式隐藏设备图层等):票 05 已定设备图层自身的模式规则,其余图层归票 13。
  • 本票只产决策,不实现功能;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 原型的关系:本票给的是拟议契约,原型负责现场校准「正常/离线无特效是否够可辨」与「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% 雾票性能约束管辖。