Corrected in README.md, CLAUDE.md, 02-environment/README.md and 03-plc/VERSIONS.md. The @VERSION 1.03.00 header inside the .qli files is the quickload FILE FORMAT version, not the CI Server release. Left alone. This also sharpens a caveat that was previously stated wrongly. The EQP-E-DUP_ITEM rejection - an item import fails if two items share an IO_ADDRESS - was observed on 2026-08-14 on the DEVELOPMENT machine, which ran R1.03. Production runs R1.05, where it has never been tested. The design still works around it by publishing level and speed in % only, and that workaround costs nothing; but anyone wanting a second unit for a measurement should try it on R1.05 before assuming they need a second PLC register to carry it.
139 lines
7 KiB
Markdown
139 lines
7 KiB
Markdown
# 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.05 | `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.*
|