🚧 Add feature requests

This commit is contained in:
Jonas Bengtsson
2026-10-03 12:05:38 +02:00
parent 973d619cee
commit ba8df075c3
2 changed files with 269 additions and 26 deletions
+229 -20
View File
@@ -5,9 +5,9 @@
| | |
|---|---|
| Project | `wago-modbustcp-simulator-py` |
| Version | 1.1 (Draft for review) |
| Version | 1.2 (Draft for review) |
| Date | 2026-10-03 |
| Status | Framework phase; module addressing implemented (FR-4.3.11); long-duration simulation (§8), bench mode (§9) and web UI (§10) requested |
| Status | Framework phase; module addressing implemented (FR-4.3.11); long-duration simulation (§8), bench mode (§9), web UI (§10) and runtime device configuration (§11) 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) |
@@ -471,24 +471,40 @@ additionally be proven **over long time**:
- **Clock edge cases**: midnight rollovers and (via absolute-timestamp datasets) DST transitions.
Long runs cannot be hand-scripted step by step. Two mechanisms replace the
scripted `sequence`:
scripted `sequence`, and both run **in parallel on the same sim-time axis**:
1. **Dataset playback** — designated inputs are driven from time-series files
(e.g., CSV exported from a plant historian, or synthesized profiles).
2. **Degradation models** — plant-model parameters (e.g., pump capacity)
degrade as a function of runtime and cycles, exercising the PLC’s
1. **Dataset playback** *(optional)* — designated inputs are driven from
time-series files (e.g., CSV exported from a plant historian, or
synthesized profiles).
2. **Signal logic** — plant-model plugins (FR-4.3.8) compute endogenous
signals and closed-loop physics in Python, scheduled sequence steps
(`at:`) perform discrete actions, and degradation models age plant
parameters (e.g., pump capacity) from wear, exercising the PLC’s
compensation and maintenance logic.
Datasets are **not required**: a continuous run may be driven entirely by
logic (plugin-generated demand profiles, scheduled events, degradation),
entirely by datasets, or by any mix — some aliases fed from CSV while
others are computed by logic, in the same run. The signal logic works
unchanged whether or not CSV streaming is used.
### 8.2 Requirements — continuous mode & CSV dataset playback
- FR-8.2.1: A scenario may declare `mode: continuous`. The `sequence` becomes
optional; the run continues until the dataset schedule (plus any `until:`
duration) is exhausted.
- FR-8.2.2: Input aliases (di/ai) may be driven from **datasets**. Output
aliases must not appear in datasets (validation error). An endogenous process
variable (e.g., tank level) may instead be computed by a plant-model plugin
(FR-4.3.8, closed loop). **Exactly one driver** may own an alias (dataset,
plugin-declared variable, ramp, pulse, or step) — conflicts are validation errors.
optional. The run continues until **all active drivers are exhausted**:
every dataset finished (or looped to its `repeat` limit), all scheduled
steps executed, and — if a top-level `until: <duration>` is given — the
duration elapsed. With no datasets, `until:` is the primary run-length
control. A continuous scenario with neither datasets, nor scheduled
steps, nor `until:` is a validation error (it would run forever).
- FR-8.2.2: Input aliases (di/ai) may be driven from **datasets**, which are
entirely **optional**. Output aliases must not appear in datasets
(validation error). Aliases not owned by a dataset are **logic-driven**:
computed by a plant-model plugin (FR-4.3.8, closed loop), set by
scheduled steps, or left at their current value. An endogenous process
variable (e.g., tank level) is computed by the plant model. **Exactly one
driver** may own an alias (dataset, plugin-declared variable, ramp,
pulse, or step) — conflicts are validation errors.
- FR-8.2.3: CSV format: a header row with the time column (default `time`,
configurable via `time_column`) plus one column per alias. Time is elapsed
seconds (default) or ISO-8601 absolute timestamps (`time_format: iso8601`).
@@ -516,6 +532,24 @@ scripted `sequence`:
- FR-8.2.11: Malformed data (unknown column, unparsable time or value,
non-monotonic timestamps) ends the run with verdict **ERROR**, citing file
and line number.
- FR-8.2.12: **Parallel logic execution**: datasets and signal logic run in
the same deterministic tick pipeline, in a fixed order per tick:
(1) datasets apply their sampled/interpolated values to their aliases,
(2) alias edges are detected,
(3) plant-model plugins run — they may read dataset-driven AND
logic-driven aliases and compute endogenous variables in the same tick,
(4) degradation models update,
(5) invariants and rate limits evaluate,
(6) scheduled steps due at this tick execute,
(7) snapshots are taken if due.
This order is part of the specification and shall be covered by tests.
- FR-8.2.13: **Logic-only continuous runs**: with no `datasets:` block at
all, continuous mode shall work unchanged — demand profiles and other
exogenous signals may be generated *inside* plant-model plugins (e.g., a
daily sinusoid or a state-based profile computed in Python), with
top-level `until:` bounding the run. All §8.3–8.4 machinery (degradation,
invariants, rate limits, snapshots) applies identically, whether or not
any CSV streaming is configured.
### 8.3 Requirements — degradation models (plant logic over time)
@@ -653,20 +687,50 @@ time,start_button,em_stop_ok,inflow_valve
440000,OFF,ON,ON
```
**Variant without datasets** (logic-only, same soak): drop the
`datasets:` block entirely, keep everything else, and let the plant
plugin *also* generate the demand signal each tick:
```yaml
name: pump_station_soak_28d_logic
mode: continuous
until: 28d # no datasets: until bounds the run
inputs: ... # same aliases — none dataset-owned
outputs: ...
plugins: [plugins.pump_plant.pump_plant]
# plugin computes, per tick (FR-8.2.12 step 3):
# inflow_rate = 12 + 3*sin(2*pi*t/24h)
# d(level)/dt = inflow - pump_flow(speed, capacity)
degradation: ... # identical to the dataset version
invariants: ...
rate_limits: ...
report: { snapshot_every: 1h }
```
Both variants test the same PLC behavior; the CSV version replays recorded
demand, the logic version synthesizes it. Mixing is equally valid — e.g.
`inflow_rate` from a dataset while `start_button` is toggled by scheduled
`at:` steps, with the plant plugin reading both (FR-8.2.12).
### 8.6 Architecture impact (planned modules)
```
src/wago_sim/scenario/
├── datasets.py # CsvDataset: streaming reader, interpolation, repeat, time_factor
├── datasets.py # CsvDataset: streaming reader, interpolation, repeat,
│ # time_factor — OPTIONAL driver in continuous mode
├── degradation.py # WearCounters, DegradationModel registry, maintenance actions
├── invariants.py # invariant & rate-limit evaluation, episode aggregation
└── engine.py # mode: continuous, parameter() accessor, snapshot scheduler
└── engine.py # mode: continuous, parameter() accessor, snapshot
# scheduler; fixed tick pipeline per FR-8.2.12:
# datasets -> edges -> plugins -> degradation ->
# invariants -> scheduled steps -> snapshots
```
```mermaid
erDiagram
ContinuousScenario ||--|{ Dataset : datasets
ContinuousScenario ||--o{ Dataset : "datasets (optional)"
Dataset ||--|{ ColumnMapping : "alias columns"
ContinuousScenario ||--|{ PlantPlugin : "signal logic (always runs)"
ContinuousScenario ||--|{ DegradationModel : degradation
DegradationModel ||--|{ WearCounter : counts
WearCounter }o--|| Alias : observes
@@ -675,6 +739,7 @@ erDiagram
ContinuousScenario ||--|{ RateLimit : rate_limits
ContinuousScenario ||--o{ Snapshot : "periodic dumps"
PlantPlugin }o--|| DegradationModel : "reads parameters"
PlantPlugin }o--o{ Alias : "reads dataset-driven + computes logic-driven"
```
### 8.7 Non-functional requirements (long-duration)
@@ -711,6 +776,15 @@ erDiagram
regardless of duration; a rate-limit breach fails the run.
8. A full 28-sim-day soak completes headless with a bounded report and a
correct PASS/FAIL/ERROR verdict.
9. A continuous run with **no datasets** (plugin-generated demand,
top-level `until:`) completes with degradation, invariants, rate limits
and snapshots all active, and a correct verdict (FR-8.2.13).
10. A **mixed run** — one alias driven by a dataset, another computed by a
plugin in the same run — is supported; the plugin observes the dataset
value applied earlier in the same tick (FR-8.2.12 order), verified by a
tick-order test.
11. A continuous scenario with no datasets, no scheduled steps, and no
`until:` is rejected at validation time (FR-8.2.1).
---
@@ -824,6 +898,10 @@ Terminal monitors (§9) suffice for single engineers, but FAT benches are
- FR-10.2.9: The UI must not disturb deterministic scenario runs: when a
scenario owns an alias, UI writes to it are rejected with a clear
message (alias-ownership rules of §8 FR-8.2.2 apply).
- FR-10.2.10: The UI's **configuration** pages (Ethernet/port settings,
Modbus watchdog settings, I/O Config with SAVE CONFIG, Modbus Mapping
overview, address-selection DIP switch) are specified in §11; §10 covers
value/state oversight and manipulation.
### 10.3 Architecture sketch (planned)
@@ -856,19 +934,150 @@ server, so a UI write and a Modbus read never race on the datastore.
---
## 11. Roadmap (post-framework)
## 11. Feature Request — Runtime Device Configuration (WBM/DIP-Switch Parity)
> **Status: requested 2026-10-03, planned (roadmap phase 5), not yet
> implemented.** Mirrors the real 750-362's configuration mechanisms so
> the FAT bench can commission the node the way a real one is
> commissioned.
### 11.1 Motivation
On the real coupler the *node* is configured through two mechanisms
(manual §4.4.2, §8.3, §9.11–9.18):
1. **Web-Based Management (WBM)** pages: Ethernet/port settings (IP
address, subnet, gateway, DHCP/BootP/EEPROM source, base IP), Modbus
settings (connection idle timeout, Modbus watchdog type/timeout/trigger
masks, keepalive), Modbus Mapping (per-module register/coil address map,
read-only), and I/O Config (connected module list + **SAVE CONFIG**,
which re-reads the terminal bus).
2. **Address-selection DIP switch** (8-pole): binary host ID 1–254 = last
IP octet (base network 192.168.1.0/24 in delivery state); 0 = use the
stored WBM port configuration; 255 = DHCP. Changing it requires a
restart to take effect.
The I/O modules themselves are **not** configured via WBM or DIP switch —
they are physically plugged/unplugged, and the coupler re-reads the
terminal bus (SAVE CONFIG / KBUS reset, register `0x102B`), rebuilding
the process image. The simulator equivalent of "plugging" a module is a
**runtime module-list change with image rebuild**.
Today the simulator freezes all of this at startup: module list from
YAML, watchdog only via Modbus registers, no DIP switch, no WBM pages.
This feature request closes that gap.
### 11.2 Requirements — runtime module configuration ("plug/unplug")
- FR-11.2.1: The node's module list shall be **mutable at runtime** via
the web UI (§10), the console monitor (§9), and a programmatic API:
insert/remove modules at slot positions, replicating physical
plug/unplug on the local bus.
- FR-11.2.2: After every change the process image is **rebuilt exactly
like the real device after a KBUS reset** (`0x102B` or WBM SAVE
CONFIG): word modules first in physical order, then bit modules
byte-packed (§7.3.2.1); image sizes updated in `0x1022–0x1025`;
module descriptions updated in `0x2030+` so a polling master sees the
new configuration.
- FR-11.2.3: Scenario aliases are **revalidated** after a rebuild.
Module-based addresses (FR-4.3.11) are position-stable — `DI2.1` stays
on module 2 when earlier slots change — while pool-based indexes may
shift; aliases orphaned by a removal are reported as configuration
errors. This difference shall be documented and covered by tests.
- FR-11.2.4: While a scenario is running, module reconfiguration is
rejected with a clear message (consistent with FR-10.2.9) — deterministic
runs must not be disturbed. In bench mode (§9) it is always allowed.
- FR-11.2.5: A reconfiguration event is logged (sim-time, change
description, new image sizes) and visible in the web UI's I/O Config
page, mirroring the WBM SAVE CONFIG flow.
### 11.3 Requirements — address-selection DIP switch emulation
- FR-11.3.1: An 8-pole address-selection switch shall be emulated per node
with the manual's exact coding (§4.4.2/§8.3.1): DIP 1 = LSB … DIP 8 =
MSB; **1–254** = host ID appended to the base network (default
192.168.1.0/24, changeable); **0** = use the stored port configuration
(WBM port settings); **255** = DHCP (virtual: an address from the
configured DHCP pool is reported).
- FR-11.3.2: The switch shall be visual and operable in the web UI
(toggle poles) and settable in the console/config; the **derived virtual
IP address** is displayed and introspectable via the API.
- FR-11.3.3: **Apply semantics match the device**: a switch change marks
the node "restart required"; on apply (explicit restart command or
`serve` restart) the simulator rebinds its listen address to the
derived IP when running in dedicated-IP mode, or updates the virtual
IP model in shared-IP mode. Outputs and connections behave like a
device restart.
- FR-11.3.4: The stored port configuration (base network, static IP for
switch position 0, DHCP pool) is part of the device state (FR-11.5.1).
### 11.4 Requirements — WBM-equivalent configuration pages (web UI)
- FR-11.4.1: **Port/Ethernet page**: virtual IP settings — configuration
source (static/EEPROM, DHCP, BootP), IP/subnet/gateway, base network for
DIP addressing — persisted as device state and reported alongside the
DIP-derived address.
- FR-11.4.2: **Modbus settings page**: connection idle timeout
(`0x1030`), Modbus watchdog fields (type Standard/Alternative, timeout,
trigger masks FC1–16/FC17–32, close-socket-on-timeout) — the same
fields the real WBM exposes (manual §9.16), mapped 1:1 onto registers
`0x1000–0x100B`. Writes obey the register rules (e.g., timeout not
changeable while the watchdog is active).
- FR-11.4.3: **Modbus Mapping page**: per-module address map generated
from the node layout — word addresses (input RO, output WO, read-back
RW) and bit addresses (input, output, read-back) in the WBM's
`<SR>[.<SO>]`–`<ER>[.<EO>]` notation (manual §9.17), plus the general
area overview of §3.3. Read-only; auto-updates after FR-11.2.2 rebuilds.
- FR-11.4.4: **I/O Config page**: module list (position, article, type,
channel count) + **SAVE CONFIG** button = re-read/rebuild per
FR-11.2.2, mirroring WBM §9.18.
### 11.5 Requirements — persistence & reset
- FR-11.5.1: Device state (module list changes, DIP switch position,
port configuration, watchdog settings written as remanent per `0x100B`)
persists to a device-state file and is reloaded on start — the
simulator's equivalent of the device's EEPROM.
- FR-11.5.2: **Factory reset** (register `0x2043` write, CLI flag, web UI
button): restores delivery state — module list from the node YAML, DIP
switch to 0, watchdog defaults, cleared statistics — and persists that
as the new device state.
### 11.6 Acceptance criteria
1. Removing a module and issuing SAVE CONFIG (or `0x102B`) rebuilds the
image: `0x1022–0x1025` and `0x2030+` reflect the change and a pymodbus
master reading process data sees the new layout.
2. Inserting a module before existing ones leaves module-addressed
aliases (`DI2.1`) pointing at the same module, while pool-indexed
aliases shift by the inserted channel count — asserted by tests.
3. Aliases orphaned by a module removal are reported as errors, not
silently misaddressed.
4. Reconfiguration during a running scenario is rejected; in bench mode
it succeeds and is logged.
5. DIP switch 00001001 (33) derives virtual IP 192.168.1.33; switch 0
shows the stored port configuration; 255 reports a DHCP-pool address;
changing the switch shows "restart required" until applied.
6. Watchdog fields edited on the web settings page behave identically to
register writes (same validation, same statistics).
7. Device state survives a simulator restart; factory reset restores
delivery defaults.
---
## 12. Roadmap (post-framework)
| Phase | Feature |
|---|---|
| 2 | Module catalog expansion (analog, counter, SSI, DALI/DSI, serial modules), mailbox parameters; **bench mode tooling (§9)**: `--set` seeding, console monitor, runtime manipulation commands |
| 3 | **Long-duration simulation (§8)**: CSV dataset playback, degradation models, invariants, soak-test reporting |
| 4 | FAT report generator (HTML/PDF), scenario comparison, template library per project type |
| 5 | Multi-node orchestration (several couplers in one FAT bench), UDP variant, WBM simulation; **web UI (§10)** |
| 5 | Multi-node orchestration (several couplers in one FAT bench), UDP variant, WBM simulation; **web UI (§10)**; **runtime device configuration (§11)**: module hot-configuration with image rebuild, DIP-switch emulation, WBM-equivalent pages, device-state persistence |
| 6 | CI integration: run scenarios headless, export JUnit XML |
---
## 12. References
## 13. References
- WAGO 750-362 manual `m07500362_xxxxxxxx_0en.pdf`, sections 7.2–7.3 (process image), 11.2 (Modbus), 12.2 (module data architecture)
- WAGO I/O System 750 system description `m075xxxxx_xxxxxxxx_0en.pdf`