feat(control): batch start/stop auto control for all enabled units
Backend: - POST /api/control/unit/batch-start-auto — starts auto on all enabled units that are not fault/comm locked and not already running auto - POST /api/control/unit/batch-stop-auto — stops auto on all units Frontend (ops view): - Add "全部启动" / "全部停止" buttons in the unit sidebar header Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0077a4ad90
commit
757d6f9a3a
|
|
@ -410,6 +410,58 @@ pub async fn stop_auto_unit(
|
|||
Ok(Json(json!({ "ok_msg": "Auto control stopped", "unit_id": unit_id })))
|
||||
}
|
||||
|
||||
pub async fn batch_start_auto(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiErr> {
|
||||
let units = crate::service::get_all_enabled_units(&state.pool).await?;
|
||||
let mut started = Vec::new();
|
||||
let mut skipped = Vec::new();
|
||||
|
||||
for unit in units {
|
||||
let mut runtime = state.control_runtime.get_or_init(unit.id).await;
|
||||
if runtime.auto_enabled {
|
||||
skipped.push(unit.id);
|
||||
continue;
|
||||
}
|
||||
if runtime.fault_locked || runtime.comm_locked {
|
||||
skipped.push(unit.id);
|
||||
continue;
|
||||
}
|
||||
runtime.auto_enabled = true;
|
||||
runtime.state = crate::control::runtime::UnitRuntimeState::Stopped;
|
||||
runtime.current_stop_elapsed_sec = 0;
|
||||
state.control_runtime.upsert(runtime).await;
|
||||
let _ = state
|
||||
.event_manager
|
||||
.send(crate::event::AppEvent::AutoControlStarted { unit_id: unit.id });
|
||||
started.push(unit.id);
|
||||
}
|
||||
|
||||
Ok(Json(json!({ "started": started, "skipped": skipped })))
|
||||
}
|
||||
|
||||
pub async fn batch_stop_auto(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiErr> {
|
||||
let units = crate::service::get_all_enabled_units(&state.pool).await?;
|
||||
let mut stopped = Vec::new();
|
||||
|
||||
for unit in units {
|
||||
let mut runtime = state.control_runtime.get_or_init(unit.id).await;
|
||||
if !runtime.auto_enabled {
|
||||
continue;
|
||||
}
|
||||
runtime.auto_enabled = false;
|
||||
state.control_runtime.upsert(runtime).await;
|
||||
let _ = state
|
||||
.event_manager
|
||||
.send(crate::event::AppEvent::AutoControlStopped { unit_id: unit.id });
|
||||
stopped.push(unit.id);
|
||||
}
|
||||
|
||||
Ok(Json(json!({ "stopped": stopped })))
|
||||
}
|
||||
|
||||
pub async fn ack_fault_unit(
|
||||
State(state): State<AppState>,
|
||||
Path(unit_id): Path<Uuid>,
|
||||
|
|
|
|||
|
|
@ -235,6 +235,14 @@ fn build_router(state: AppState) -> Router {
|
|||
"/api/control/unit/{unit_id}/stop-auto",
|
||||
post(handler::control::stop_auto_unit),
|
||||
)
|
||||
.route(
|
||||
"/api/control/unit/batch-start-auto",
|
||||
post(handler::control::batch_start_auto),
|
||||
)
|
||||
.route(
|
||||
"/api/control/unit/batch-stop-auto",
|
||||
post(handler::control::batch_stop_auto),
|
||||
)
|
||||
.route(
|
||||
"/api/control/unit/{unit_id}/ack-fault",
|
||||
post(handler::control::ack_fault_unit),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
<aside class="ops-unit-sidebar">
|
||||
<div class="panel-head">
|
||||
<h2>控制单元</h2>
|
||||
<div class="ops-batch-actions">
|
||||
<button type="button" class="secondary" id="batchStartAutoBtn" title="启动所有未锁定单元的自动控制">全部启动</button>
|
||||
<button type="button" class="danger" id="batchStopAutoBtn" title="停止所有单元的自动控制">全部停止</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list ops-unit-list" id="opsUnitList"></div>
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>PLC Control</title>
|
||||
<link rel="stylesheet" href="/ui/styles.css?v=20260325c" />
|
||||
<link rel="stylesheet" href="/ui/styles.css?v=20260325d" />
|
||||
</head>
|
||||
<body>
|
||||
<div data-partial="/ui/html/topbar.html"></div>
|
||||
|
|
@ -22,6 +22,6 @@
|
|||
<div data-partial="/ui/html/modals.html"></div>
|
||||
<div data-partial="/ui/html/api-doc-drawer.html"></div>
|
||||
|
||||
<script type="module" src="/ui/js/index.js?v=20260325c"></script>
|
||||
<script type="module" src="/ui/js/index.js?v=20260325d"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ export const dom = {
|
|||
statusText: byId("statusText"),
|
||||
wsDot: byId("wsDot"),
|
||||
wsLabel: byId("wsLabel"),
|
||||
batchStartAutoBtn: byId("batchStartAutoBtn"),
|
||||
batchStopAutoBtn: byId("batchStopAutoBtn"),
|
||||
tabOps: byId("tabOps"),
|
||||
tabConfig: byId("tabConfig"),
|
||||
opsUnitList: byId("opsUnitList"),
|
||||
|
|
|
|||
|
|
@ -190,6 +190,18 @@ function renderOpsEquipments(equipments) {
|
|||
export function startOps() {
|
||||
renderOpsUnits();
|
||||
loadAllEquipmentCards();
|
||||
|
||||
dom.batchStartAutoBtn?.addEventListener("click", () => {
|
||||
apiFetch("/api/control/unit/batch-start-auto", { method: "POST" })
|
||||
.then(() => loadUnits())
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
dom.batchStopAutoBtn?.addEventListener("click", () => {
|
||||
apiFetch("/api/control/unit/batch-stop-auto", { method: "POST" })
|
||||
.then(() => loadUnits())
|
||||
.catch(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
/** Called by WS handler when a unit's runtime changes — syncs manual button disabled state. */
|
||||
|
|
|
|||
|
|
@ -323,6 +323,16 @@ body {
|
|||
padding: 7px 12px;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.ops-batch-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.ops-batch-actions button {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue