Skip to content

arcnode-io/ems-twin-service

Repository files navigation

ems-twin-service 🪞⚡

pipeline coverage python ty fastapi

Physics-grounded digital twin of an ARCNODE deployment — coherent sim-mode telemetry, dispatch answering, shadow-mode mirroring, and what-if economics from one plant model.

About

The EMS stack has a first-class sim mode (ADR-001 §5) but nothing that fills it with coherent plant behavior: protocol fixtures emit independent sawtooth values with no causal link between dispatch, state of charge, bus power flow, and metering. This service is that missing plant.

It consumes the Device Topology Manifest (from ems-device-api GET /topology or a local file), assembles a causally-coupled site model, and publishes contract-conformant telemetry to MQTT per ADR-002 — every measurement of every device in the DTM, at its template poll rate, strict {ts, value} payloads, QoS 0 retained.

Physics (BESS-centric, Lebowitz/Kirschen-grounded):

  • split-efficiency SoC integration (η_ch = η_dis = √η_rt, default η_rt = 0.88 at POI)
  • power rating + energy-headroom clamps, ramp limiting, usable SoC window
  • full-equivalent-cycle throughput, two-term SOH fade (calendar + cycling)
  • first-order thermal, threshold-driven warn/alarm evaluation from template bands
  • module ↔ rack fanout and energy-weighted rollups
  • reactive dispatch on the four-quadrant PQ circle (√(P²+Q²) ≤ S)
  • site coupling: diurnal compute load → grid import = load − BESS discharge → meter integration

Vocabulary-driven, not slug-driven: any template declaring both state_of_charge and active_power is treated as a battery. Everything else gets deterministic bounded profiles around template bounds.nominal — no dead channels, on any DTM.

Dispatch: subscribes commands/set/active_power/watts, answers the locked received|done|failed lifecycle on events/dispatch_state (same contract as ems-industrial-gateway dispatch.rs), and actually ramps the plant.

Twin, not just simulator: twin_mode: shadow subscribes to live measurements and mirrors them into plant state instead of publishing — derived quantities (headroom, SOH, alarms, what-if) then run against the mirrored plant.

Full-path fidelity: twin_mode: fixture drives the ems-industrial-fixtures mock servers' registers over their HTTP control API (engineering → raw via the exact inverse of the gateway's ADR-002 §4 scaling), so the real Rust gateway polls real Modbus and publishes through its real conversion path. In this mode the twin follows dispatch commands silently — the gateway owns the ack lifecycle. Run the twin or the gateway as MQTT publisher, never both (see docs/adr-001-twin-charter.md).

What-if replay: POST /whatif/replay runs a dispatch schedule × price series through the same step_bess the live loop uses — SoC trajectory, revenue/cost/profit, constraint clamps. Arbitrage math per Kirschen: profit requires the price spread to beat 1/η_rt.

API

Route Purpose
GET / healthcheck
GET /twin/state full twin snapshot: mode, site coupling, units, active alarms
POST /whatif/replay schedule × prices → trajectory + economics

Run

uv sync
uv run poe dev          # api on :8010, twin loop against cfg.yml mqtt
uv run poe checks       # deptry, black, ruff, ty, bandit, pip-audit
uv run poe unit         # colocated unit tests
uv run poe integration  # testcontainers: HiveMQ CE round-trip e2e

Config in cfg.yml (ENV=local|beta). Secrets via env only — template-secrets.env lists MQTT_USERNAME / MQTT_PASSWORD.

Source Layout

src/
├── shared/          # ADR-002 topics + consumer mirrors of edp-api schemas
│   └── schemas/     # Dtm, DeviceTemplate, samples (tolerant-reader subset)
├── plant/           # pure physics: bess step, profiles, alarms, site assembly
├── telemetry/       # poll-rate scheduler + wire frames
├── fixture/         # engineering->raw encoding + control-API pusher
├── dispatch/        # commands/ consumer, lifecycle acks (or silent follow)
├── twin/            # runtime: MQTT session, tick loop, snapshot API
└── whatif/          # schedule replay + economics
tests/               # HiveMQ CE testcontainer e2e + API e2e + DTM fixtures

See Also

  • ems/system_adr.md (ADR-001) and ems/topic_structure_adr.md (ADR-002)
  • docs/adr-001-twin-charter.md — scope, seams, deployment composition
  • docs/external-references.md — cross-repo contracts this service consumes

About

Mirror of gitlab.com/arcnode-io/digital-twin

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors