feat(event): add RemLocal/RemRecovered events for REM-triggered auto-stop

When any equipment's REM signal switches to local mode, fire a dedicated
`unit.rem_local` event (with unit + equipment context) and record it to
the event log. Also fire `unit.rem_recovered` when all REM signals return
to remote. AutoControlStopped is still fired alongside RemLocal when
auto was running at the time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-03-27 10:39:17 +08:00
parent 4227747852
commit 7ae952f93e
2 changed files with 55 additions and 4 deletions

View File

@ -326,6 +326,22 @@ async fn check_fault_comm(
.unwrap_or(false)
});
// Find the first equipment that just switched to local (for event payload).
let rem_local_eq_id = if any_rem_local && !runtime.rem_local {
all_roles
.iter()
.find(|(_, roles)| {
roles
.get("rem")
.and_then(|rp| monitor.get(&rp.point_id))
.map(|m| !super::monitor_value_as_bool(m) && m.quality == PointQuality::Good)
.unwrap_or(false)
})
.map(|(eq_id, _)| *eq_id)
} else {
None
};
drop(monitor);
let prev_comm = runtime.comm_locked;
@ -362,10 +378,18 @@ async fn check_fault_comm(
}
}
// Stop auto-control when any equipment switches to local mode.
if any_rem_local && runtime.auto_enabled {
runtime.auto_enabled = false;
let _ = state.event_manager.send(AppEvent::AutoControlStopped { unit_id: unit.id });
// Fire RemLocal event when any equipment first switches to local mode.
if let Some(eq_id) = rem_local_eq_id {
let _ = state.event_manager.send(AppEvent::RemLocal { unit_id: unit.id, equipment_id: eq_id });
if runtime.auto_enabled {
runtime.auto_enabled = false;
let _ = state.event_manager.send(AppEvent::AutoControlStopped { unit_id: unit.id });
}
}
// Fire RemRecovered when all rem signals return to remote.
if prev_rem_local && !any_rem_local {
let _ = state.event_manager.send(AppEvent::RemRecovered { unit_id: unit.id });
}
runtime.comm_locked != prev_comm

View File

@ -41,6 +41,8 @@ pub enum AppEvent {
FaultAcked { unit_id: Uuid },
CommLocked { unit_id: Uuid },
CommRecovered { unit_id: Uuid },
RemLocal { unit_id: Uuid, equipment_id: Uuid },
RemRecovered { unit_id: Uuid },
UnitStateChanged { unit_id: Uuid, from_state: String, to_state: String },
PointNewValue(crate::telemetry::PointNewValue),
}
@ -245,6 +247,12 @@ async fn handle_control_event(
AppEvent::CommRecovered { unit_id } => {
tracing::info!("Comm recovered for unit {}", unit_id);
}
AppEvent::RemLocal { unit_id, equipment_id } => {
tracing::warn!("REM local: unit={}, equipment={}", unit_id, equipment_id);
}
AppEvent::RemRecovered { unit_id } => {
tracing::info!("REM recovered for unit {}", unit_id);
}
AppEvent::UnitStateChanged { unit_id, from_state, to_state } => {
tracing::info!("Unit {} state: {} → {}", unit_id, from_state, to_state);
}
@ -413,6 +421,25 @@ async fn persist_event_if_needed(
serde_json::json!({ "unit_id": unit_id }),
))
}
AppEvent::RemLocal { unit_id, equipment_id } => {
let unit_code = fetch_unit_code(pool, *unit_id).await;
let eq_code = fetch_equipment_code(pool, *equipment_id).await;
Some((
"unit.rem_local", "warn",
Some(*unit_id), Some(*equipment_id), None,
format!("单元【{}】切换为本地控制,触发设备:{},自动控制已停止", unit_code, eq_code),
serde_json::json!({ "unit_id": unit_id, "equipment_id": equipment_id }),
))
}
AppEvent::RemRecovered { unit_id } => {
let code = fetch_unit_code(pool, *unit_id).await;
Some((
"unit.rem_recovered", "info",
Some(*unit_id), None, None,
format!("单元【{}】已切换回远程控制", code),
serde_json::json!({ "unit_id": unit_id }),
))
}
AppEvent::UnitStateChanged { .. } => None,
AppEvent::PointNewValue(_) => None,
};