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 <noreply@anthropic.com>
This commit is contained in:
parent
f37924ae36
commit
00c16ae3d7
|
|
@ -96,7 +96,7 @@ async fn unit_task(state: AppState, store: Arc<ControlRuntimeStore>, unit_id: Uu
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Wait when not active ──────────────────────────────────────────────
|
// ── 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! {
|
tokio::select! {
|
||||||
_ = fault_tick.tick() => {}
|
_ = fault_tick.tick() => {}
|
||||||
_ = notify.notified() => {
|
_ = notify.notified() => {
|
||||||
|
|
@ -261,7 +261,7 @@ async fn wait_phase(
|
||||||
store.upsert(runtime.clone()).await;
|
store.upsert(runtime.clone()).await;
|
||||||
push_ws(state, &runtime).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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -488,6 +488,18 @@ pub async fn start_auto_unit(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut runtime = state.control_runtime.get_or_init(unit_id).await;
|
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.auto_enabled = true;
|
||||||
runtime.state = crate::control::runtime::UnitRuntimeState::Stopped;
|
runtime.state = crate::control::runtime::UnitRuntimeState::Stopped;
|
||||||
state.control_runtime.upsert(runtime).await;
|
state.control_runtime.upsert(runtime).await;
|
||||||
|
|
@ -529,7 +541,7 @@ pub async fn batch_start_auto(
|
||||||
skipped.push(unit.id);
|
skipped.push(unit.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if runtime.fault_locked || runtime.comm_locked {
|
if runtime.fault_locked || runtime.comm_locked || runtime.manual_ack_required {
|
||||||
skipped.push(unit.id);
|
skipped.push(unit.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,14 @@ export function renderOpsUnits() {
|
||||||
const actions = item.querySelector(".ops-unit-item-actions");
|
const actions = item.querySelector(".ops-unit-item-actions");
|
||||||
|
|
||||||
const isAutoOn = runtime?.auto_enabled;
|
const isAutoOn = runtime?.auto_enabled;
|
||||||
|
const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required);
|
||||||
const autoBtn = document.createElement("button");
|
const autoBtn = document.createElement("button");
|
||||||
autoBtn.className = isAutoOn ? "danger" : "secondary";
|
autoBtn.className = isAutoOn ? "danger" : "secondary";
|
||||||
autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
|
autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
|
||||||
autoBtn.title = isAutoOn ? "停止自动控制" : "启动自动控制";
|
autoBtn.disabled = startBlocked;
|
||||||
|
autoBtn.title = startBlocked
|
||||||
|
? (runtime?.fault_locked ? "设备故障中,无法启动自动控制" : "需人工确认故障后才可启动自动控制")
|
||||||
|
: (isAutoOn ? "停止自动控制" : "启动自动控制");
|
||||||
autoBtn.addEventListener("click", (e) => {
|
autoBtn.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
apiFetch(`/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`, { method: "POST" })
|
apiFetch(`/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`, { method: "POST" })
|
||||||
|
|
|
||||||
|
|
@ -151,10 +151,14 @@ export function renderUnits() {
|
||||||
actions.append(editBtn, deleteBtn);
|
actions.append(editBtn, deleteBtn);
|
||||||
|
|
||||||
const isAutoOn = runtime?.auto_enabled;
|
const isAutoOn = runtime?.auto_enabled;
|
||||||
|
const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required);
|
||||||
const autoBtn = document.createElement("button");
|
const autoBtn = document.createElement("button");
|
||||||
autoBtn.className = isAutoOn ? "danger" : "secondary";
|
autoBtn.className = isAutoOn ? "danger" : "secondary";
|
||||||
autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
|
autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
|
||||||
autoBtn.title = isAutoOn ? "停止自动控制" : "启动自动控制";
|
autoBtn.disabled = startBlocked;
|
||||||
|
autoBtn.title = startBlocked
|
||||||
|
? (runtime?.fault_locked ? "设备故障中,无法启动自动控制" : "需人工确认故障后才可启动自动控制")
|
||||||
|
: (isAutoOn ? "停止自动控制" : "启动自动控制");
|
||||||
autoBtn.addEventListener("click", (e) => {
|
autoBtn.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const url = `/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`;
|
const url = `/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue