fix: improve map editor and monitor operations
This commit is contained in:
@@ -12,6 +12,36 @@ function kv(rows: Kv[] | null | undefined, ...keys: string[]): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
function isEmptyStatus(v: string): boolean {
|
||||
const s = v.trim()
|
||||
return !s || s === '/' || s === '-' || s === '[]' || s === '(0, 0)' || s === '(0,0)' || /^none$/i.test(s)
|
||||
}
|
||||
|
||||
function compactJsonList(raw: string, maxItems = 3): string {
|
||||
if (isEmptyStatus(raw)) return ''
|
||||
try {
|
||||
const arr = JSON.parse(raw) as unknown
|
||||
if (!Array.isArray(arr) || arr.length === 0) return ''
|
||||
const items = arr.slice(0, maxItems).map((x) => {
|
||||
if (x == null) return ''
|
||||
if (typeof x === 'number' || typeof x === 'string') return String(x)
|
||||
if (typeof x === 'object') {
|
||||
const o = x as Record<string, unknown>
|
||||
if (o.siteId != null) return `#${o.siteId}`
|
||||
if (o.siteID != null) return `#${o.siteID}`
|
||||
if (o.id != null) return `#${o.id}`
|
||||
if (o.carid != null) return `车${o.carid}`
|
||||
}
|
||||
return ''
|
||||
}).filter(Boolean)
|
||||
if (!items.length) return ''
|
||||
const more = arr.length > maxItems ? `+${arr.length - maxItems}` : ''
|
||||
return items.join(',') + more
|
||||
} catch {
|
||||
return raw.length > 24 ? `${raw.slice(0, 24)}…` : raw
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleLite /projection/cars 会把「正常但未初始化」「导航失联」等一律标成 fault,
|
||||
* 与地图标签/车体 AlarmLevel 不一致。这里用 lstatus + 车体 status 重新推导。
|
||||
@@ -28,7 +58,7 @@ export function deriveCarState(car: Pick<Car, 'state' | 'lstatus'>, statusRows?:
|
||||
const hasAlarm =
|
||||
(Number.isFinite(alarmLevel) && alarmLevel > 0) ||
|
||||
(/true/i.test(riskAlarm)) ||
|
||||
(alarmInfo.length > 0 && !/^\/$|^-$|^none$/i.test(alarmInfo))
|
||||
(alarmInfo.length > 0 && !isEmptyStatus(alarmInfo))
|
||||
|
||||
// 明确健康:以「正常」开头(含「正常但未初始化」)→ 绝不是故障
|
||||
if (/^正常/.test(lstatus)) {
|
||||
@@ -62,6 +92,90 @@ export function deriveCarState(car: Pick<Car, 'state' | 'lstatus'>, statusRows?:
|
||||
return 'idle'
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 SimpleLite 车体浮窗 CarVisual.FormatTrafficStatus 对齐:
|
||||
* 1) TCStat 含 BeforeLock → 互锁
|
||||
* 2) blockedBy 有车 →「被 X、Y 车阻塞」;仅非车阻塞 →「阻塞中」
|
||||
* 3) 有 holdingLocks → 正常拿锁;否则 → 畅通
|
||||
*/
|
||||
export function formatTrafficStatus(statusRows?: Kv[] | null): string {
|
||||
if (!statusRows?.length) return '—'
|
||||
|
||||
const tc = kv(statusRows, 'TCStat')
|
||||
if (tc && /BeforeLock/i.test(tc)) return '互锁(interlock)'
|
||||
|
||||
const blockedIds = parseBlockedByCarIds(kv(statusRows, 'blockedBy'))
|
||||
if (blockedIds != null) {
|
||||
return blockedIds.length > 0
|
||||
? `被 ${blockedIds.join('、')} 车阻塞`
|
||||
: '阻塞中'
|
||||
}
|
||||
|
||||
const holding = kv(statusRows, 'holdingLocks')
|
||||
if (holding && !isEmptyStatus(holding) && holding !== '[]') return '正常拿锁'
|
||||
return '畅通'
|
||||
}
|
||||
|
||||
/** @returns null=无阻塞;[]=阻塞但无车 id;number[]=阻塞车 id */
|
||||
function parseBlockedByCarIds(raw: string): number[] | null {
|
||||
if (!raw || isEmptyStatus(raw)) return null
|
||||
|
||||
try {
|
||||
const arr = JSON.parse(raw) as unknown
|
||||
if (!Array.isArray(arr) || arr.length === 0) return null
|
||||
const ids = arr
|
||||
.map((item) => {
|
||||
if (item == null) return null
|
||||
if (typeof item === 'number') return item
|
||||
if (typeof item === 'object') {
|
||||
const o = item as Record<string, unknown>
|
||||
const n = Number(o.carid ?? o.Item1 ?? o.item1)
|
||||
return Number.isFinite(n) ? n : null
|
||||
}
|
||||
return null
|
||||
})
|
||||
.filter((n): n is number => n != null && n !== -1)
|
||||
return [...new Set(ids)]
|
||||
} catch {
|
||||
// 兼容 ToString 形态:[(11, 2), (12, 3)]
|
||||
const ids = [...raw.matchAll(/\(\s*(-?\d+)\s*,\s*-?\d+\s*\)/g)]
|
||||
.map((m) => Number(m[1]))
|
||||
.filter((n) => Number.isFinite(n) && n !== -1)
|
||||
if (!ids.length && /\[\s*\]/.test(raw)) return null
|
||||
if (raw.includes('(') || raw.includes('[')) return [...new Set(ids)]
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function isTrafficCongested(status: string | undefined | null): boolean {
|
||||
return !!status && /阻塞|互锁|拥堵/.test(status)
|
||||
}
|
||||
|
||||
export function formatLockPoint(statusRows?: Kv[] | null, fields?: { siteId?: string }): string {
|
||||
const holding = compactJsonList(kv(statusRows, 'holdingLocks'))
|
||||
if (holding) return holding
|
||||
const pending = compactJsonList(kv(statusRows, 'pendingLocks'))
|
||||
if (pending) return `待 ${pending}`
|
||||
|
||||
const siteRaw = fields?.siteId || kv(statusRows, 'siteID', 'siteId')
|
||||
const siteId = Number(siteRaw)
|
||||
if (Number.isFinite(siteId) && siteId > 0) return `#${siteId}`
|
||||
return '—'
|
||||
}
|
||||
|
||||
export function formatAlarmInfo(statusRows?: Kv[] | null, lstatus?: string): { text: string; level: number } {
|
||||
const levelRaw = Number(kv(statusRows, '车体_AlarmLevel', 'AlarmLevel'))
|
||||
const level = Number.isFinite(levelRaw) ? levelRaw : 0
|
||||
const info = kv(statusRows, '车体_AlarmInfo', 'AlarmInfo').trim()
|
||||
if (info && !isEmptyStatus(info) && info !== '0') {
|
||||
return { text: info, level: level > 0 ? level : 1 }
|
||||
}
|
||||
if (lstatus && /故障|报警|异常|失联|超时|检修/i.test(lstatus)) {
|
||||
return { text: lstatus, level: Math.max(level, 1) }
|
||||
}
|
||||
return { text: '', level }
|
||||
}
|
||||
|
||||
export function applyRuntimeEnrichment(car: Car, statusRows?: Kv[] | null): Car {
|
||||
const socFromStatus = extractSocFromKv(statusRows)
|
||||
let batterySoc: number
|
||||
@@ -74,9 +188,18 @@ export function applyRuntimeEnrichment(car: Car, statusRows?: Kv[] | null): Car
|
||||
batterySoc = parseSocNumber(car.batterySoc) ?? 0
|
||||
}
|
||||
|
||||
const alarm = formatAlarmInfo(statusRows, car.lstatus)
|
||||
const siteRaw = kv(statusRows, 'siteID', 'siteId')
|
||||
const siteIdNum = Number(siteRaw)
|
||||
|
||||
return {
|
||||
...car,
|
||||
batterySoc,
|
||||
state: deriveCarState(car, statusRows)
|
||||
state: deriveCarState(car, statusRows),
|
||||
alarmInfo: alarm.text || undefined,
|
||||
alarmLevel: alarm.level || undefined,
|
||||
trafficStatus: formatTrafficStatus(statusRows),
|
||||
lockPoint: formatLockPoint(statusRows, { siteId: siteRaw }),
|
||||
siteId: Number.isFinite(siteIdNum) && siteIdNum > 0 ? siteIdNum : undefined
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user