| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- import { getMonitoringPointStatus } from './gisMapColors.js'
- import { isActiveWorkOrderStatus } from './waterSupplyUiModel.js'
- /**
- * 监测报警一张图的点位状态阈值:
- * 同一设备只保留一个点位,点位状态按“关联工单 > 报警 > 预警 > 离线 > 正常”取最高风险语义。
- * 该状态只服务于地图着色,不替代设备报警状态、报警记录状态或工单状态。
- */
- const alarmLevelLabels = { 1: '一级报警', 2: '二级报警', 3: '三级报警', 4: '四级报警' }
- export function deriveAlarmGisStatus(point = {}) {
- const sharedStatus = getMonitoringPointStatus(point)
- if (sharedStatus !== 'normal') return sharedStatus
- const hasAlarmEvent = point.alarmId ?? point.id ?? point.warningType ?? point.alarmType
- return hasAlarmEvent && Number(point.recordStatus) !== 1 ? 'alarm' : 'normal'
- }
- const displayStatusLabels = { 1: '待审核', 2: '已转工单', 3: '已解除' }
- function deviceKeyOf(record = {}) {
- const key = record.deviceCode ?? record.equipmentCode ?? record.equipmentId
- return key === null || key === undefined ? '' : String(key)
- }
- function hasCoordinate(value) {
- return String(value ?? '').trim() !== '' && Number.isFinite(Number(value))
- }
- function numberOrUndefined(value) {
- return value === null || value === undefined || value === '' ? undefined : Number(value)
- }
- function toText(...values) {
- const found = values.find(value => value !== null && value !== undefined && String(value).trim() !== '')
- return found === undefined ? undefined : found
- }
- /** 台账按设备建索引时优先保留仍有关联工单的那条报警 */
- function preferLedgerRow(candidate, current) {
- if (!current) return true
- const candidateActive = Boolean(candidate.orderNo) && isActiveWorkOrderStatus(candidate.orderStatus)
- const currentActive = Boolean(current.orderNo) && isActiveWorkOrderStatus(current.orderStatus)
- return candidateActive && !currentActive
- }
- function indexLedgerRows(ledgerRows) {
- const byAlarmId = new Map()
- const byDeviceCode = new Map()
- ledgerRows.forEach((row) => {
- if (!row) return
- const alarmId = row.id ?? row.alarmId
- if (alarmId !== null && alarmId !== undefined && !byAlarmId.has(String(alarmId))) {
- byAlarmId.set(String(alarmId), row)
- }
- const deviceCode = deviceKeyOf(row)
- if (!deviceCode) return
- if (preferLedgerRow(row, byDeviceCode.get(deviceCode)) || !byDeviceCode.has(deviceCode)) {
- byDeviceCode.set(deviceCode, row)
- }
- })
- return { byAlarmId, byDeviceCode }
- }
- function buildDevicePointIndex(devicePoints) {
- const byDeviceCode = new Map()
- devicePoints.forEach((device) => {
- const deviceCode = deviceKeyOf(device)
- if (deviceCode && !byDeviceCode.has(deviceCode)) byDeviceCode.set(deviceCode, device)
- })
- return byDeviceCode
- }
- function pickRepresentative(alarms) {
- return [...alarms].sort((a, b) => {
- const levelA = numberOrUndefined(a.alarmLevel)
- const levelB = numberOrUndefined(b.alarmLevel)
- if (levelA === undefined && levelB === undefined) return 0
- if (levelA === undefined) return 1
- if (levelB === undefined) return -1
- return levelA - levelB
- })[0]
- }
- /**
- * 报警点位归一化:只保留有坐标的报警点,同一设备合并为一个点位,
- * 并用报警台账与设备点位补齐设备报警状态、报警记录状态、报警展示状态和关联工单状态。
- * 三类状态各自成字段,任何一类缺失都不会用另一类顶替。
- */
- export function buildAlarmGisPoints({ alarmPoints = [], ledgerRows = [], devicePoints = [] } = {}) {
- const alarmList = (Array.isArray(alarmPoints) ? alarmPoints : [])
- .filter(point => point && hasCoordinate(point.longitude ?? point.lng) && hasCoordinate(point.latitude ?? point.lat))
- if (!alarmList.length) return []
- const { byAlarmId, byDeviceCode: ledgerByDeviceCode } = indexLedgerRows(Array.isArray(ledgerRows) ? ledgerRows : [])
- const deviceByDeviceCode = buildDevicePointIndex(Array.isArray(devicePoints) ? devicePoints : [])
- const groups = new Map()
- alarmList.forEach((point, index) => {
- const key = deviceKeyOf(point) || `alarm-${point.alarmId ?? index}`
- const group = groups.get(key) || []
- group.push(point)
- groups.set(key, group)
- })
- const built = []
- groups.forEach((alarms, deviceCode) => {
- const representative = pickRepresentative(alarms)
- const alarmId = representative.alarmId ?? representative.id
- const ledgerRow = (alarmId !== undefined && byAlarmId.get(String(alarmId)))
- || ledgerByDeviceCode.get(deviceCode)
- || {}
- const devicePoint = deviceByDeviceCode.get(deviceCode) || {}
- const recordStatus = numberOrUndefined(ledgerRow.alarmStatus)
- const deviceAlarmStatus = numberOrUndefined(devicePoint.alarmStatus)
- ?? numberOrUndefined(ledgerRow.deviceAlarmStatus)
- const displayStatus = numberOrUndefined(ledgerRow.displayStatus)
- const longitude = Number(representative.longitude ?? representative.lng)
- const latitude = Number(representative.latitude ?? representative.lat)
- const alarmLevel = numberOrUndefined(representative.alarmLevel)
- const point = {
- ...representative,
- deviceCode: toText(representative.deviceCode, deviceKeyOf(ledgerRow), deviceKeyOf(devicePoint), deviceCode),
- deviceName: toText(representative.deviceName, devicePoint.equipmentName, ledgerRow.deviceName),
- longitude,
- latitude,
- lng: longitude,
- lat: latitude,
- alarmCount: alarms.length,
- alarmLevel,
- alarmLevelText: toText(representative.alarmLevelText, alarmLevelLabels[alarmLevel]),
- recordStatus,
- recordStatusText: toText(ledgerRow.alarmStatusText, recordStatus === undefined ? '未获取' : recordStatus === 1 ? '已处理' : '待处理'),
- deviceAlarmStatus,
- deviceAlarmStatusText: toText(ledgerRow.deviceAlarmStatusText, deviceAlarmStatus === undefined ? '未获取' : deviceAlarmStatus === 1 ? '报警中' : '正常'),
- displayStatus,
- displayStatusText: toText(ledgerRow.displayStatusText, displayStatusLabels[displayStatus]),
- orderNo: toText(ledgerRow.orderNo, representative.orderNo),
- orderStatus: numberOrUndefined(ledgerRow.orderStatus) ?? numberOrUndefined(representative.orderStatus),
- onlineStatus: numberOrUndefined(devicePoint.onlineStatus) ?? numberOrUndefined(representative.onlineStatus),
- currentStatus: numberOrUndefined(devicePoint.currentStatus) ?? numberOrUndefined(representative.currentStatus),
- onlineStatusText: toText(devicePoint.onlineStatusText, representative.onlineStatusText),
- currentStatusText: toText(devicePoint.currentStatusText, representative.currentStatusText)
- }
- point.statusKey = deriveAlarmGisStatus(point)
- built.push(point)
- })
- return built.sort((a, b) => {
- const levelA = a.alarmLevel ?? Number.MAX_SAFE_INTEGER
- const levelB = b.alarmLevel ?? Number.MAX_SAFE_INTEGER
- return levelA - levelB
- })
- }
- const searchableFields = ['deviceName', 'deviceCode', 'name', 'warningType', 'alarmType', 'equipmentLocation', 'location']
- export function filterAlarmGisPoints(points = [], keyword = '') {
- const list = Array.isArray(points) ? points : []
- const needle = String(keyword ?? '').trim().toLocaleLowerCase()
- if (!needle) return list
- return list.filter((point) => searchableFields
- .some(field => String(point?.[field] ?? '').toLocaleLowerCase().includes(needle)))
- }
- export function summarizeAlarmGisPoints(points = []) {
- const list = Array.isArray(points) ? points : []
- const byStatus = { normal: 0, alarm: 0, warning: 0, workOrder: 0, offline: 0 }
- let highLevel = 0
- list.forEach((point) => {
- const statusKey = point?.statusKey ?? deriveAlarmGisStatus(point)
- if (Object.prototype.hasOwnProperty.call(byStatus, statusKey)) byStatus[statusKey] += 1
- const alarmLevel = numberOrUndefined(point?.alarmLevel)
- if (alarmLevel !== undefined && alarmLevel <= 2) highLevel += 1
- })
- return { total: list.length, highLevel, byStatus }
- }
|