From 00c16ae3d701287fe72761d68d0ccdcef63dc64b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 26 Mar 2026 10:54:10 +0800 Subject: [PATCH] fix(unit): block auto control start when fault is active or unacknowledged Prevent starting unit auto control while fault_locked or manual_ack_required, enforcing that faults must be manually acknowledged before resuming automation. Also disable the Start Auto button in the frontend with descriptive tooltips. Co-Authored-By: Claude Sonnet 4.6 --- src/control/engine.rs | 4 ++-- src/handler/control.rs | 14 +++++++++++++- web/js/ops.js | 6 +++++- web/js/units.js | 6 +++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/control/engine.rs b/src/control/engine.rs index d376802..ca16717 100644 --- a/src/control/engine.rs +++ b/src/control/engine.rs @@ -96,7 +96,7 @@ async fn unit_task(state: AppState, store: Arc, unit_id: Uu } // ── Wait when not active ────────────────────────────────────────────── - if !runtime.auto_enabled || runtime.fault_locked || runtime.comm_locked { + if !runtime.auto_enabled || runtime.fault_locked || runtime.comm_locked || runtime.manual_ack_required { tokio::select! { _ = fault_tick.tick() => {} _ = notify.notified() => { @@ -261,7 +261,7 @@ async fn wait_phase( store.upsert(runtime.clone()).await; push_ws(state, &runtime).await; } - if !runtime.auto_enabled || runtime.fault_locked || runtime.comm_locked { + if !runtime.auto_enabled || runtime.fault_locked || runtime.comm_locked || runtime.manual_ack_required { return false; } } diff --git a/src/handler/control.rs b/src/handler/control.rs index 6909b57..686abcc 100644 --- a/src/handler/control.rs +++ b/src/handler/control.rs @@ -488,6 +488,18 @@ pub async fn start_auto_unit( } let mut runtime = state.control_runtime.get_or_init(unit_id).await; + if runtime.fault_locked { + return Err(ApiErr::BadRequest( + "Unit is fault locked, cannot start auto control".to_string(), + None, + )); + } + if runtime.manual_ack_required { + return Err(ApiErr::BadRequest( + "Fault acknowledgement required before starting auto control".to_string(), + None, + )); + } runtime.auto_enabled = true; runtime.state = crate::control::runtime::UnitRuntimeState::Stopped; state.control_runtime.upsert(runtime).await; @@ -529,7 +541,7 @@ pub async fn batch_start_auto( skipped.push(unit.id); continue; } - if runtime.fault_locked || runtime.comm_locked { + if runtime.fault_locked || runtime.comm_locked || runtime.manual_ack_required { skipped.push(unit.id); continue; } diff --git a/web/js/ops.js b/web/js/ops.js index fcf651b..3c499db 100644 --- a/web/js/ops.js +++ b/web/js/ops.js @@ -50,10 +50,14 @@ export function renderOpsUnits() { const actions = item.querySelector(".ops-unit-item-actions"); const isAutoOn = runtime?.auto_enabled; + const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required); const autoBtn = document.createElement("button"); autoBtn.className = isAutoOn ? "danger" : "secondary"; autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto"; - autoBtn.title = isAutoOn ? "停止自动控制" : "启动自动控制"; + autoBtn.disabled = startBlocked; + autoBtn.title = startBlocked + ? (runtime?.fault_locked ? "设备故障中,无法启动自动控制" : "需人工确认故障后才可启动自动控制") + : (isAutoOn ? "停止自动控制" : "启动自动控制"); autoBtn.addEventListener("click", (e) => { e.stopPropagation(); apiFetch(`/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`, { method: "POST" }) diff --git a/web/js/units.js b/web/js/units.js index ca3aa14..38bf6f9 100644 --- a/web/js/units.js +++ b/web/js/units.js @@ -151,10 +151,14 @@ export function renderUnits() { actions.append(editBtn, deleteBtn); const isAutoOn = runtime?.auto_enabled; + const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required); const autoBtn = document.createElement("button"); autoBtn.className = isAutoOn ? "danger" : "secondary"; autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto"; - autoBtn.title = isAutoOn ? "停止自动控制" : "启动自动控制"; + autoBtn.disabled = startBlocked; + autoBtn.title = startBlocked + ? (runtime?.fault_locked ? "设备故障中,无法启动自动控制" : "需人工确认故障后才可启动自动控制") + : (isAutoOn ? "停止自动控制" : "启动自动控制"); autoBtn.addEventListener("click", (e) => { e.stopPropagation(); const url = `/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`;