# wrps-demo-kit A working method for building **demo automation use cases** on **Yokogawa CI Server** (SCADA) and **OpenPLC Runtime** (soft PLC, in Docker) — with the **Waterloo Road Pump Station** as its first complete example. The demo is **live**: a simulated three-pump station runs on a soft PLC and is polled over Modbus TCP by CI Server, with operator displays that command it. > [!NOTE] > **The pump station is not real.** There is no Waterloo Road Pump Station. The > whole design set was generated with AI from a one-page idea, deliberately > following a real project lifecycle. That method is the most reusable thing here > — see `01-design/README.md`. ## The two machines | Role | Host | Address | |---|---|---| | **SCADA** — Yokogawa CI Server R1.03 | `yau-poc-cicore1` | `10.0.0.21` | | **PLC** — OpenPLC Runtime v4, in Docker | `yau-sls-poc-lin001` | `10.0.0.17` | **Architecture invariant:** OpenPLC Runtime is the Modbus TCP **server** (slave) on port 502. CI Server is the Modbus **client** (master) and polls it. It is never the other way round. ## The chain — everything derives from the PLC This is the spine of the repository. One table in `03-plc/build.py` generates the contract; everything downstream is derived from it, so the two sides cannot drift apart. ``` 03-plc/src/*.st the PLC program - hand written │ build.py ▼ 03-plc/register-map.csv THE CONTRACT - 69 Modbus points │ gen_scada_points.py ▼ 04-scada/modbus_points/ ci-server-points.csv the SCADA view - 49 points, poll groups, units │ gen_ciserver_qli.py ▼ wrps_*.qli 6 sections · 49 points · 49 items │ dssqld -i (quickload) ▼ CI SERVER ◄──── Modbus TCP ──── the PLC on 10.0.0.17:502 ▲ │ file copy │ 04-scada/hmi/out/*.xml 6 operator displays ``` **Never hand-edit anything downstream of `src/`.** Change the `REGISTERS` table in `03-plc/build.py`, rebuild, then re-run the SCADA generators. Verified 2026-09-02: every generated file in that chain reproduces **byte-identically**. ## Where to start | If you want to… | Read | |---|---| | understand the demo and why it exists | `01-design/00-origin/` — two minutes | | build design docs for your **own** demo | `01-design/README.md` — the AI-generated lifecycle method | | change the PLC program | `03-plc/GETTING-STARTED.md` | | understand how the PLC program is built | `03-plc/ARCHITECTURE.md` | | stand up a **new** PLC container | `03-plc/GETTING-STARTED.md` §2 | | know what version of everything is running | `03-plc/VERSIONS.md` | | get onto the machines | `02-environment/README.md` | | import or export CI Server configuration | `04-scada/QUICKLOAD.md` | | fix a display whose values are dead | `04-scada/README.md` | ## Layout ``` 01-design/ the design authority — and how it was produced 00-origin/ the one-page idea everything grew from 02-environment/ the two hosts, access, ports, the container, migration 03-plc/ the PLC: ST sources, generators, the register map as-built/ what is ACTUALLY running, copied from the container 04-scada/ the CI Server side modbus_points/ the tag database, and the historian hmi/ the displays and their generator ciserver-backup-2026-08/ ⚠️ outdated exports, evidence only 05-tests/ Modbus verification harness, and a fake PLC 99-reference/ read-only worked examples — sorts last on purpose ``` ## What to reuse for the next demo | Reuse as-is | Adapt per demo | WRPS-only | |---|---|---| | `03-plc/gen_project.py` — the Editor has no flat-`.st` import | `03-plc/build.py`'s `REGISTERS` table — change the tags, keep the machinery | The design documents | | `05-tests/verify_modbus.py` — proves the map before you touch SCADA | `04-scada/modbus_points/gen_ciserver_qli.py` — sections, NSIDs, units | The ST control logic | | `05-tests/fake_plc.py` — tests the SCADA side with no PLC at all | `04-scada/hmi/build_display.py` — the layout is WRPS, the component kit is not | The displays | | The `IO_MUX` pattern — one POU owns every located variable | `02-environment/openplc-compose.yml` | `03-plc/as-built/` | | `01-design/README.md`'s document-generation method | | | The single most transferable idea is the **generated contract**: one table produces the PLC's register map, the SCADA point list and the CI Server import files. Nothing is transcribed by hand, so nothing silently disagrees. ## ⚠️ Open items — read before relying on this | | | |---|---| | **No acceptance tests have been run** | `01-design/WRPS-CTL-003` specifies 14 pass-1 and 6 pass-2 tests. **None have been executed.** The program compiles, runs and publishes its registers correctly — which says nothing about whether the control logic is right. This is the largest gap in the project. | | **No deployment toolchain** | The OpenPLC Editor lived only on the retired `dev-ubuntu` host. STruC++ ships only inside it, so the PLC program is effectively **frozen** until someone installs the Editor. `03-plc/DEPLOY.md` §0. | | **CI Server state unverified** | `yau-poc-cicore1` was unreachable during the 2026-09 audit. Everything in `04-scada/` is what the repo believes, not what the server holds. | | **The historian has no generator** | It is the one hand-maintained link in a generated chain, and it has drifted: the server runs `WRPS_ONE_SEC` at **5 s**, not 1, and `WRPS_THIRTY_SEC` at 30 s rather than a 60 s `WRPS_ONE_MIN`. **Never hardcode a sample interval.** | | **PLC scan overruns** | The 100 ms task overran ~100 times over nine days. Logged, tolerated, uninvestigated. | | **The runtime image is irreplaceable** | Made with `docker commit`, in no registry. Deleting it loses the compiled program. `03-plc/as-built/` is the only other copy. | | **Live demo tuning is not in this repo** | Operators retune setpoints over Modbus; those live only in the container volume and revert on restart. | ## Conventions - **`src/` is canonical.** The repo leads; the servers follow. Never treat a file on a host as the source of truth. - **Generated files are never hand-edited.** Every one says so in its header. - **Secrets are never committed.** They live in `02-environment/secrets.local.md`, which is git-ignored. A template shows the shape. - **`99-reference/` is read-only.** Never edited, never deployed. ## Verified 2026-09-02 Everything below was checked against the running systems during the audit, not taken from documentation: - The deployed PLC program **matches `03-plc/src/`** — identical POU structure, zero differences in non-declaration lines - `register-map.csv`, `ci-server-points.csv` and all three `.qli` **regenerate byte-identically** - All six displays build clean - `verify_modbus.py` against `fake_plc.py`: **69/69 read, 15/15 writable, exit 0** - The live PLC is running the **simulation** build, wet-weather scenario, and CI Server is polling it --- *Assembled from the WRPS working repo, September 2026.*