/** * 监测**设备**自身状态 → 视觉表现(票 44;决策票 07)。 * * ── 为什么必须是**平行**于 `pointStatus.js` 的另一张表,而不是复用它 ── * 两张表的业务对象不同、状态来源也不同: * · `pointStatus.js` 表达**监测点聚合状态**(后端解析并发布,六个取值含 `EMPTY`「无设备」); * · 本表表达**监测设备自身状态**(五个取值,**没有 `EMPTY`** —— 那是点位聚合态专有)。 * 早先 `detailFlow.js` 正拿点位那张表给设备配色配形(即"把设备猜成点位状态"在代码里已经发生), * 那是越界;本票把它改到本表(见 `domain/detailFlow.js` 的 `deviceStatusView`)。 * * ── 三条纪律(与 `pointStatus.js` 同源,但取值集不同)── * 1. **前端不重算**:只接收后端 `deviceStatus` 字符串;认不出的落 `UNKNOWN`(「状态未发布」), * 宁可如实说不知道,**不猜成 NORMAL** —— 猜成正常是最危险的一种谎话。 * 2. **颜色不是唯一线索**:每态同时有 `tone`(色键)+ `shape`(形状)+ `label`(文字)。 * 色键**只写键名**,色值仍只在 `styles/tokens.css` 声明(票 06 的单一口径)。 * 3. **色键可同、语义不混**:点位与设备可用同一组 `--lv1..--neutral` 调色板, * 但两表的键位含义各自成立,不得互相推导。 * * 五态与中文标签照抄后端 `CockpitMonitoringPointDeviceCard.deviceStatus` 的取值 * (`ALARM`/`WARNING`/`WORK_ORDER`/`NORMAL`/`OFFLINE`),不另造业务词汇。 * 本票只交付**静态**色/形/文字;动效(扩散脉冲/闪烁/旋转环)归票 47。 */ import { SHAPES } from './pointStatus.js' /** 后端设备自身状态的五个取值(原样字符串,不改名、不缩写)。 */ export const DEVICE_STATUS = { ALARM: 'ALARM', WARNING: 'WARNING', WORK_ORDER: 'WORK_ORDER', NORMAL: 'NORMAL', OFFLINE: 'OFFLINE', } /** * 设备状态视觉词表。 * * 形状**复用** `pointStatus.js` 的 `SHAPES` 片段(同风格、同一份 SVG 来源,避免两套图形定义), * 但**键与语义在本表独立成立**:例如点位表的 `EMPTY`(无设备)在这里根本不存在。 * `priority` 数值沿用后端给的状态优先级(1 最紧急),前端不自定序。 */ export const DEVICE_STATUS_VISUALS = { ALARM: { key: 'ALARM', tone: 'lv1', shape: 'triangle', label: '报警', priority: 1, effect: 'pulse' }, WARNING: { key: 'WARNING', tone: 'lv2', shape: 'diamond', label: '预警', priority: 2, effect: 'blink' }, WORK_ORDER: { key: 'WORK_ORDER', tone: 'lv3', shape: 'square', label: '工单处置', priority: 3, effect: 'spin' }, NORMAL: { key: 'NORMAL', tone: 'lv4', shape: 'circle', label: '正常', priority: 4, effect: null }, OFFLINE: { key: 'OFFLINE', tone: 'point-offline', shape: 'ring', label: '离线', priority: 5, effect: null }, } /** * 认不出的设备状态:如实说「不知道」,**不落到任何一个真实状态上**(尤其不猜 NORMAL)。 * `effect: null` —— 状态都不知道,**更不能给它编一个动效**(那会把"未知"画成"正在报警")。 */ export const DEVICE_UNKNOWN_VISUAL = { key: 'UNKNOWN', tone: 'point-unknown', shape: 'hollowDiamond', label: '状态未发布', priority: 6, effect: null } /** 设备状态字符串 → 视觉词条;认不出的一律 `DEVICE_UNKNOWN_VISUAL`。 */ export function deviceVisualForStatus(status) { return DEVICE_STATUS_VISUALS[status] || DEVICE_UNKNOWN_VISUAL } /** 一条设备的视觉词条 → 地图标记要的形状名(引擎只认形状,不认业务状态)。 */ export function deviceShapeOf(device) { return deviceVisualForStatus(device && device.status).shape } /** * 设备状态图例行(**独立于监测点摘要图例**,规格与票 09 的边界都要求分开)。 * * `count` = 当前模式实际绘制的该状态设备数(由调用方给,本层只负责行与顺序)。 * 0 不隐藏行(形状的含义要能查到)。 * @param {object} [byStatus] `{ [visualKey]: count }` */ export function deviceLegend(byStatus = {}) { const rows = [...Object.keys(DEVICE_STATUS_VISUALS).map((key) => DEVICE_STATUS_VISUALS[key]), DEVICE_UNKNOWN_VISUAL] return rows .sort((a, b) => a.priority - b.priority) .map((visual) => ({ key: visual.key, tone: visual.tone, shape: visual.shape, label: visual.label, count: byStatus[visual.key] || 0, })) } /** 该形状名在本表(及其复用的 `SHAPES`)里是否存在;供静态守卫与降级判据用。 */ export function isKnownDeviceShape(shape) { return Object.prototype.hasOwnProperty.call(SHAPES, shape) }