🚧 Implement continous simulation

This commit is contained in:
Jonas Bengtsson
2026-10-04 14:50:57 +02:00
parent eb36a2d947
commit 263afc9c1e
13 changed files with 1914 additions and 42 deletions
+22 -20
View File
@@ -7,7 +7,7 @@
| Project | `wago-modbustcp-simulator-py` |
| Version | 1.4 (Draft for review) |
| Date | 2026-10-03 |
| Status | Framework phase; module addressing implemented (FR-4.3.11); service/container deployment implemented (§12); plain-text request tracing implemented (NFR-6.4); clean CLI error handling implemented (NFR-6.5 CLI part); web UI §10 in implementation (M1–M5a done: auth, overview, manipulation, diagnostics, configuration, channel names, import/export, split overview; M6 scenario view open); long-duration simulation (§8), bench mode (§9), runtime device configuration (§11 — coupler/module/channel-name subset now covered by web UI M5/M5a), multi-vendor device profiles (§13) and additional fieldbus protocols PROFINET/COMLI (§14) requested |
| Status | Framework phase; module addressing implemented (FR-4.3.11 v1.5 per-type numbering); service/container deployment implemented (§12); plain-text request tracing implemented (NFR-6.4); clean CLI error handling implemented (NFR-6.5 CLI part); web UI §10 in implementation (M1–M5a done: auth, overview, manipulation, diagnostics, configuration, channel names, import/export, split overview; M6 scenario view open); long-duration simulation (§8) implemented 2026-10-04 incl. PlantEnv (FR-8.2.14–16), datasets, degradation, invariants, snapshots; bench mode (§9), runtime device configuration (§11 — coupler/module/channel-name subset covered by web UI M5/M5a), multi-vendor device profiles (§13) and additional fieldbus protocols PROFINET/COMLI (§14) requested |
| Target device simulated | WAGO 750-362 / 750-362/0000-0001 FC Modbus TCP (G4) bus coupler |
| Reference documents | `docs/m07500362_xxxxxxxx_0en.pdf` (750-362 Modbus TCP manual), Modbus TCP specification (modbus.org) |
@@ -478,8 +478,8 @@ stateDiagram-v2
## 8. Feature Request — Long-Duration (Continuous) Simulation
> **Status: requested 2026-10-03, planned (roadmap phase 3), not yet implemented.**
> Extends the scenario engine (§4) with a `continuous` mode for endurance
> **Status: implemented 2026-10-04** (roadmap phase 3 complete). Extends
> the scenario engine (§4) with a `continuous` mode for endurance
> (“soak”) testing: the facility runs for hours, days or weeks of simulated
> time against evolving inputs and an aging plant.
@@ -626,8 +626,11 @@ unchanged whether or not CSV streaming is used.
load-hours (integral of the analog value). Counters are deterministic state.
- FR-8.3.2: A **degradation model** computes a named plant parameter (e.g.
`pump_capacity`, range 0…1) from wear counters and sim-time. Built-in
shapes: `linear` (per runtime hour, per start cycle), `exponential`,
`staircase` (threshold table); custom shapes via Python plugin.
shapes: `linear` (per runtime hour via `rate:`, or per start cycle with
`per: cycles`), `exponential`, `staircase` (`staircase:` level table).
A model is configured with `observes:` naming the alias whose wear
counts feed it (NB: the key is `observes` — a YAML `on` key would be
parsed as boolean True by YAML 1.1).
- FR-8.3.3: Plant-model plugins read degraded parameters via
`engine.parameter("pump_capacity")` and shall use them in the loop physics
(e.g., actual pump flow = nominal flow × capacity), so the PLC must
@@ -702,31 +705,30 @@ datasets:
repeat: 4
degradation:
pump_wear:
pump_capacity:
observes: pump_relay # wear counts from this alias
model: linear
parameter: pump_capacity # consumed by the plant model
per_runtime_hour: 0.0004 # 0.04 %/h → ~10 % after ~10.4 sim-days
per_start_cycle: 0.0002 # 0.02 % per start
floor: 0.80
maintenance_at: 0.90
on_threshold: # fires when capacity crosses 90 %
- inject_diagnosis: { module: 4, channel: 1, bits: [user_overrange] }
- assert: { label: "maintenance alarm raised",
conditions: { maintenance_alarm: ON }, within: 60s }
on_reset: # after maintenance_reset
- clear_diagnosis: { module: 4 }
rate: 0.0004 # -0.04 %/h runtime → ~10 % after ~10.4 sim-days
from: 1.0
maintenance_at: 0.90 # threshold crossing injects diagnosis
diagnosis: { module: 4, channel: 1, value: 0x0001 }
# the PLC raises maintenance_alarm; the scenario asserts it:
sequence: # scheduled events (sim-time)
- at: 12d
maintenance_reset: pump_wear # service visit: capacity restored
maintenance_reset: true # service visit: capacity restored
- at: 12d
assert: { label: "maintenance alarm raised",
conditions: { maintenance_alarm: ON }, within: 60s }
plugins: [plugins.pump_plant.pump_plant] # loop: d(level)/dt = inflow − pump_flow(speed, capacity)
invariants:
- { alias: level_sensor, min: 200, max: 9000, label: "tank level bounds" }
- { alias: level_sensor, condition: '>= 200', label: "tank level floor" }
- { alias: level_sensor, condition: '<= 9000', label: "tank level brim" }
rate_limits:
- { alias: pump_relay, edge: rising, max_per_hour: 6, label: "pump start cycles" }
- { alias: speed_setpoint, max_per_hour: 3000 } # ramp-rate bound
report:
snapshot_every: 1h