🎉 Initial commit

This commit is contained in:
Jonas Bengtsson
2026-10-03 09:46:11 +02:00
commit 973d619cee
34 changed files with 5787 additions and 0 deletions
+243
View File
@@ -0,0 +1,243 @@
# wago-modbustcp-simulator-py
**WAGO 750-362 Modbus TCP bus coupler simulator for autonomous PLC FAT testing.**
The simulator replaces a physical WAGO I/O node during Factory
Acceptance Tests: the PLC under test connects via Modbus TCP and talks to
a virtual node whose I/O behavior is driven by scripted scenarios.
## Features
- Faithful WAGO 750-362 register map (manual §11.2.4): process image
words/bits with the exact mapped/unmapped areas and exception behavior,
read-back mirrors (+`0x0200` / +`0x1000`), configuration registers
`0x1000–0x2FFF`, constants `0x2000–0x2008` (byte-order check `0x1234`).
- Function codes FC1, FC2, FC3, FC4, FC5, FC6, FC11, FC15, FC16, FC22,
FC23 with the device limits (2000/800 bits, 125/100 registers).
- Modbus watchdog (`0x1000–0x100B`), statistics block (`0x1029`),
response-delay injection (`0x1037`), module diagnosis (`0x1050`).
- Virtual node built from a YAML module list (WAGO 750 catalog subset,
extensible).
- **Scenario engine**: YAML scenarios with aliases, macros, ramps,
pulses, waits, and PASS/FAIL assertions with timeouts; state machines
and Python action plugins for complex plant models.
- **Two I/O addressing schemes** for aliases: *module-based* —
`{ type: di, module: 2, channel: 1 }` = `DI2.1` (module slot and
channel both 0-based) — and *pool-based* — `{ type: di, channel: 3 }`
(1-based index across the node's DI pool). Both may be mixed.
- **Bench mode** *(requested, planned — spec §9)*: run the simulator
without a scenario (`wago-sim serve --node ...` already works);
planned additions: `--set DI0.1=ON` startup seeding, an interactive
console monitor with live I/O table, and runtime set/pulse/watch
commands.
- **Web UI** *(requested, planned — spec §10)*: browser front-end to
overlook and manipulate the virtual node's I/O — live channel tables
with `DI0.1`-style addresses, toggle/force/pulse/ramp actions,
watchdog/statistics/diagnostics views with fault injection, and
multi-client consistency for collaborative FAT benches.
- **Long-duration (continuous) simulation** *(requested, planned — see
`docs/spec/software-spec.md` §8)*: endurance/soak testing over days or
weeks of simulated time. Designated inputs are driven from streaming
CSV datasets (historian exports, demand/operator profiles, seamless
`repeat`, `time_factor` acceleration), while degradation models age
plant parameters (e.g., pump capacity) from wear counters — exercising
the PLC's compensation, maintenance-alarm and service-recovery logic.
Continuous `invariants` and `rate_limits` produce bounded, episode-based
reports for unattended runs.
- ≤ 15 concurrent Modbus TCP connections, idle-timeout handling.
## Install (dev)
```bash
pip install -e .[dev]
```
## Usage
Run the Modbus TCP server against a node configuration:
```bash
wago-sim serve --node config/pump_node.yaml --port 502
```
Validate a scenario:
```bash
wago-sim validate --scenario scenarios/pump_station_fat.yaml
```
Run a scenario headless and write reports:
```bash
wago-sim run --node config/pump_node.yaml \
--scenario scenarios/pump_station_fat.yaml \
--report-jsonl out/report.jsonl --report-csv out/report.csv
```
## Documentation
- `docs/spec/software-spec.md` — the software specification
(requirements, architecture, scenario DSL, acceptance criteria,
and the long-duration simulation feature request in §8).
- `docs/m07500362_xxxxxxxx_0en.pdf` — WAGO 750-362 reference manual.
## Testing
```bash
pytest
```
## TODO
Work remaining, tracked against the software specification
(`docs/spec/software-spec.md`). Completed items are checked and grouped
first; open items follow, grouped by spec phase, with the spec section or
requirement ID for each item in parentheses.
### Completed — framework phase (spec §3 + §4 core, 124 tests passing)
- [x] MBAP + PDU codecs: function codes FC1, FC2, FC3, FC4, FC5, FC6,
FC11, FC15, FC16, FC22, FC23 with the 750-362 device limits
(2000/800 bits, 125/100 registers) and exception codes (§3.2).
- [x] WAGO register map (§3.3): word/bit process-image areas with the
exact mapped/unmapped ranges, illegal-address exceptions, output
read-back mirrors (+`0x0200` / +`0x1000`), digital
channel-number bit addressing, constants `0x2000–0x2008`
(incl. byte-order check `0x2002` = `0x1234`).
- [x] Configuration/diagnostic/firmware registers (§3.5): image sizes
`0x1022–0x1025`, statistics `0x1029`, connections `0x102A`, idle
timeout `0x1030`, MAC-ID `0x1031`, response delay `0x1037`,
module diagnosis `0x1050`, LED error `0x1020/0x1021`.
- [x] Modbus watchdog (§3.4): registers `0x1000–0x100B`, Standard and
Alternative modes, trigger/masks/stop sequences, expiry clears
outputs, address-aware `0x04` lock in Standard mode, optional
TCP close on timeout.
- [x] Fault-injection paths (§3.6): response-delay injection,
connection refusal beyond 15, illegal-address probing responses,
diagnosis/LED registers scriptable.
- [x] Virtual node built from a YAML module list (13-module WAGO 750
catalog subset; complex-first, bit-byte-packed image layout per
manual §7.3.2.1).
- [x] Scenario DSL (§4): named aliases with `scale`/`offset`, macros
with parameters (inline expansion, blocking waits inside macros),
sequence steps `set` / `reset` / `toggle` / `pulse` / `ramp` /
`wait` / `assert` / `report`, rising-edge detection on all
aliases.
- [x] Two I/O addressing schemes for scenario aliases (FR-4.3.11):
module-based (`{ type: di, module: 2, channel: 1 }` = `DI2.1`,
module slot and channel 0-based) and pool-based (`channel:` =
1-based index within the node's kind pool); kind/module/channel
validation with clear errors; both verified by tests.
- [x] Assertions with `within:` timeouts → PASS/FAIL verdicts with
sim-time stamps (FR-4.3.5).
- [x] Python action/plant-model plugins: per-tick models and step
actions (FR-4.3.8), e.g. `plugins/pump_plant.py`.
- [x] Deterministic tick-driven engine: sim-time decoupled from
wall-clock (FR-4.3.9 core).
- [x] CLI: `wago-sim serve / run / validate`.
- [x] Reports: console verdict summary, JSONL and CSV export
(`--report-jsonl` / `--report-csv`).
- [x] asyncio Modbus TCP server: ≤ 15 concurrent connections, idle
connection reaper, response-delay task.
- [x] Test suite: 124 tests incl. real-socket server tests and
`pymodbus` interop; spec §7 acceptance criteria 1–7 covered.
### Framework gaps (spec §4 — Scenario & Macro Engine)
- [ ] State machines: `states:` with entry actions, transitions on
input/output edges or conditions, and guards (FR-4.3.2) —
currently only the time-ordered `sequence:` is implemented.
- [ ] Actions `noise`, `sin`, `hold` on simulated inputs (FR-4.3.4) —
`set`/`reset`/`pulse`/`toggle`/`ramp` are done.
- [ ] `wait_edge` step is accepted but not yet blocking — implement
edge-wait semantics in the engine loop.
- [ ] Assertions on statistics registers, e.g. "watchdog expired",
"BadAddress counter > 0" (FR-4.3.6).
- [ ] Determinism guard: seed/record plugin RNG state so identical runs
produce identical reports (FR-4.3.9, NFR-6.3) — needed once
`noise`/`sin` or randomizing plugins exist.
- [ ] Multi-node scenarios — several couplers in one scenario
(FR-4.3.10).
### Phase 2 — Module catalog expansion & bench mode (spec §9)
- [ ] Analog module variants, counter, pulse-width, SSI, incremental
encoder, serial interface, DALI/DSI modules with their
process-data layouts (spec §12.2 of the WAGO manual).
- [ ] Mailbox parameterization traffic beyond process data.
- [ ] Bench mode tooling (§9): `--set DI0.1=ON` startup seeding,
interactive console monitor (`--monitor`) with live channel table
and change highlighting (FR-9.2.2–9.2.3).
- [ ] Console runtime manipulation commands: `set`, `pulse`, `watch`,
`reset stats` (FR-9.2.4).
- [ ] `serve --scenario` optional combination + passive-node hint
(FR-9.2.5). *(Plain `serve` without a scenario already works —
see Completed above.)*
### Phase 3 — Long-duration (continuous) simulation (spec §8)
The soak-testing feature requested 2026-10-03 — specified in §8,
not yet implemented:
- [ ] `scenario/datasets.py` — streaming CSV playback for designated
inputs: header-driven time column, `interpolation: linear|hold`,
`repeat` with seamless seams, `time_factor` acceleration,
`until` limits, bounded memory, ERROR verdict with file/line on
malformed data (FR-8.2.1–8.2.11).
- [ ] `scenario/degradation.py` — wear counters (runtime hours, start
cycles, load-hours) and degradation models (`linear`,
`exponential`, `staircase`, plugin shapes) computing plant
parameters such as `pump_capacity` (FR-8.3.1–8.3.2).
- [ ] Degradation hooks in plant plugins: `engine.parameter()` accessor
used in loop physics, e.g. actual flow = nominal × capacity
(FR-8.3.3).
- [ ] `maintenance_at` thresholds: diagnosis injection via register
`0x1050`, maintenance-alarm assertions, `maintenance_reset`
action restoring capacity and clearing diagnoses (FR-8.3.4–8.3.5,
FR-8.3.8).
- [ ] `scenario/invariants.py` — per-tick `invariants` with bounded
episode aggregation and `rate_limits` over sliding one-hour
windows (FR-8.4.1–8.4.2).
- [ ] `snapshot_every` periodic dumps of aliases, wear counters, and
degradation parameters (FR-8.4.3).
- [ ] Continuous-mode verdict rules and bounded reporting for unattended
multi-day runs (FR-8.4.4–8.4.5).
- [ ] Scheduled sequence steps via `at: <duration>` interleaved with
datasets on the sim-time axis (FR-8.4.6).
- [ ] Acceptance tests per spec §8.8 (streaming 100k-row CSV,
interpolation checks, repeat determinism, time_factor math,
alias-ownership validation, degradation/recovery round-trip,
episode recording, full 28-day soak).
### Phase 4 — Reporting (spec §11)
- [ ] FAT report generator (HTML/PDF) from scenario results and Modbus
traces (console/JSONL/CSV export is done — see Completed above).
- [ ] Scenario comparison (run-vs-run diff) and template library per
project type.
### Phase 5 — Multi-node orchestration & protocol variants (spec §11)
- [ ] Several couplers in one FAT bench (ties into FR-4.3.10).
- [ ] Modbus UDP variant.
- [ ] WBM (web-based management) simulation pages.
- [ ] **Web UI (§10)**: browser front-end for overlooking and
manipulating I/O — node/module/channel tables with `DI0.1`-style
and Modbus addresses (FR-10.2.2), live value updates from all
sources (FR-10.2.3), toggle/force/pulse/ramp manipulation through
Modbus-consistent paths (FR-10.2.4), diagnostics views with
fault injection and statistics reset (FR-10.2.5), scenario/run
view (FR-10.2.6), multi-client consistency (FR-10.2.7), optional
read-only/access control (FR-10.2.8), alias-ownership protection
during scenario runs (FR-10.2.9).
### Phase 6 — CI integration (spec §11)
- [ ] Headless scenario execution in CI pipelines.
- [ ] JUnit XML export of verdicts for test dashboards.
### Operational hardening (spec §6 — NFRs)
- [ ] Per-transaction Modbus trace log with sim-time + wall-time stamps
and PDU hex (NFR-6.4).
- [ ] Performance budget validation: ≥ 500 requests/s, ≤ 10 ms added
latency (NFR-6.1).