diff --git a/README.md b/README.md index 75eb618..c95ab76 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,139 @@ # wrps-demo-kit -A kit for building **demo automation use cases** on **Yokogawa CI Server** (SCADA) -and **OpenPLC Runtime** (soft PLC, in Docker) — with the **Waterloo Road Pump -Station (WRPS)** demo as its first worked example. +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] -> **This repository is being assembled.** It is the successor to an earlier -> working repo, and material is being reviewed and moved across one folder at a -> time. Sections not yet present have not yet been audited. +> **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 systems +## The two machines | Role | Host | Address | |---|---|---| -| SCADA — Yokogawa CI Server | `yau-poc-cicore1` | `10.0.0.21` | -| PLC — OpenPLC Runtime container | `yau-sls-poc-lin001` | `10.0.0.17` | +| **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. The -register map is the contract between the two sides. +**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 -To be filled in as each part is brought across. +``` +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, 2026-09.* +*Assembled from the WRPS working repo, September 2026.*