From 04b5b33bb776e48b9995af31d6c119675d710f46 Mon Sep 17 00:00:00 2001 From: bot_dev1 Date: Wed, 5 Aug 2026 10:44:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=20issue=20#155=20?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=A1=AE=E8=AE=A4=E9=A1=B5=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E8=B6=85=E6=97=B6=E8=87=AA=E5=8A=A8=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=EF=BC=88ackTimeoutS=20=E5=90=8C=E6=BA=90=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin/admin.js | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/web/admin/admin.js b/web/admin/admin.js index f9b9ab8..aff8888 100644 --- a/web/admin/admin.js +++ b/web/admin/admin.js @@ -244,15 +244,18 @@ function seedAlerts() { return [ { id: "ALM-2001", severity: "P0", point: "CLF-01.TEMP", message: "氯化炉温度骤升越上限(920℃ > 900℃)", - time: new Date(now - 12 * 60000).toLocaleString("zh-CN"), status: "pending", + time: new Date(now - 12 * 60000).toLocaleString("zh-CN"), ts: now - 12 * 60000, + status: "pending", sop: "1) 现场确认热电偶;2) 降氯气进料 10%;3) 通知值班长;4) 必要时切旁路。" }, { id: "ALM-2002", severity: "P1", point: "RF-01.VAC", message: "还原炉真空度波动超阈值", - time: new Date(now - 45 * 60000).toLocaleString("zh-CN"), status: "pending", + time: new Date(now - 45 * 60000).toLocaleString("zh-CN"), ts: now - 45 * 60000, + status: "pending", sop: "1) 检查真空泵组;2) 核查泄漏点;3) 加强监控 30 分钟。" }, { id: "ALM-2003", severity: "P2", point: "ST-01.STEAM", message: "蒸汽流量低于经验区间", - time: new Date(now - 90 * 60000).toLocaleString("zh-CN"), status: "acked", + time: new Date(now - 90 * 60000).toLocaleString("zh-CN"), ts: now - 90 * 60000, + status: "acked", sop: "记录趋势,纳入班报跟踪。" } ]; } @@ -261,7 +264,29 @@ function getAlerts() { if (!a) { a = seedAlerts(); save(ALERTS_KEY, a); } return a; } +/* issue #155:确认超时自动升级(alarm_panel.ti.yaml ackTimeoutS=300, + * 对齐 alarm_config.py 的 ack_timeout_s 语义:待处理超过阈值 severity 升一级, + * 每次渲染前检查一次,升级落审计)。 */ +var SEV_UP = { P2: "P1", P1: "P0" }; +var ackTimeoutS = 300; // alarm plan 加载后被 severityColors 同源配置覆盖 +function escalateOverdueAlerts() { + var alerts = getAlerts(); + var changed = false; + alerts.forEach(function (a) { + if (a.status !== "pending" || !a.ts || !SEV_UP[a.severity]) return; + if (Date.now() - a.ts > ackTimeoutS * 1000 && !a.escalated) { + var from = a.severity; + a.severity = SEV_UP[a.severity]; + a.escalated = true; + changed = true; + audit("update", "alert", a.id + " 超时未确认(>" + ackTimeoutS + "s),severity " + + from + " → " + a.severity); + } + }); + if (changed) save(ALERTS_KEY, alerts); +} function renderAlerts() { + escalateOverdueAlerts(); var filter = document.getElementById("alert-filter").value; var box = document.getElementById("alert-list"); box.innerHTML = ""; @@ -382,11 +407,15 @@ function renderAudit() { download("audit-export-" + Date.now() + ".json", JSON.stringify(typeof UserStore !== "undefined" ? UserStore.auditLog() : [], null, 2)); }; - // severity 配色资产(与驾驶舱同源;取不到用内置兜底) + // severity 配色 + ackTimeoutS 资产(与驾驶舱同源;取不到用内置兜底) fetch("../cockpit/plans/alarm_panel.ti.json").then(function (r) { return r.ok ? r.json() : null; }).then(function (p) { - if (p && p.severityStyles) { severityStyles = p.severityStyles; renderAlerts(); } + if (p) { + if (p.severityStyles) severityStyles = p.severityStyles; + if (p.ackTimeoutS) ackTimeoutS = p.ackTimeoutS; // #155 超时升级阈值同源 + renderAlerts(); + } }).catch(function () {}); renderModels(); loadKbFromCitations(); renderAlerts(); renderAudit(); }