🚧 Add feature requests
This commit is contained in:
+229
-20
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user