Commit Graph

84 Commits

Author SHA1 Message Date
caoqianming dd0e782450 fix(engine): push WS immediately on notify wake-up
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>
2026-03-26 08:44:46 +08:00
caoqianming 8c1b7b636d refactor(engine): replace 500ms ticker with per-unit event-driven tasks
- 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>
2026-03-26 08:33:00 +08:00
caoqianming a8d36578fa feat: ws backoff, signal dots, dom cap, unwrap fix, batch size limit
- logs.js: WS reconnect exponential backoff 1s→2s→4s…30s
- ops.js: replace badge+text signal display with red/green/yellow dots
  (sig-on=green, sig-fault=red, sig-warn=yellow, gray=off)
- events.js: cap live-prepended event cards at 100 DOM nodes
- source.rs: fix attach_children unwrap() → Option<TreeNode>/filter_map
- point.rs: add max=500 validation to all batch Vec<Uuid> fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 16:37:14 +08:00
caoqianming 757d6f9a3a 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>
2026-03-25 13:05:23 +08:00
caoqianming 0077a4ad90 fix(engine): stop coal_feeder before starting distributor on acc_time trigger
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>
2026-03-25 13:00:24 +08:00
caoqianming 3a8a2c1389 fix(sim): try OPC UA write first in simulate_run_feedback, fallback to cache patch
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>
2026-03-25 12:50:26 +08:00
caoqianming b832d98196 fix(control): block manual commands during auto, fix engine stop_time=0 bug, add sim feedback
- 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>
2026-03-25 12:37:43 +08:00
caoqianming 989a0286e9 feat(sim): simulate RUN signal feedback when SIMULATE_PLC=true
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>
2026-03-25 11:08:38 +08:00
caoqianming 4076f6575e feat(web): dual-view UI — 运维/配置 tab, ops equipment cards with live signal values
- 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>
2026-03-25 10:25:20 +08:00
caoqianming 2732238be7 feat(api): add GET /api/unit/{id}/detail with nested equipment and points
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>
2026-03-25 08:37:09 +08:00
caoqianming 622d010cb1 fix(server): add Cache-Control: no-store to static file responses
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>
2026-03-24 16:57:39 +08:00
caoqianming b5a8d6a71d fix(engine): correct fault equipment ID lookup and all_roles data structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 15:03:26 +08:00
caoqianming d2bd567799 feat(main): register control engine routes and start engine 2026-03-24 14:56:19 +08:00
caoqianming 856c888667 feat(control): add start-auto, stop-auto, ack-fault, runtime endpoints 2026-03-24 14:56:16 +08:00
caoqianming 459bb49c65 feat(control): implement state machine engine with fault/comm monitoring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 14:53:59 +08:00
caoqianming 5c0b99c0d4 feat(service): add get_all_enabled_units and get_equipment_by_unit_id 2026-03-24 14:48:30 +08:00
caoqianming 6a4c3b1d39 feat(websocket): add UnitRuntimeChanged message type 2026-03-24 14:47:35 +08:00
caoqianming 68e724898c feat(event): add business control events (fault, comm, auto, state change) 2026-03-24 14:46:25 +08:00
caoqianming 684ca9da85 feat(control): reject manual commands when unit is fault/comm locked 2026-03-24 14:44:48 +08:00
caoqianming 628553f2b8 refactor(control): extract pulse command helper to control/command.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 14:31:37 +08:00
caoqianming 9194bd1dca feat(control): add auto_enabled and flt_active to UnitRuntime 2026-03-24 14:23:53 +08:00
caoqianming f7dc39a70a fix: point bind bug 2026-03-24 13:39:45 +08:00
caoqianming a38204511a refactor(control): align point roles and equipment kind 2026-03-24 13:17:53 +08:00
caoqianming 2d80266422 fix(opcua): trigger reconnect on BadTimeout and tighten subscription params
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>
2026-03-24 12:50:55 +08:00
caoqianming 0b9b7aef7d fix(opcua): relax subscription timeout handling 2026-03-24 12:28:23 +08:00
caoqianming c50127b9d0 feat(event): stream created events over websocket 2026-03-24 12:28:12 +08:00
caoqianming 97d2f6ebf8 feat(control): add manual equipment pulse commands 2026-03-24 11:16:50 +08:00
caoqianming 4e3d325437 feat(control): add unit and event foundation 2026-03-24 10:20:23 +08:00
caoqianming fec7b60d6b feat(web): reorganize equipment layout and point flows 2026-03-23 12:49:26 +08:00
caoqianming 06ace5e67d refactor(app): split services and web modules 2026-03-23 11:31:38 +08:00
caoqianming 8be82e372e feat(point): add equipment metadata scaffolding 2026-03-23 10:38:20 +08:00
caoqianming a691f07e8e feat(web): add API.md drawer preview 2026-03-20 19:00:46 +08:00
caoqianming 920e37f759 feat(web): add inline point chart panel 2026-03-20 10:54:20 +08:00
caoqianming bf548161a6 feat: 心跳检测设置为4秒 2026-03-20 09:58:15 +08:00
caoqianming 8eb1d6671a perf(connection): reduce subscription lock contention 2026-03-17 08:28:55 +08:00
caoqianming f33d989905 perf(point): batch point creation queries 2026-03-17 08:25:04 +08:00
caoqianming 7e6c7a7e4c feat(reconnect): add retry backoff and manual reconnect 2026-03-17 08:15:54 +08:00
caoqianming f33c96a4e5 fix(opcua): default point quality to Good when status is absent
Made-with: Cursor
2026-03-16 09:57:37 +08:00
caoqianming 503aefc4cb refactor(event): rename ReloadEvent to AppEvent and split event channels
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
2026-03-13 14:44:30 +08:00
caoqianming 5fa63ad6dd fix(opcua): stabilize reconnect loop and coalesce telemetry events
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
2026-03-13 14:26:50 +08:00
caoqianming 5406568969 fix: harden event handling and source safety
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
2026-03-13 14:22:16 +08:00
caoqianming 6f215162a3 feat(page): add page table and CRUD handlers 2026-03-11 13:54:14 +08:00
caoqianming efed6aa816 feat: add page 2026-03-11 13:23:05 +08:00
caoqianming 1374abe550 feat(log): add file-based log APIs and switch file logs to JSON 2026-03-09 14:47:19 +08:00
caoqianming 63bcf679c2 fix(opcua): reconnect when subscription becomes invalid 2026-03-09 08:58:40 +08:00
caoqianming 0893c9783c fix: 修复心跳检查中订阅状态判断逻辑,避免启动时误触发重连 2026-03-06 13:51:47 +08:00
caoqianming 5dc1081c90 改进心跳检测中的订阅状态检查,通过读取服务器节点属性来验证订阅是否真正有效 2026-03-06 12:57:22 +08:00
caoqianming afab910780 在 DataChangeCallback 中添加 BadNoSubscription 错误检测 2026-03-06 10:58:27 +08:00
caoqianming d4d5749ccc 优化 OPC UA event_loop 监控和重连机制 2026-03-06 10:01:18 +08:00
caoqianming 76b6e17927 feat: save event_loop handle to avoid ghost session 2026-03-06 09:38:14 +08:00