/** * 设备盖子 iotData.fx / 同类字段:value 约定 0=打开,1=关闭。 * 优先展示 desc;无有效 value 时用 desc 多语言关键词判断。 */ export function isFxCoverOpen(fx) { if (!fx || typeof fx !== 'object') return false const v = fx.value if (v !== undefined && v !== null && v !== '') { if (v === 0 || v === '0' || v === false) return true if (v === 1 || v === '1' || v === true) return false } const raw = fx.desc if (raw === undefined || raw === null) return false const s = String(raw).trim() if (!s) return false const closedHintsCjk = ['关闭', '合上', '閉じ', '閉ま'] for (const h of closedHintsCjk) { if (s.indexOf(h) >= 0) return false } if (/\b(closed?|shut)\b/i.test(s)) { return false } if (/(geschlossen|fermé|ferme|cerrad[oa]|chius[oa]|закрыт|закрыто)/i.test(s)) { return false } const openHintsCjk = ['打开', '开启', '开盖', '開い', '開いてい'] for (const h of openHintsCjk) { if (s.indexOf(h) >= 0) return true } const openWordRe = /(^|[^a-z])open($|[^a-z])|opened|opening|geöffnet|geoffnet|ouvert|abiert[oa]?|aperto|открыт|открыто/i if (openWordRe.test(s)) return true return false } /** * 与 fx 同步的展示文案:有 desc 用 desc,否则用 open/close。 */ export function deviceCoverDisplayText(fx, tOpen, tClose, tNoData) { if (!fx || typeof fx !== 'object') return tNoData const d = fx.desc if (d !== undefined && d !== null && String(d).trim() !== '') { return String(fx.desc) } const v = fx.value if (v === undefined || v === null || v === '') return tNoData return isFxCoverOpen(fx) ? tOpen : tClose } /** * 标量字段(如 HW1.case_opened)与 fx.value 同约定:0=开,1=关。 */ export function isCoverOpenFromScalar(raw) { if (raw === undefined || raw === null || raw === '') return false if (raw === 0 || raw === '0' || raw === false) return true if (raw === 1 || raw === '1' || raw === true) return false const s = String(raw).trim() if (s === '0' || s === 'false') return true if (s === '1' || s === 'true') return false const n = Number(s) if (!Number.isNaN(n)) { if (n === 0) return true if (n === 1) return false } return false }