| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /**
- * 一级模式图层计划(票 46;规格 18 / 决策票 13)。
- *
- * 这是「一张图什么时候显示什么」的唯一领域接缝:不碰 Vue/Cesium/接口,不在组件或引擎
- * 二次过滤。设施点、管线和异常设备数据尚未全部具备时,计划如实输出 degradedNotes,
- * 不把缺数据伪装成空数据,也不伪造设施/管线标记。
- */
- import { MAP_OBJECT_KIND } from './mapSummary.js'
- import { planPointMarkers, pointLegend } from './pointStatus.js'
- export const MODE_LAYER = {
- OVERVIEW: 'overview',
- INFRASTRUCTURE: 'infrastructure',
- MONITORING: 'monitoring',
- WARNING_RESPONSE: 'warningResponse',
- }
- export const MODE_SELECTION = {
- PRESERVE: 'preserve',
- CLEAR: 'clear',
- }
- const ATTENTION_STATUSES = new Set(['ALARM', 'WARNING'])
- const MODE_KEYS = new Set(Object.values(MODE_LAYER))
- function hasArray(value) {
- return Array.isArray(value)
- }
- function note(key, text, mode) {
- return { key, text, mode }
- }
- /** 关联设备的模式过滤唯一判据;数据可用性仍由 deviceLayer.js 先判。 */
- export function isAssociatedDeviceVisible(mode, status) {
- if (mode === MODE_LAYER.INFRASTRUCTURE) return false
- if (mode === MODE_LAYER.WARNING_RESPONSE) return ATTENTION_STATUSES.has(status)
- return true
- }
- /** 总览↔分项保留选择;分项↔分项清空选择。 */
- export function planModeSelectionTransition({ fromMode, toMode, selection } = {}) {
- const current = selection || { level: 'none', pointCode: '', deviceCode: '' }
- if (!fromMode || fromMode === toMode) return { action: MODE_SELECTION.PRESERVE, selection: current }
- if (fromMode === MODE_LAYER.OVERVIEW || toMode === MODE_LAYER.OVERVIEW) {
- return { action: MODE_SELECTION.PRESERVE, selection: current }
- }
- return {
- action: MODE_SELECTION.CLEAR,
- selection: { level: 'none', pointCode: '', deviceCode: '' },
- }
- }
- function abnormalCodeSet(abnormalDevices) {
- return new Set(
- (Array.isArray(abnormalDevices) ? abnormalDevices : [])
- .filter((item) => item && ATTENTION_STATUSES.has(item.status))
- .map((item) => item.code)
- .filter(Boolean),
- )
- }
- function abnormalStatusMap(abnormalDevices) {
- return new Map(
- (Array.isArray(abnormalDevices) ? abnormalDevices : [])
- .filter((item) => item && item.code)
- .map((item) => [item.code, item.status || '']),
- )
- }
- function isAbnormalPoint(object, abnormalCodes) {
- return object && object.kind === MAP_OBJECT_KIND.MONITORING_POINT &&
- Array.isArray(object.deviceCodes) && object.deviceCodes.some((code) => abnormalCodes.has(code))
- }
- function visibleMapObjects(mode, objects, abnormalDevices) {
- const abnormalCodes = abnormalCodeSet(abnormalDevices)
- const source = Array.isArray(objects) ? objects : []
- if (mode === MODE_LAYER.INFRASTRUCTURE) return []
- if (mode === MODE_LAYER.WARNING_RESPONSE) {
- return source.filter((object) => {
- if (!object) return false
- if (object.kind === MAP_OBJECT_KIND.MONITORING_POINT) return isAbnormalPoint(object, abnormalCodes)
- if (object.kind === MAP_OBJECT_KIND.UNASSOCIATED_EQUIPMENT) return abnormalCodes.has(object.code)
- return false
- })
- }
- return source.filter((object) => object && (
- object.kind === MAP_OBJECT_KIND.MONITORING_POINT ||
- object.kind === MAP_OBJECT_KIND.UNASSOCIATED_EQUIPMENT
- ))
- }
- function planNonPointLayers({ mode, entered, facilities, pipelines, degradedNotes }) {
- const facilityVisible = mode === MODE_LAYER.OVERVIEW || mode === MODE_LAYER.INFRASTRUCTURE
- const pipelineVisible = entered && MODE_KEYS.has(mode)
- const facilityReady = hasArray(facilities)
- const pipelineReady = hasArray(pipelines)
- if (facilityVisible && !facilityReady) degradedNotes.push(note('facilities-unready', '设施数据未就绪', mode))
- if (pipelineVisible && !pipelineReady) degradedNotes.push(note('pipelines-unready', '管线数据未就绪', mode))
- // 票 33:**原样引用,不 slice()**。每 30s freshness tick 会让计划整体重算——
- // 以前这里每次 `slice()` 出新数组,`MapCanvas` 的 `deep:false` watcher 靠引用判变化,
- // 于是「数据没变」也被判成变了,批批触发管线全量重同步(票 26 诊断的放大器之一)。
- // 计划的消费者(mapService/MapCanvas)只读不写这两个数组,别名是安全的。
- return {
- facilities: facilityVisible && facilityReady ? facilities : [],
- pipelines: pipelineVisible && pipelineReady ? pipelines : [],
- layerStates: {
- facilities: facilityVisible ? (facilityReady ? 'ready' : 'degraded') : 'hidden',
- pipelines: pipelineVisible ? (pipelineReady ? 'ready' : 'degraded') : 'hidden',
- },
- }
- }
- function selectionVisible({ mode, selection, objects, markers, associatedDevices, abnormalCodes }) {
- const current = selection || {}
- if (!current.level || current.level === 'none') return false
- // 设备级选择**优先看设备**(设备级选择仍保留 pointCode 供"返回点位"用,
- // 先查 pointCode 会把"设备被本模式隐藏"误判成"点位可见 ⇒ 显示")。
- if (current.deviceCode) {
- // 该设备是**当前点位的关联设备**(在 `associatedDevices` 里找得到):
- // 详情面板与该设备能否上图无关 —— 一台 `no-location`(后端判不可上图)的设备
- // 仍然要看它的档案/指标/预警等分区,所以这里**不要求 placement === 'drawn'**,
- // 只按模式可见性(预警处置只显示报警/预警)判定。
- // (本票真机人工走查抓到:默认现网边界设备统一 `UNVERIFIED_COORDINATE/no-location`,
- // 点设备卡片后 `selectionVisible=false` ⇒ 外壳 `<aside v-if>` 卸载 ⇒ 摘要面板消失。)
- const row = (Array.isArray(associatedDevices) ? associatedDevices : [])
- .find((item) => item && item.code === current.deviceCode)
- if (row) {
- const parent = (Array.isArray(objects) ? objects : []).find((item) =>
- item && item.kind === MAP_OBJECT_KIND.MONITORING_POINT && item.code === row.pointCode)
- return Boolean(parent && isAssociatedDeviceVisible(mode, row.status) &&
- (mode !== MODE_LAYER.WARNING_RESPONSE || isAbnormalPoint(parent, abnormalCodes)))
- }
- // 未关联设备:以地图对象身份判(基础设施整体隐藏;预警处置只认异常设备)。
- const object = (Array.isArray(objects) ? objects : [])
- .find((item) => item && item.kind === MAP_OBJECT_KIND.UNASSOCIATED_EQUIPMENT && item.code === current.deviceCode)
- return Boolean(object && (mode !== MODE_LAYER.INFRASTRUCTURE) &&
- (mode !== MODE_LAYER.WARNING_RESPONSE || abnormalCodes.has(object.code)))
- }
- if (current.pointCode) {
- const point = (Array.isArray(objects) ? objects : []).find((item) =>
- item && item.kind === MAP_OBJECT_KIND.MONITORING_POINT && item.code === current.pointCode)
- return Boolean(point && mode !== MODE_LAYER.INFRASTRUCTURE &&
- (mode !== MODE_LAYER.WARNING_RESPONSE || isAbnormalPoint(point, abnormalCodes)))
- }
- return false
- }
- /**
- * 统一输出当前模式的图层计划。
- *
- * @returns {{mode:string, entered:boolean, markers:Array, blocked:Array, legend:Array,
- * locateTargets:Array, degradedNotes:Array, facilities:Array, pipelines:Array,
- * associatedDevices:Array, layerStates:object, selection:object}}
- */
- export function planModeLayers({
- mode = MODE_LAYER.OVERVIEW,
- objects = [],
- abnormalDevices,
- facilities,
- pipelines,
- associatedDevices = [],
- selection,
- entered = true,
- } = {}) {
- const normalizedMode = MODE_KEYS.has(mode) ? mode : MODE_LAYER.OVERVIEW
- const degradedNotes = []
- if (entered !== true) {
- return {
- mode: normalizedMode,
- entered: false,
- markers: [],
- blocked: [],
- legend: [],
- locateTargets: [],
- degradedNotes: [],
- facilities: [],
- pipelines: [],
- associatedDevices: [],
- layerStates: { facilities: 'hidden', pipelines: 'hidden', associatedDevices: 'hidden' },
- selection: { visible: false, action: MODE_SELECTION.PRESERVE },
- }
- }
- const source = visibleMapObjects(normalizedMode, objects, abnormalDevices)
- const pointPlan = planPointMarkers(source)
- const abnormalCodes = abnormalCodeSet(abnormalDevices)
- const abnormalDataReady = hasArray(abnormalDevices)
- if (normalizedMode === MODE_LAYER.WARNING_RESPONSE && !abnormalDataReady) {
- degradedNotes.push(note('abnormal-devices-unready', '异常设备数据未就绪,预警处置不猜测异常点', normalizedMode))
- }
- const visiblePointCodes = new Set(source.filter((item) => item.kind === MAP_OBJECT_KIND.MONITORING_POINT).map((item) => item.code))
- const visibleDevices = (Array.isArray(associatedDevices) ? associatedDevices : [])
- .filter((row) => row && visiblePointCodes.has(row.pointCode) && row.placement === 'drawn' && isAssociatedDeviceVisible(normalizedMode, row.status))
- const nonPoint = planNonPointLayers({
- mode: normalizedMode,
- entered: true,
- facilities,
- pipelines,
- degradedNotes,
- })
- const locateTargets = pointPlan.markers.map((marker) => ({
- kind: marker.kind,
- code: marker.code,
- name: marker.name,
- longitude: marker.longitude,
- latitude: marker.latitude,
- }))
- return {
- mode: normalizedMode,
- entered: true,
- // `markers` 与 `legend` 都来自同一份 pointPlan,地图服务只消费它们,不重判模式。
- markers: pointPlan.markers,
- blocked: pointPlan.blocked,
- legend: pointLegend(pointPlan.byStatus),
- byStatus: pointPlan.byStatus,
- locateTargets,
- degradedNotes,
- facilities: nonPoint.facilities,
- pipelines: nonPoint.pipelines,
- associatedDevices: visibleDevices,
- blockedAssociatedDevices: (Array.isArray(associatedDevices) ? associatedDevices : [])
- .filter((row) => row && row.placement !== 'drawn'),
- layerStates: {
- ...nonPoint.layerStates,
- associatedDevices: normalizedMode === MODE_LAYER.INFRASTRUCTURE ? 'hidden' : 'ready',
- },
- selection: {
- visible: selectionVisible({
- mode: normalizedMode,
- selection,
- objects,
- markers: pointPlan.markers,
- associatedDevices,
- abnormalCodes,
- }),
- action: MODE_SELECTION.PRESERVE,
- },
- }
- }
- /** 供测试和外壳使用的状态判断,不泄漏内部 Set。 */
- export function isAttentionDeviceStatus(status) {
- return ATTENTION_STATUSES.has(status)
- }
- /** 仅供验证说明使用:异常设备状态来源是设备事实,不是点位聚合状态。 */
- export function abnormalStatusesFromDevices(abnormalDevices) {
- return [...abnormalStatusMap(abnormalDevices).entries()].map(([code, status]) => ({ code, status }))
- }
|