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 registers0x1000–0x2FFF, constants0x2000–0x2008(byte-order check0x1234). - 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=ONstartup 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. - Runtime device configuration (requested, planned — spec §11):
WBM/DIP-switch parity — module "plug/unplug" at runtime with
process-image rebuild (SAVE CONFIG / KBUS
0x102Bsemantics), address-selection DIP-switch emulation (host ID 1–254, 0 = stored config, 255 = DHCP), WBM-equivalent pages (Port/Ethernet, Modbus settings, Modbus Mapping, I/O Config), and device-state persistence with factory reset. - 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, seamlessrepeat,time_factoracceleration), while degradation models age plant parameters (e.g., pump capacity) from wear counters — exercising the PLC's compensation, maintenance-alarm and service-recovery logic. Continuousinvariantsandrate_limitsproduce bounded, episode-based reports for unattended runs. - ≤ 15 concurrent Modbus TCP connections, idle-timeout handling.
Install (dev)
pip install -e .[dev]
Usage
Run the Modbus TCP server against a node configuration:
wago-sim serve --node config/pump_node.yaml --port 502
Validate a scenario:
wago-sim validate --scenario scenarios/pump_station_fat.yaml
Run a scenario headless and write reports:
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
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)
- 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).
- 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, constants0x2000–0x2008(incl. byte-order check0x2002=0x1234). - Configuration/diagnostic/firmware registers (§3.5): image sizes
0x1022–0x1025, statistics0x1029, connections0x102A, idle timeout0x1030, MAC-ID0x1031, response delay0x1037, module diagnosis0x1050, LED error0x1020/0x1021. - Modbus watchdog (§3.4): registers
0x1000–0x100B, Standard and Alternative modes, trigger/masks/stop sequences, expiry clears outputs, address-aware0x04lock in Standard mode, optional TCP close on timeout. - Fault-injection paths (§3.6): response-delay injection, connection refusal beyond 15, illegal-address probing responses, diagnosis/LED registers scriptable.
- 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).
- Scenario DSL (§4): named aliases with
scale/offset, macros with parameters (inline expansion, blocking waits inside macros), sequence stepsset/reset/toggle/pulse/ramp/wait/assert/report, rising-edge detection on all aliases. - 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. - Assertions with
within:timeouts → PASS/FAIL verdicts with sim-time stamps (FR-4.3.5). - Python action/plant-model plugins: per-tick models and step
actions (FR-4.3.8), e.g.
plugins/pump_plant.py. - Deterministic tick-driven engine: sim-time decoupled from wall-clock (FR-4.3.9 core).
- CLI:
wago-sim serve / run / validate. - Reports: console verdict summary, JSONL and CSV export
(
--report-jsonl/--report-csv). - asyncio Modbus TCP server: ≤ 15 concurrent connections, idle connection reaper, response-delay task.
- Test suite: 124 tests incl. real-socket server tests and
pymodbusinterop; 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-orderedsequence:is implemented. - Actions
noise,sin,holdon simulated inputs (FR-4.3.4) —set/reset/pulse/toggle/rampare done. wait_edgestep 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/sinor 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=ONstartup 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 --scenariooptional combination + passive-node hint (FR-9.2.5). (Plainservewithout 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. Datasets are optional: signal logic (plugins, scheduled steps, degradation) runs in parallel with CSV streaming, or entirely without it.
scenario/datasets.py— streaming CSV playback for designated inputs (OPTIONAL driver): header-driven time column,interpolation: linear|hold,repeatwith seamless seams,time_factoracceleration,untillimits, bounded memory, ERROR verdict with file/line on malformed data (FR-8.2.1–8.2.11).- Parallel logic pipeline — fixed per-tick order: datasets → edge detection → plant plugins → degradation → invariants/rate limits → scheduled steps → snapshots; plugins may read dataset-driven and logic-driven aliases in the same tick (FR-8.2.12).
- Logic-only continuous runs — no
datasets:needed: demand profiles generated inside plugins, top-leveluntil:bounds the run; degradation/invariants/rate limits/snapshots apply identically (FR-8.2.13). - Continuous-mode run-end + validation rules: run ends when all
drivers exhausted; scenario with no datasets, no scheduled steps
and no
until:rejected (FR-8.2.1). scenario/degradation.py— wear counters (runtime hours, start cycles, load-hours) and degradation models (linear,exponential,staircase, plugin shapes) computing plant parameters such aspump_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_atthresholds: diagnosis injection via register0x1050, maintenance-alarm assertions,maintenance_resetaction restoring capacity and clearing diagnoses (FR-8.3.4–8.3.5, FR-8.3.8).scenario/invariants.py— per-tickinvariantswith bounded episode aggregation andrate_limitsover sliding one-hour windows (FR-8.4.1–8.4.2).snapshot_everyperiodic 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, logic-only run, mixed dataset+logic tick-order run).
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). - Runtime device configuration (§11): module hot-configuration
(plug/unplug at slot positions) with process-image rebuild per
KBUS/SAVE-CONFIG semantics and updated
0x1022–0x1025/0x2030+registers (FR-11.2.1–11.2.2); alias revalidation after rebuilds with position-stable module addressing (FR-11.2.3); rejection during running scenarios (FR-11.2.4); address-selection DIP-switch emulation with the manual's exact coding (1–254 host ID, 0 = stored config, 255 = DHCP, restart-to-apply) (FR-11.3.1–11.3.4); WBM-equivalent pages — Port/Ethernet, Modbus settings, Modbus Mapping, I/O Config with SAVE CONFIG (FR-11.4.1–11.4.4); device-state persistence and factory reset via0x2043(FR-11.5.1–11.5.2).
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).