import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import vm from 'node:vm' import * as Cesium from 'cesium' import * as effects from '../../../src/modules/cockpit/domain/deviceEffects.js' import { groupOverlappingDevices, fanLayout } from '../../../src/modules/cockpit/domain/overlapDevices.js' import { EMPHASIS_STYLE, shouldEmphasize } from '../../../src/modules/cockpit/domain/mapEmphasis.js' import { MAP_LABEL_STYLE, mapLabelText } from '../../../src/modules/cockpit/domain/mapLabelStyle.js' import { createTilesetLoadingController } from '../../../src/modules/cockpit/services/tilesetLoading.js' // Execute the complete production module. Only module imports/CSS are adapted for Node; // all engine helpers and Cesium Entity/Property/DataSource implementations remain real. const original = readFileSync(new URL('../../../src/modules/cockpit/services/cesiumEngine.js', import.meta.url), 'utf8') const mutations = { 'owner-only': ["const displayOwner = representsGroup ? (entry || aggregate) : owner", 'const displayOwner = owner'], 'wrong-representative': ['overlapEffectCode: representative.code', 'overlapEffectCode: group.rows[0].code'], 'fake-visible': ['visible: source.show !== false && entity.show !== false && entity.isShowing !== false', 'visible: true'], 'skip-cleanup': ['} else {\n clearRingEntities(source)\n', '} else {\n // mutation: cleanup removed\n'], } const mutation = process.env.T47_MUTATION assert.ok(!mutation || mutations[mutation], 'known mutation') const source = mutation ? original.replace(...mutations[mutation]) : original if (mutation) assert.notEqual(source, original, 'mutation reached production code') const code = source.replace(/^import\s+[^]*?from\s+['"][^'"\n]+['"][ \t]*$/gm, '') .replace(/^import\s+['"][^'"\n]+['"][ \t]*$/gm, '') .replace(/^export /gm, '') const callbacks = new Map() let now = 300 let nextFrame = 0 const context = vm.createContext({ ...Cesium, Math, CesiumMath: Cesium.Math, ...effects, EMPHASIS_STYLE, shouldEmphasize, MAP_LABEL_STYLE, mapLabelText, createTilesetLoadingController, Date: { now: () => now }, window: { requestAnimationFrame(callback) { const id = ++nextFrame; callbacks.set(id, callback); return id }, cancelAnimationFrame(id) { callbacks.delete(id) }, }, }) vm.runInContext(`${code}\nglobalThis.engine = { syncAssociatedDevices, setOverlapPresentation, applyDeviceEffects, readDeviceEffects, readAssociatedDevices, clearAssociatedDevices, stopDeviceEffects, applyDeviceReactions, pickDeviceMarkerAt };`, context) const engine = context.engine const viewer = { dataSources: new Cesium.DataSourceCollection(), scene: { requestRender() {} }, } const rowsOf = (statuses) => statuses.map((status, index) => ({ code: `EQ-${index}`, pointCode: 'MP-T47', status, longitude: 110.3632, latitude: 28.4524, placement: 'drawn', coordinate: { renderable: true }, size: 14, shape: status === 'ALARM' ? 'triangle' : 'circle', })) let rows = rowsOf(['ALARM', 'NORMAL']) let group let plan function load(nextRows = rows, options = {}) { rows = nextRows const groups = groupOverlappingDevices(rows, { maxFan: options.maxFan || 10 }) group = groups[0] plan = effects.planDeviceEffects({ devices: rows, enabled: options.enabled !== false, abnormalLimit: options.abnormalLimit || 30 }) engine.syncAssociatedDevices(viewer, rows, () => 'red', () => 'icon', groups) engine.applyDeviceEffects(viewer, plan, { devices: rows, fxImageOf: (kind) => kind }) } function frame(time = now) { now = time const pending = [...callbacks] callbacks.clear() for (const [, callback] of pending) callback() return engine.readDeviceEffects(viewer) } function present(state, selectedCode = '', preview = false) { const fanGroup = selectedCode ? { ...group, rows: group.rows.filter((row) => row.code !== selectedCode), maxFan: Math.max(1, group.maxFan - 1) } : group const fan = selectedCode && group.maxFan <= 1 ? [] : fanLayout(fanGroup) const offsets = Object.fromEntries(fan.map((item) => [item.deviceCode, { x: item.x, y: item.y }])) engine.setOverlapPresentation(viewer, { groupKey: group.key, state, selectedCode, preview, offsets }) engine.applyDeviceEffects(viewer, plan, { devices: rows, selectedCode, fxImageOf: (kind) => kind }) return frame() } let failed = 0 let passed = 0 const check = (name, action) => { try { action(); passed += 1; console.log(`PASS ${name}`) } catch (error) { failed += 1; console.error(`FAIL ${name}: ${error.message}`) } } load() await Promise.resolve() check('collapsed overlap keeps a visible abnormal effect', () => { present('collapsed') const visible = engine.readAssociatedDevices(viewer).filter((item) => item.id.startsWith('fx|') && item.visible) assert.equal(visible.length, 5) }) check('selecting NORMAL preserves group effects without animating the NORMAL device', () => { const state = present('selected', 'EQ-1') const normal = state.devices.find((item) => item.code === 'EQ-1') assert.equal(normal.status, 'NORMAL') assert.equal(normal.effect, '') assert.equal(normal.alpha, 1) assert.ok(state.fx.some((item) => item.visible && item.displayGroup === group.key && item.status === 'ALARM')) const entry = engine.readAssociatedDevices(viewer).find((item) => item.deviceCode === 'EQ-1') assert.ok(entry.labelText.includes('组内报警')) }) const visibleFx = (state = engine.readDeviceEffects(viewer)) => state.fx.filter((item) => item.visible && item.alpha > 0) check('expanded effects follow device offsets and stop claiming group ownership', () => { const state = present('expanded') const ring = visibleFx(state).find((item) => item.kind === 'ring') assert.equal(ring.displayGroup, '') const device = engine.readAssociatedDevices(viewer).find((item) => item.deviceCode === ring.for) assert.equal(ring.pixelOffset.x, device.pixelOffset.x) assert.equal(ring.pixelOffset.y, device.pixelOffset.y) }) check('selected ALARM keeps its device effect and selected boost', () => { const normalScale = visibleFx(present('collapsed')).find((item) => item.kind === 'ring' && item.layer === 0).scale const selected = visibleFx(present('selected', 'EQ-0')) assert.ok(selected.every((item) => item.displayGroup === '')) assert.ok(selected.find((item) => item.kind === 'ring' && item.layer === 0).scale > normalScale) }) for (const [status, key] of [['ALARM', 'scale'], ['WARNING', 'alpha'], ['WORK_ORDER', 'rotation']]) { for (const staticStatus of ['NORMAL', 'OFFLINE', 'UNKNOWN']) { check(`${status} remains visible across frames behind selected ${staticStatus}`, () => { load(rowsOf([status, staticStatus])) present('selected', 'EQ-1') const at300 = visibleFx(frame(300)).find((item) => item.kind === 'ring' && item.layer === 0) const at700 = visibleFx(frame(700)).find((item) => item.kind === 'ring' && item.layer === 0) assert.ok(at300 && at700) assert.notEqual(at300[key], at700[key]) assert.equal(at700.status, status) assert.equal(at700.for, 'EQ-0') assert.equal(at700.displayGroup, group.key) assert.equal(engine.readDeviceEffects(viewer).devices.find((item) => item.code === 'EQ-1').effect, '') }) } } check('preview leave returns to a visible group effect without hiding the selected entry', () => { load(rowsOf(['ALARM', 'NORMAL'])) present('expanded', 'EQ-1', true) assert.equal(visibleFx().filter((item) => item.displayGroup).length, 0) const closed = present('selected', 'EQ-1') assert.equal(visibleFx(closed).length, 5) assert.equal(engine.readAssociatedDevices(viewer).find((item) => item.deviceCode === 'EQ-1').visible, true) }) check('K11 fallback preserves ALARM even when its device is outside the first ten', () => { load(rowsOf([...Array(10).fill('NORMAL'), 'ALARM'])) assert.equal(group.degraded, true) assert.equal(plan.degraded, false) const state = present('expanded') assert.equal(engine.readAssociatedDevices(viewer).filter((item) => item.deviceCode && item.visible).length, 10) assert.equal(visibleFx(state).length, 5) assert.ok(visibleFx(state).every((item) => item.displayGroup === group.key && item.for === 'EQ-10')) present('expanded', 'EQ-0') assert.equal(engine.readAssociatedDevices(viewer).filter((item) => item.deviceCode && item.visible).length, 10) assert.equal(visibleFx().length, 5) }) check('group status uses highest priority and does not silently change to selected WARNING', () => { load(rowsOf(['WARNING', 'ALARM', 'WORK_ORDER'])) const state = present('selected', 'EQ-0') assert.ok(visibleFx(state).some((item) => item.displayGroup === group.key && item.status === 'ALARM')) assert.ok(visibleFx(state).some((item) => !item.displayGroup && item.status === 'WARNING')) }) check('selected peer ALARM does not duplicate the same status at the group entry', () => { load(rowsOf(['ALARM', 'ALARM', 'NORMAL'])) const state = present('selected', 'EQ-1') assert.equal(visibleFx(state).length, 5) assert.ok(visibleFx(state).every((item) => item.for === 'EQ-1' && item.displayGroup === '')) }) check('only static states create no rings and no animation loop', () => { load(rowsOf(['NORMAL', 'OFFLINE', 'UNKNOWN'])) assert.equal(engine.readDeviceEffects(viewer).fx.length, 0) assert.equal(callbacks.size, 0) }) check('30 abnormal devices still animate under overlap degradation', () => { load(rowsOf(Array(30).fill('ALARM'))) assert.equal(plan.degraded, false) assert.equal(visibleFx(present('collapsed')).length, 5) assert.equal(callbacks.size, 1) }) check('31 abnormal devices stop all rings but retain a static abnormal aggregate and count', () => { load(rowsOf(Array(31).fill('ALARM'))) assert.equal(plan.degraded, true) assert.equal(plan.abnormalCount, 31) assert.equal(engine.readDeviceEffects(viewer).fx.length, 0) assert.equal(callbacks.size, 0) const aggregate = engine.readAssociatedDevices(viewer).find((item) => item.overlapAggregate) assert.equal(aggregate.visible, true) assert.equal(aggregate.deviceStatus, 'ALARM') assert.equal(aggregate.overlapCount, 31) }) check('explicit effects off removes rings without removing static device identity', () => { load(rowsOf(['ALARM', 'NORMAL'])) present('selected', 'EQ-1') engine.applyDeviceEffects(viewer, effects.planDeviceEffects({ devices: rows, enabled: false }), { devices: rows }) assert.equal(engine.readDeviceEffects(viewer).fx.length, 0) assert.equal(callbacks.size, 0) const normal = engine.readAssociatedDevices(viewer).find((item) => item.deviceCode === 'EQ-1') assert.equal(normal.visible, true) assert.equal(normal.deviceStatus, 'NORMAL') }) check('hidden data source reports no visible rings and restores correctly', () => { load(rowsOf(['ALARM', 'NORMAL'])) const dataSource = viewer.dataSources.getByName('cockpit-associated-device-markers')[0] dataSource.show = false assert.equal(visibleFx(frame()).length, 0) dataSource.show = true assert.equal(visibleFx(frame()).length, 5) }) check('stopping effects cancels pending frames and makes retained rings invisible', () => { engine.stopDeviceEffects() const state = frame() assert.equal(visibleFx(state).length, 0) assert.equal(callbacks.size, 0) }) check('same device codes with fresh status replace old ALARM rings with WORK_ORDER rings', () => { load(rowsOf(['ALARM', 'NORMAL'])) present('selected', 'EQ-1') const previousCount = engine.readDeviceEffects(viewer).fx.length load(rowsOf(['WORK_ORDER', 'NORMAL'])) const state = present('selected', 'EQ-1') assert.equal(previousCount, 5) assert.equal(state.fx.length, 4) assert.ok(visibleFx(state).every((item) => item.status === 'WORK_ORDER')) assert.ok(engine.readAssociatedDevices(viewer).find((item) => item.deviceCode === 'EQ-1').labelText.includes('组内工单处置')) }) check('replacement coordinates break overlap and old group ownership does not survive', () => { const next = rowsOf(['ALARM', 'NORMAL']) next[1].longitude += 0.001 load(next) const state = frame() assert.equal(group.k, 1) assert.equal(visibleFx(state).length, 5) assert.ok(visibleFx(state).every((item) => item.displayGroup === '')) }) check('effect entities cannot be picked as equipment', () => { load(rowsOf(['ALARM', 'NORMAL'])) present('selected', 'EQ-1') viewer.canvas = { getBoundingClientRect: () => ({ left: 0, top: 0 }) } viewer.clock = { currentTime: Cesium.JulianDate.now() } viewer.scene.globe = { ellipsoid: Cesium.Ellipsoid.WGS84, getHeight: () => 0 } const target = Cesium.Cartesian3.fromDegrees(110.3632, 28.4524) const positionWC = Cesium.Cartesian3.fromDegrees(110.3632, 28.4524, 1000) viewer.camera = { positionWC, directionWC: Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(target, positionWC, new Cesium.Cartesian3()), new Cesium.Cartesian3()) } context.SceneTransforms = { worldToWindowCoordinates: () => ({ x: 500, y: 300 }) } const dataSource = viewer.dataSources.getByName('cockpit-associated-device-markers')[0] viewer.scene.pick = () => ({ id: dataSource.entities.values.find((item) => item.properties?.effectFx) }) assert.equal(engine.pickDeviceMarkerAt(viewer, 700, 500), null) const picked = engine.pickDeviceMarkerAt(viewer, 500, 300) assert.equal(picked.code, 'EQ-1') assert.equal(picked.status, 'NORMAL') assert.equal(picked.overlapEntry, true) }) check('clearing the layer removes old effects, owners and scheduled callbacks', () => { engine.clearAssociatedDevices(viewer) assert.equal(engine.readDeviceEffects(viewer).fx.length, 0) assert.equal(engine.readAssociatedDevices(viewer).length, 0) assert.equal(callbacks.size, 0) const newRows = rowsOf(['WARNING', 'NORMAL']).map((item) => ({ ...item, code: `${item.code}-new`, pointCode: 'MP-NEW' })) load(newRows) const state = frame() assert.ok(state.fx.every((item) => item.for.endsWith('-new'))) assert.ok(state.devices.every((item) => item.code.endsWith('-new'))) }) engine.clearAssociatedDevices(viewer) console.log(`T47 overlap/effects: ${passed} passed, ${failed} failed; no browser/GPU verification.`) process.exitCode = failed ? 1 : 0