📝 Update README & specifications.

This commit is contained in:
Jonas Bengtsson
2026-10-06 19:21:41 +02:00
parent 597b5bf73c
commit c8b655111e
2 changed files with 222 additions and 5 deletions
+186 -5
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 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 |
| 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), and host network settings (§15) requested 2026-10-06 |
| 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) |
@@ -1018,7 +1018,8 @@ Terminal monitors (§9) suffice for single engineers, but FAT benches are
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
overview, address-selection DIP switch) are specified in §11; the
Docker-gated host network settings page is specified in §15. §10 covers
value/state oversight and manipulation.
- FR-10.2.11: **Channel names** (requested 2026-10-03): every module
channel can carry an application-specific name (e.g. `start_button`,
@@ -1594,7 +1595,187 @@ the same process-image core.
---
## 15. Roadmap (post-framework)
## 15. Feature Request — Host Network Settings (Docker Bench Host)
> **Status: requested 2026-10-06, not yet planned into a roadmap
> phase.** A settings page in the web UI that views and manipulates
> the **host OS's IP addresses**, exposed only when an explicit
> environment flag is set **and** the simulator runs inside Docker.
> This section captures the requirements and the enabling
> architecture (a whitelisted host-side agent); it does not commit to
> a schedule.
### 15.1 Motivation
A containerized simulator (§12) cannot reach beyond its own network
namespace. It can rebind its listen address (FR-11.3.3 dedicated-IP
mode), but the **host's** network interfaces — what the bench network
and the PLC under test actually see — are outside the container. As a
result, FAT cases that depend on *network-level* events cannot be
staged from inside the simulator today:
- "Re-addressing" the virtual coupler (DIP switch, FR-11.3.1–11.3.3)
changes nothing on the bench network in the default bridge
deployment: the PLC keeps talking to the unchanged host address, so
the reconnect/re-addressing logic in the PLC program is never
exercised.
- Cable-pull / node-power-off fault cases require stopping the whole
container (coarse — every port drops at once) or shell access to the
bench host, breaking the "operate everything from the browser"
workflow of the web UI (§10).
- Moving the simulated node to a different subnet mid-bench
(multi-bench swaps, address conflicts) likewise needs host access.
The requested feature closes the gap: an opt-in settings page through
which an authenticated operator changes the host OS's IP addresses
from the bench UI — making the simulated node's network identity
**real on the bench network**, and making network-level fault cases
first-class FAT scenarios.
### 15.2 Requirements
- FR-15.2.1: **Opt-in via environment flag.** The feature is disabled
unless `WAGO_SIM_HOSTNET` is set truthy (same semantics as
`WAGO_SIM_VERBOSE`: `1`/`true`/`yes`/`on`); the flag lives in the
compose file so enabling it is a visible, deliberate deployment
change. Default: off. When off, the page is absent and every
`/api/hostnet/*` route answers 404 — no dormant surface.
- FR-15.2.2: **Docker-only.** Independently, the simulator must detect
a container environment — binding: the compose file sets
`WAGO_SIM_IN_DOCKER=1` (supplementary signal: `/.dockerenv` exists).
Outside docker the routes stay 404 regardless of the flag: a
bare-metal operator has direct access to the OS network settings;
the page exists precisely because a container does not.
- FR-15.2.3: **Settings page.** A page in the web UI's configuration
area (alongside the Port/Ethernet page, FR-11.4.1) listing the
host's network interfaces — name, link state, current IPv4
addresses — read from **live host state**, not a desired-state
mirror, so out-of-band changes made on the host appear on refresh.
The page also shows the agent's health and the effective whitelist.
- FR-15.2.4: **Operations.** Add an address (address + prefix length),
remove an address, replace an interface's primary address — each
validated client-side and agent-side (address/prefix syntax,
interface exists, interface whitelisted). Changes are
**runtime-only** (like `ip addr add`: non-persistent across host
reboots); v1 deliberately does not write netplan/NetworkManager/
systemd-networkd configuration, so a host reboot is a guaranteed
escape hatch back to the deployed addressing. Optional v2, behind
the same whitelist and an explicit "disruptive" confirmation: set
an interface's link up/down (cable-pull simulation).
- FR-15.2.5: **Safety rails.** The agent enforces an interface
**whitelist** from its own config file (operations on other
interfaces are refused and surfaced as errors on the page); it
snapshots an interface's address set before every mutation, and the
page offers **undo last** / **restore interface**; every mutation
and every refusal is appended to a single-line **audit log**
(operator session, interface, old → new, timestamp) written to
stdout/journald like the simulator's own logging (FR-12.2.6) and
viewable (bounded) on the page.
- FR-15.2.6: **Trust boundary** (same stance as FR-10.2.16): the page
requires an authenticated session (FR-10.2.8); read-only mode
(`WAGO_SIM_WEB_READONLY`) hides the page and answers mutations with
403. Changing host addresses is a privileged operation by design —
the flag must never be set on a network the operator does not
control.
- FR-15.2.7: **Host-side agent** (binding architecture decision). A
container cannot change host addresses, and the simulator container
itself shall not run privileged (§12 non-root, least-privilege
deployment). Instead a small companion service —
`wago-sim-hostnet-agent` — runs **on the host** (systemd unit in
`deploy/`, root or `CAP_NET_ADMIN`), listening on a Unix domain
socket that the compose file mounts into the container read-write.
The agent's protocol is a narrow whitelist of operations (health,
list, snapshot, address add/remove/change, restore) over JSON
lines; it never executes a shell with operator input — on Linux v1
it uses the netlink API (`pyroute2`, with the `ip` tool as a
fallback). *Rejected alternative, documented why*: running the
simulator container itself privileged with host namespaces — it
forfeits the non-root deployment and couples host control to the
simulator's own lifecycle and restarts.
- FR-15.2.8: **Degraded states never break the UI.** Flag set but the
agent socket absent/unreachable: the page shows a clear "host
control unavailable" banner and mutation routes answer 503 with a
message; the Modbus server and the rest of the web UI are
unaffected.
- FR-15.2.9: **DIP dedicated-IP integration** (FR-11.3.3). When a DIP
address change is applied in dedicated-IP mode and the derived
interface is whitelisted, the apply step may route through the same
agent — the derived IP is added to the host NIC — so the PLC on the
bench network must genuinely re-connect to the new node address,
exactly like a physical coupler after a DIP change. Under
`network_mode: host` the simulator rebinds directly per FR-11.3.3;
either way the agent supplies the address on the host.
### 15.3 Architecture sketch (planned)
```
bench host (Linux)
├── wago-sim hostnet-agent --config /etc/wago-sim/hostnet.yaml
│ (systemd unit: deploy/wago-sim-hostnet-agent.service;
│ root or CAP_NET_ADMIN — it owns the interface whitelist)
│ config: socket: /run/wago-sim/hostnet.sock, whitelist: [...]
└── docker compose
├── mounts /run/wago-sim:/run/wago-sim (agent socket in)
└── wago-sim serve (page + /api/hostnet/* routes,
gated by FR-15.2.1 flag + FR-15.2.2 docker detection)
```
Web API, mounted only under the FR-15.2.1/2 gates:
| Method/Route | Purpose | FR |
|---|---|---|
| `GET /api/hostnet` | availability (flag, docker, agent health) + interface/address list + whitelist | 15.2.3 |
| `POST /api/hostnet/interfaces/{name}/addresses` | add an address (address, prefix length) | 15.2.4 |
| `DELETE /api/hostnet/interfaces/{name}/addresses/{addr}` | remove an address | 15.2.4 |
| `POST /api/hostnet/interfaces/{name}/restore` | undo last / restore snapshot | 15.2.5 |
| `GET /api/hostnet/audit` | bounded recent audit lines | 15.2.5 |
### 15.4 Scoping questions (answer before planning)
1. Platform scope v1: Linux bench hosts only (agent via `pyroute2`)?
Windows hosts run the container inside Docker Desktop/WSL2 — the
container's "host" is the WSL2 VM; reaching the Windows NICs needs
a Windows-side agent over a named pipe — defer or include?
2. Networking-mode story for the DIP integration (FR-15.2.9): keep
the current bridge deployment (host IP + mapped port) as v1, or
require `network_mode: host` for full parity (node IP == host IP,
port 502)?
3. Agent authentication: Unix-socket file permissions only, or a
shared token in the agent config to defend against other local
users?
4. IPv6 in v1 or deferred?
5. Does link up/down (cable pull) belong in v1 — it can sever the
operator's own web UI session on that NIC — or are address
add/remove/change sufficient for the planned FAT cases?
### 15.5 Acceptance criteria (draft)
1. Without `WAGO_SIM_HOSTNET`: no page; all `/api/hostnet/*` routes
answer 404; the rest of the UI unchanged (regression suite green).
2. Flag set but not in docker: same as 1.
3. Flag set + docker + agent reachable: the page lists the host's real
interfaces; adding `192.168.1.33/24` to the whitelisted interface
shows in `ip addr` **on the host**, and the new address is
reachable from the bench network (a master connects through it).
4. Removing that address severs the master's connection through it —
the PLC's reconnect logic is exercised (the FAT case).
5. Restore/undo returns the interface to its pre-change snapshot; a
host reboot likewise restores the deployed addressing (changes are
runtime-only, FR-15.2.4).
6. An operation on a non-whitelisted interface is refused by the
agent and surfaced as an error on the page; the audit log records
every mutation and every refusal.
7. Read-only mode: page hidden, mutations 403.
8. DIP integration: applying switch position 33 in dedicated-IP mode
results in `192.168.1.33` present on the whitelisted NIC (bridge
mode) or the server rebind (host mode); the "restart required"
semantics of FR-11.3.3 are unchanged.
9. Agent socket missing: "host control unavailable" banner,
mutations 503, Modbus server and the rest of the web UI unaffected.
---
## 16. Roadmap (post-framework)
| Phase | Feature |
|---|---|
@@ -1603,11 +1784,11 @@ the same process-image core.
| 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)**; **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 |
| ? | **Multi-vendor device profiles (§13)** and **additional fieldbus protocols PROFINET/COMLI (§14)** — not yet scheduled; phase assignment after §13.3/§14.3 scoping |
| ? | **Multi-vendor device profiles (§13)**, **additional fieldbus protocols PROFINET/COMLI (§14)**, and **host network settings (§15)** — not yet scheduled; phase assignment after §13.3/§14.3/§15.4 scoping |
---
## 16. References
## 17. 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`