| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /**
- * T46 一级模式图层计划与选择态矩阵 纯逻辑检查(票 46;规格 18 / 决策票 13)。
- *
- * 零依赖 node 断言脚本(约定见 t01-panel-catalog.mjs)。
- * 运行:node .scratch/cockpit-shell-panel-layout/verify/t46-mode-layer-plan.mjs
- *
- * 主 seam 是 `domain/modeLayerPlan.js` 的纯函数:五种状态 × 各对象层的可见矩阵、
- * 异常点判定(只认设备状态事实)、图例与标记一致、引导门控、降级说明、回联目标与选择态。
- */
- import {
- MODE_LAYER,
- MODE_SELECTION,
- planModeLayers,
- planModeSelectionTransition,
- isAssociatedDeviceVisible,
- isAttentionDeviceStatus,
- } from '../../../src/modules/cockpit/domain/modeLayerPlan.js'
- import { legendMatchesMarkers } from '../../../src/modules/cockpit/domain/pointStatus.js'
- const checks = []
- function check(name, pass, detail = '') {
- checks.push({ name, pass })
- const rest = detail === '' ? '' : ` — ${detail}`
- console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${rest}`)
- }
- /** 造一张摘要对象集:2 监测点(1 异常)+ 1 未关联设备。 */
- function fixtureObjects() {
- return [
- {
- kind: 'monitoring-point',
- code: 'MP-A',
- name: '异常点监测点',
- status: 'NORMAL',
- deviceCodes: ['EQ-A1', 'EQ-A2'],
- longitude: 110.1,
- latitude: 28.4,
- coordinate: { state: 'VALID', reason: '', renderable: true, validated: true },
- },
- {
- kind: 'monitoring-point',
- code: 'MP-B',
- name: '正常点监测点',
- status: 'NORMAL',
- deviceCodes: [],
- longitude: 110.2,
- latitude: 28.4,
- coordinate: { state: 'VALID', reason: '', renderable: true, validated: true },
- },
- {
- kind: 'unassociated-equipment',
- code: 'EQ-FREE-1',
- name: '异常未关联设备',
- status: '',
- deviceCodes: [],
- longitude: 110.3,
- latitude: 28.4,
- coordinate: { state: 'VALID', reason: '', renderable: true, validated: true },
- },
- ]
- }
- /** 异常设备事实(照 `/monitoring/situation` 的领域形状)。 */
- function fixtureAbnormal() {
- return [
- { code: 'EQ-A1', name: '关联报警设备', status: 'ALARM' },
- { code: 'EQ-FREE-1', name: '异常未关联设备', status: 'WARNING' },
- ]
- }
- /** 关联设备行(placement 已由 deviceLayer.js 判好;计划只做模式过滤)。 */
- function fixtureAssociated() {
- return [
- { code: 'EQ-A1', pointCode: 'MP-A', status: 'ALARM', placement: 'drawn', longitude: 110.11, latitude: 28.41 },
- { code: 'EQ-A2', pointCode: 'MP-A', status: 'NORMAL', placement: 'drawn', longitude: 110.12, latitude: 28.42 },
- ]
- }
- function planOf(mode, extra = {}) {
- return planModeLayers({
- mode,
- objects: fixtureObjects(),
- abnormalDevices: fixtureAbnormal(),
- associatedDevices: fixtureAssociated(),
- entered: true,
- ...extra,
- })
- }
- // ================================================================ A 完整可见矩阵
- {
- const overview = planOf(MODE_LAYER.OVERVIEW)
- const infra = planOf(MODE_LAYER.INFRASTRUCTURE)
- const monitoring = planOf(MODE_LAYER.MONITORING)
- const warning = planOf(MODE_LAYER.WARNING_RESPONSE)
- const codesOf = (plan) => plan.markers.map((m) => `${m.kind}|${m.code}`).sort()
- check('A1-overview-shows-everything',
- codesOf(overview).length === 3 &&
- codesOf(overview).includes('monitoring-point|MP-A') &&
- codesOf(overview).includes('monitoring-point|MP-B') &&
- codesOf(overview).includes('unassociated-equipment|EQ-FREE-1'),
- `总览 ${codesOf(overview).join(' ')}`)
- check('A2-infrastructure-hides-monitoring',
- infra.markers.length === 0,
- `基础设施 ${infra.markers.length} 个标记(监测点/未关联设备/关联设备全隐藏)`)
- check('A3-monitoring-shows-points',
- codesOf(monitoring).length === 3,
- `运行监测 ${codesOf(monitoring).join(' ')}`)
- check('A4-warning-only-abnormal-points',
- codesOf(warning).length === 2 &&
- codesOf(warning).includes('monitoring-point|MP-A') &&
- codesOf(warning).includes('unassociated-equipment|EQ-FREE-1'),
- `预警处置 ${codesOf(warning).join(' ')}(正常点 MP-B 不在、异常未关联设备在)`)
- // 关联设备图层按票 05 锁定的范围:概览/运行监测全部,基础设施隐藏,预警处置仅 ALARM/WARNING。
- check('A5-associated-device-matrix',
- overview.associatedDevices.length === 2 &&
- monitoring.associatedDevices.length === 2 &&
- infra.associatedDevices.length === 0 &&
- warning.associatedDevices.length === 1 && warning.associatedDevices[0].code === 'EQ-A1',
- `关联设备各模式 ${[overview, infra, monitoring, warning].map((p) => p.associatedDevices.length).join('/')}`)
- // 异常点判定只认 WARNING/ALARM:工单/离线不混入(规格 Q8)。
- const onlyWorkOrder = planModeLayers({
- mode: MODE_LAYER.WARNING_RESPONSE,
- objects: fixtureObjects(),
- abnormalDevices: [
- { code: 'EQ-A1', status: 'WORK_ORDER' },
- { code: 'EQ-FREE-1', status: 'OFFLINE' },
- ],
- entered: true,
- })
- check('A6-work-order-offline-not-abnormal',
- onlyWorkOrder.markers.length === 0,
- `工单/离线不构成异常点(标记 ${onlyWorkOrder.markers.length} 个)`)
- // 异常设备数据未就绪 → 不猜:预警处置不画任何点,且给降级说明(规格:不借用点位聚合状态)。
- const noAbnormalData = planOf(MODE_LAYER.WARNING_RESPONSE, { abnormalDevices: null })
- check('A7-abnormal-data-missing-degrades-honestly',
- noAbnormalData.markers.length === 0 &&
- noAbnormalData.degradedNotes.some((note) => note.key === 'abnormal-devices-unready'),
- `异常数据未就绪:标记 ${noAbnormalData.markers.length} 个,降级说明 ${
- noAbnormalData.degradedNotes.map((n) => n.key).join(',') || '无'
- }`)
- // 管线四模式常驻(进入后);设施只在概览/基础设施可见。
- const pipelinePlan = (mode) => planModeLayers({
- mode,
- objects: fixtureObjects(),
- abnormalDevices: fixtureAbnormal(),
- pipelines: [],
- entered: true,
- })
- check('A8-pipeline-resident-in-all-modes',
- [MODE_LAYER.OVERVIEW, MODE_LAYER.INFRASTRUCTURE, MODE_LAYER.MONITORING, MODE_LAYER.WARNING_RESPONSE]
- .every((mode) => pipelinePlan(mode).layerStates.pipelines === 'ready'),
- '管线在四模式均为可见层(契约就绪=ready;未就绪应是 degraded 而非 hidden)')
- // 未就绪也不许被模式隐藏(常驻 ≠ 数据就绪才常驻):
- const pipelineUnready = (mode) => planModeLayers({
- mode,
- objects: fixtureObjects(),
- abnormalDevices: fixtureAbnormal(),
- pipelines: null,
- entered: true,
- })
- check('A8b-pipeline-unready-still-visible-not-hidden',
- [MODE_LAYER.OVERVIEW, MODE_LAYER.INFRASTRUCTURE, MODE_LAYER.MONITORING, MODE_LAYER.WARNING_RESPONSE]
- .every((mode) => pipelineUnready(mode).layerStates.pipelines === 'degraded'),
- '管线数据未就绪四模式都是 degraded(degraded ≠ hidden:不把缺数据伪装成"该模式没有")')
- check('A9-facility-only-overview-infrastructure',
- pipelinePlan(MODE_LAYER.OVERVIEW).layerStates.facilities === 'degraded' &&
- pipelinePlan(MODE_LAYER.INFRASTRUCTURE).layerStates.facilities === 'degraded' &&
- pipelinePlan(MODE_LAYER.MONITORING).layerStates.facilities === 'hidden' &&
- pipelinePlan(MODE_LAYER.WARNING_RESPONSE).layerStates.facilities === 'hidden',
- '设施点只在概览/基础设施可见,运行监测/预警处置隐藏')
- }
- // ================================================================ B 引导态门控
- {
- const plan = planModeLayers({
- mode: MODE_LAYER.OVERVIEW,
- objects: fixtureObjects(),
- abnormalDevices: fixtureAbnormal(),
- associatedDevices: fixtureAssociated(),
- entered: false,
- })
- check('B1-intro-plan-empty',
- plan.markers.length === 0 && plan.legend.length === 0 && plan.locateTargets.length === 0 &&
- plan.associatedDevices.length === 0 && plan.facilities.length === 0 && plan.pipelines.length === 0 &&
- plan.selection.visible === false,
- `引导态全空(markers ${plan.markers.length} / legend ${plan.legend.length} / targets ${plan.locateTargets.length})`)
- }
- // ================================================================ C 图例与标记同源
- {
- for (const mode of Object.values(MODE_LAYER)) {
- const plan = planOf(mode)
- const verdict = legendMatchesMarkers(plan.legend, plan.markers)
- check(`C1-legend-matches-markers-${mode}`, verdict.ok, verdict.problems.join(';') || '图例与标记逐键一致')
- }
- }
- // ================================================================ D 回联目标
- {
- const warning = planOf(MODE_LAYER.WARNING_RESPONSE)
- check('D1-locate-targets-follow-mode',
- warning.locateTargets.length === 2 &&
- warning.locateTargets.every((t) => ['MP-A', 'EQ-FREE-1'].includes(t.code)),
- `预警处置回联目标 ${warning.locateTargets.map((t) => t.code).join('/')}`)
- const infra = planOf(MODE_LAYER.INFRASTRUCTURE)
- check('D2-infrastructure-has-no-monitoring-targets',
- infra.locateTargets.length === 0,
- '基础设施模式下没有监测点回联目标(不拿监测点坐标冒充设施)')
- }
- // ================================================================ E 降级说明(数据未就绪 ≠ 空数据)
- {
- const withArrays = planModeLayers({
- mode: MODE_LAYER.INFRASTRUCTURE,
- objects: [],
- facilities: [],
- pipelines: [],
- entered: true,
- })
- check('E1-ready-layers-no-note',
- withArrays.degradedNotes.length === 0 && withArrays.layerStates.facilities === 'ready',
- '设施/管线契约就绪(空数组也是就绪)时无降级说明')
- const withoutArrays = planModeLayers({
- mode: MODE_LAYER.INFRASTRUCTURE,
- objects: [],
- facilities: null,
- pipelines: null,
- entered: true,
- })
- check('E2-unready-layers-degraded-notes',
- withoutArrays.degradedNotes.some((n) => n.key === 'facilities-unready') &&
- withoutArrays.degradedNotes.some((n) => n.key === 'pipelines-unready') &&
- withoutArrays.layerStates.facilities === 'degraded',
- `未就绪说明 ${withoutArrays.degradedNotes.map((n) => n.key).join(',')}`)
- }
- // ================================================================ F 选择态语义
- {
- const pointSelection = { level: 'point', pointCode: 'MP-B', deviceCode: '' }
- // 总览↔分项:保留身份;目标模式显示不了就暂停(visible=false),切回自动恢复。
- const infra = planOf(MODE_LAYER.INFRASTRUCTURE, { selection: pointSelection })
- const back = planOf(MODE_LAYER.OVERVIEW, { selection: pointSelection })
- check('F1-preserve-suspends-in-infrastructure',
- infra.selection.visible === false && back.selection.visible === true,
- `选 MP-B:基础设施暂停(visible=${infra.selection.visible})、切回总览恢复(visible=${back.selection.visible})`)
- // 分项↔分项:清空。
- const t = planModeSelectionTransition({ fromMode: 'monitoring', toMode: 'warningResponse', selection: pointSelection })
- check('F2-submode-switch-clears-selection',
- t.action === MODE_SELECTION.CLEAR && t.selection.level === 'none',
- '分项↔分项清空选择')
- const t2 = planModeSelectionTransition({ fromMode: 'overview', toMode: 'monitoring', selection: pointSelection })
- check('F3-overview-submode-preserves',
- t2.action === MODE_SELECTION.PRESERVE && t2.selection.pointCode === 'MP-B',
- '总览↔分项保留选择身份')
- // 选中的设备在预警处置隐藏(NORMAL)→ 暂停;报警设备可见。
- const normalDeviceSelection = { level: 'equipment', pointCode: 'MP-A', deviceCode: 'EQ-A2' }
- const warningHidden = planOf(MODE_LAYER.WARNING_RESPONSE, { selection: normalDeviceSelection })
- const alarmDeviceSelection = { level: 'equipment', pointCode: 'MP-A', deviceCode: 'EQ-A1' }
- const warningShown = planOf(MODE_LAYER.WARNING_RESPONSE, { selection: alarmDeviceSelection })
- check('F4-device-selection-pauses-by-mode',
- warningHidden.selection.visible === false && warningShown.selection.visible === true,
- `NORMAL 设备选中在预警处置暂停(${warningHidden.selection.visible})、ALARM 设备可见(${warningShown.selection.visible})`)
- // 未关联设备选中:基础设施隐藏,预警处置只认异常的那台。
- const freeSelection = { level: 'equipment', pointCode: '', deviceCode: 'EQ-FREE-1' }
- const infraFree = planOf(MODE_LAYER.INFRASTRUCTURE, { selection: freeSelection })
- const warningFree = planOf(MODE_LAYER.WARNING_RESPONSE, { selection: freeSelection })
- check('F5-free-equipment-selection',
- infraFree.selection.visible === false && warningFree.selection.visible === true,
- `未关联设备在基础设施暂停(${infraFree.selection.visible})、预警处置可见(${warningFree.selection.visible})`)
- // 回归(真机人工走查抓到):设备级选择 + **不上图**(`no-location`,现网正式边界
- // `UNVERIFIED_COORDINATE/renderable=false`)的设备 → 详情仍应可见。
- // 详情面板与该设备能否上图无关 —— 一台无地图位置的设备仍要看档案/指标/预警分区。
- // 曾误判为不可见导致外壳 `<aside v-if>` 卸载、点击设备卡片后面板直接消失。
- const noLocationRows = [
- { code: 'EQ-NL-1', pointCode: 'MP-A', status: 'ALARM', placement: 'no-location', longitude: null, latitude: null },
- ]
- const nlPoint = planModeLayers({
- mode: MODE_LAYER.OVERVIEW,
- objects: fixtureObjects(),
- associatedDevices: noLocationRows,
- selection: { level: 'equipment', pointCode: 'MP-A', deviceCode: 'EQ-NL-1' },
- entered: true,
- })
- check('F6-no-location-device-detail-visible',
- nlPoint.selection.visible === true,
- `不上图设备(no-location)的设备级详情可见(visible=${nlPoint.selection.visible})—— 详情与能否上图无关`)
- }
- // ================================================================ G 模式判据唯一性(关联设备)
- {
- const STATUSES = ['ALARM', 'WARNING', 'WORK_ORDER', 'NORMAL', 'OFFLINE', 'UNKNOWN']
- check('G1-infrastructure-hides-all-associated',
- STATUSES.every((status) => !isAssociatedDeviceVisible('infrastructure', status)),
- '基础设施对任何设备状态都隐藏')
- check('G2-warning-only-attention-statuses',
- isAssociatedDeviceVisible('warningResponse', 'ALARM') &&
- isAssociatedDeviceVisible('warningResponse', 'WARNING') &&
- !['WORK_ORDER', 'NORMAL', 'OFFLINE', 'UNKNOWN'].some((status) => isAssociatedDeviceVisible('warningResponse', status)),
- '预警处置只认设备自身 ALARM/WARNING(未知不猜)')
- check('G3-overview-monitoring-show-all',
- STATUSES.every((status) =>
- isAssociatedDeviceVisible('overview', status) && isAssociatedDeviceVisible('monitoring', status)),
- '概览/运行监测显示全部(状态未知也显示,不凭状态隐藏)')
- check('G4-attention-predicate-consistent',
- isAttentionDeviceStatus('ALARM') && isAttentionDeviceStatus('WARNING') && !isAttentionDeviceStatus('NORMAL'),
- '「需要处置」两态判据一致')
- }
- const failed = checks.filter((item) => !item.pass)
- console.log('')
- console.log(`T46 模式图层计划检查:${checks.length - failed.length}/${checks.length} 项通过。`)
- if (failed.length) {
- console.log('未通过:')
- for (const item of failed) console.log(` · ${item.name}`)
- process.exit(1)
- }
- console.log('T46 模式图层计划全部通过。')
|