When auto_enabled or fault_locked changes externally, the engine task
wakes via notify but previously only pushed WS on the next state
transition (potentially seconds later). Now push the fresh runtime
immediately in the notify.notified() arm so the frontend reflects
the change without delay.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Engine now spawns one async task per enabled unit (supervised every 10s)
- wait_phase uses sleep_until + select! for precise timing; 500ms fault-tick
runs inside each phase so fault/comm is still checked promptly
- WS UnitRuntimeChanged pushed only on state transitions, not every tick
- ControlRuntimeStore gains notify_unit/get_or_create_notify for instant
wake-up when handlers change auto_enabled or fault_locked
- UnitRuntime: remove last_tick_at, current_run/stop/distributor_elapsed_sec;
add display_acc_sec (snapshot at transition, avoids mid-cycle jitter)
- accumulated_run_sec now increments by exact run_time_sec*1000 per cycle
- unit.state_changed events no longer written to DB (too frequent)
- Frontend: show display_acc_sec instead of accumulated_run_sec
- styles: event-card flex-shrink:0 fixes text overlap under flex column
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
When accumulated_run_sec reaches acc_time_sec, the coal feeder must be stopped
before entering DistributorRunning state. Previously the feeder was left running
while the distributor also ran, which is incorrect per the control spec.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of always patching the local cache, first attempt to write the RUN
point value through the normal OPC UA path. If the proxy accepts the write,
write_point_values_batch already emits PointNewValue locally so no extra work
is needed. Only fall back to direct cache patching when the write is rejected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- validator: reject equipment start/stop when unit auto_enabled
- engine: fix stop_time_sec==0 causing infinite Stopped state (never starts)
- engine: call simulate_run_feedback after auto commands when SIMULATE_PLC=true
- command: extract simulate_run_feedback to shared module (was private in handler)
- web: disable Start/Stop buttons when unit auto is active; sync on WS runtime update
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a successful start/stop command, write run=true/false directly
into the point monitor cache and broadcast PointNewValue via WebSocket.
Gated by SIMULATE_PLC=true env var; real OPC-UA values override it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 运维/配置 tab switch; grid-ops / grid-config layout classes
- New ops-panel: unit sidebar + equipment card grid (REM/RUN/FLT signals)
- All equipment cards shown by default; unit click acts as filter
- Signal cells seed from point_monitor cache on render, then update via WS PointNewValue
- New log-stream-panel: SSE realtime log stream, active only in config view
- Backend: get_unit_detail now includes point_monitor (current value) in each point
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Returns unit with its equipments, each embedding their bound points.
Uses 2 queries (equipment list + points via ANY) to avoid N+1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents browser from caching JS/CSS modules, so frontend changes take
effect immediately on page refresh without needing hard refresh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the special-case that silently ignored BadTimeout in the
subscription status callback. BadTimeout means the server has already
dropped the subscription, so reconnect must be triggered immediately
rather than waiting for the heartbeat check.
Also reduce lifetime_count (120→15) and max_keep_alive_count (10→5)
so failures are detected within 15s instead of 120s, while still
satisfying the OPC UA spec requirement of lifetime >= 3×keepalive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clarify event semantics by renaming ReloadEvent to AppEvent and route control vs telemetry traffic through dedicated channels. This keeps control events isolated from high-frequency PointNewValue updates while preserving the existing send() call pattern.
Made-with: Cursor
Always clear reconnect-in-progress markers after reconnect attempts so heartbeat-triggered retries are not blocked. Reduce high-frequency event overhead by coalescing consecutive point updates in the event worker and processing only the latest value per source/client handle.
Made-with: Cursor
Improve runtime resilience by bounding the reload event queue and processing telemetry updates without per-point spawned tasks. Also reduce security risk by sanitizing source responses, avoiding internal error detail leaks, and standardizing write-key configuration with backward compatibility.
Made-with: Cursor