Bring back crates/app_feeder_distributor/src/control/simulate.rs which
was dropped when feeder's local connection/telemetry/websocket modules
moved into core; the restored module reuses core's equivalents via
plc_platform_core::{connection, service, telemetry, websocket}.
Also restore the call sites that were lost alongside it:
- app.rs starts the chaos task when SIMULATE_PLC=true
- engine.rs fires simulate_run_feedback after each pulse command so
the auto-control state machine sees the RUN bit transition it
would get from a real PLC
- handler/control.rs does the same after manual start/stop commands
The SIMULATE_PLC flag is now read via simulate::enabled() from the
environment rather than state.config.simulate_plc (the old config
struct was removed with the module migration). To expose equipment
ids by kind (used for run feedback), build_equipment_maps now also
returns HashMap<kind, Uuid>.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add MetadataCache to PlatformContext — a lazy-loaded, cross-app cache
of code fields used when formatting event messages. Each persisted
AppEvent previously did 1-2 extra SELECTs to look up the code for its
human-readable message; after this change the same id hits the cache
on all subsequent events.
Invalidation: the platform-owned equipment handler invalidates its
entry on update/delete; feeder's unit handler does the same for
units. Deletes are invalidated for hygiene only — no further events
should target a deleted id.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move TelemetryProcessor (PointNewValue batching/dedup/broadcast) from feeder to
plc_platform_core. PlatformBuilder.build() now auto-wires telemetry processing and
reconnect task. PlatformContext.emit_event() handles connection management side effects
(connect/reconnect/disconnect/subscribe/unsubscribe) directly. Simplify PlatformEventSink
trait from 6 methods to single on_event(). Feeder's AppEvent now only contains business
events; FeederPlatformEventSink only handles UnitsChanged for control runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move source/point/equipment/tag/page handlers from feeder to plc_platform_core
using State<PlatformContext>; feeder re-exports via handler modules
- Keep batch_set_point_value in feeder (requires app-specific write key auth)
- Add PlatformEvent enum and persist_and_broadcast() in core for platform event
persistence to DB + WebSocket broadcast
- Add PlatformContext::emit_event() that handles both sink notification and
async persistence in one call
- Add platform_routes<S>() in core for centralized route registration;
both feeder and ops merge it instead of duplicating route definitions
- Implement FromRef<AppState> for PlatformContext in both apps
- Add FeederPlatformEventSink adapter bridging core events to feeder's
EventManager + ControlRuntimeStore
- Add event namespace prefixes: platform.source.created, feeder.unit.fault_locked, etc.
- Register full platform CRUD routes in ops app
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PlatformContext now holds pool, connection_manager, ws_manager
- bootstrap_platform returns PlatformBuilder for pre-Arc setup
- Feeder AppState embeds PlatformContext (state.platform.pool etc.)
- Ops AppState embeds PlatformContext with real DB connection
- Remove WebSocket type duplication: feeder re-exports from core
- Add subscribe_room/send_to_room/remove_room_if_empty to WebSocketManager
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>