🚧 WIP
This commit is contained in:
@@ -52,6 +52,11 @@ a virtual node whose I/O behavior is driven by scripted scenarios.
|
||||
Continuous `invariants` and `rate_limits` produce bounded, episode-based
|
||||
reports for unattended runs.
|
||||
- ≤ 15 concurrent Modbus TCP connections, idle-timeout handling.
|
||||
- **Service / container deployment** — run as infrastructure:
|
||||
Dockerfile (non-root, health check, ~197 MB), docker-compose service
|
||||
(restart policy, log rotation, read-only config mounts), systemd unit
|
||||
example, env-var configuration (`WAGO_SIM_NODE/HOST/PORT`), and
|
||||
graceful SIGTERM shutdown (`docker stop` completes in < 1 s).
|
||||
|
||||
## Install (dev)
|
||||
|
||||
@@ -81,6 +86,76 @@ wago-sim run --node config/pump_node.yaml \
|
||||
--report-jsonl out/report.jsonl --report-csv out/report.csv
|
||||
```
|
||||
|
||||
## Deployment (service / container)
|
||||
|
||||
The simulator runs as a long-lived service. Configuration comes from
|
||||
environment variables — `WAGO_SIM_NODE`, `WAGO_SIM_HOST`, `WAGO_SIM_PORT` —
|
||||
so no CLI arguments are needed in the container command.
|
||||
|
||||
Docker:
|
||||
|
||||
```bash
|
||||
docker build -t wago-sim .
|
||||
docker run -d --name wago-sim -p 5020:502 \
|
||||
-v ./config:/app/config:ro -v ./scenarios:/app/scenarios:ro \
|
||||
-v ./plugins:/app/plugins:ro -v ./out:/app/out \
|
||||
-e WAGO_SIM_NODE=config/pump_node.yaml \
|
||||
wago-sim
|
||||
```
|
||||
|
||||
Compose (restart policy, health check, log rotation included):
|
||||
|
||||
```bash
|
||||
docker compose up -d # start
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose logs -f wago-sim
|
||||
docker compose down # graceful SIGTERM shutdown
|
||||
```
|
||||
|
||||
Bare metal / VM (systemd; unit file in `deploy/wago-sim.service`):
|
||||
|
||||
```bash
|
||||
sudo cp deploy/wago-sim.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload && sudo systemctl enable --now wago-sim
|
||||
journalctl -u wago-sim -f
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Host port 5020 by default; use 502 if your bench expects it and your
|
||||
host allows binding privileged ports (the container runs non-root;
|
||||
the systemd unit grants `CAP_NET_BIND_SERVICE` to a dedicated user).
|
||||
- `docker stop` sends SIGTERM; the server closes all client connections
|
||||
and the listening socket cleanly (< 1 s) — useful for testing the
|
||||
PLC's fieldbus-failure reaction deliberately.
|
||||
- Reports from headless `run` invocations go to the mounted `out/`.
|
||||
|
||||
## Verbose mode (request tracing)
|
||||
|
||||
Run with `-v` / `--verbose` (or `WAGO_SIM_VERBOSE=1` for containers) to
|
||||
trace every Modbus transaction in plain text — with the WAGO 750-362
|
||||
register-map meaning of every address annotated:
|
||||
|
||||
```
|
||||
$ wago-sim serve --node config/pump_node.yaml -v
|
||||
|
||||
REQ txn=0x0001 unit=1 FC03 Read Holding Registers
|
||||
addr=0x2002 count=1 [config/constant regs 0x2002]
|
||||
RSP txn=0x0001 unit=1 FC03 -> 1 regs: [0x1234]
|
||||
REQ txn=0x0002 unit=1 FC06 Write Single Register
|
||||
addr=0x0000 value=0x1234 [output area 1 (%QW0)]
|
||||
RSP txn=0x0002 unit=1 FC06 -> echo
|
||||
EXC txn=0x0006 unit=1 FC03 -> code=0x02 Illegal Data Address
|
||||
RAW txn=0x0007 unit=1 PDU[3]: 07 00 00
|
||||
```
|
||||
|
||||
Traces go to the standard log at DEBUG level, so they appear in the
|
||||
terminal, in `docker logs`, and in `journalctl` alike. Read vs write
|
||||
mappings are annotated correctly per the manual's Tables 119/120
|
||||
(e.g. word address `0x0000` reads as `%IW0` but writes to `%QW0`).
|
||||
|
||||
## Documentation
|
||||
|
||||
- `docs/spec/software-spec.md` — the software specification
|
||||
@@ -101,7 +176,7 @@ Work remaining, tracked against the software specification
|
||||
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)
|
||||
### Completed — framework phase (spec §3 + §4 core, 141 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
|
||||
@@ -141,13 +216,30 @@ requirement ID for each item in parentheses.
|
||||
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] Service / container deployment (spec §12): env-var config
|
||||
(`WAGO_SIM_NODE/HOST/PORT`, CLI args override), graceful
|
||||
SIGINT/SIGTERM shutdown via `server.stop()`, Dockerfile
|
||||
(python:3.12-slim, non-root user, TCP health check),
|
||||
docker-compose service (restart policy, resource limits, log
|
||||
rotation, read-only config mounts), systemd unit example with
|
||||
hardening; verified end-to-end (build, Modbus round-trip,
|
||||
`docker stop` in 0.34 s); CLI env tests added.
|
||||
- [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.
|
||||
- [x] Test suite: 178 tests incl. real-socket server tests, `pymodbus`
|
||||
interop, CLI env-config and bad-argument tests, and plain-text
|
||||
trace tests; spec §7 acceptance criteria 1–7 covered.
|
||||
- [x] Clean CLI error handling: invalid arguments (unknown command/
|
||||
options, non-numeric or out-of-range `--port`, bad
|
||||
`WAGO_SIM_PORT`), missing/unparsable node and scenario files, and
|
||||
unwritable report paths all produce a one-line
|
||||
`wago-sim: error: …` on stderr with exit code 2 — never a Python
|
||||
traceback; missing report directories are auto-created;
|
||||
internal errors keep the full traceback for debugging (NFR-6.4
|
||||
operational goal).
|
||||
|
||||
### Framework gaps (spec §4 — Scenario & Macro Engine)
|
||||
|
||||
@@ -271,7 +363,12 @@ streaming, or entirely without it.
|
||||
|
||||
### Operational hardening (spec §6 — NFRs)
|
||||
|
||||
- [ ] Per-transaction Modbus trace log with sim-time + wall-time stamps
|
||||
and PDU hex (NFR-6.4).
|
||||
- [x] Plain-text request/response tracing in verbose mode (`-v` /
|
||||
`WAGO_SIM_VERBOSE`): every transaction logged as readable
|
||||
`REQ`/`RSP`/`EXC`/`RAW` lines with WAGO register-map annotations
|
||||
(input/output areas, %IW/%QW mapping, read-back mirrors,
|
||||
config/constant registers, illegal areas) per Tables 119/120 —
|
||||
`src/wago_sim/trace.py` + tests. Covers NFR-6.4's readability goal;
|
||||
PDU hex is included in `RAW` lines for malformed frames.
|
||||
- [ ] Performance budget validation: ≥ 500 requests/s, ≤ 10 ms added
|
||||
latency (NFR-6.1).
|
||||
Reference in New Issue
Block a user