docs: top-level README
The old repo had no landing page at all - a reader arriving on the git
server saw a folder listing and CLAUDE.md, which is written for an agent.
Leads with what the repo is, that the demo is live, and that the pump
station is fiction generated from a one-page idea. Then the two machines,
the architecture invariant, and the generation chain, which is the spine
of the whole thing.
Two tables aimed at the team this is being handed to:
'Where to start' - one row per question someone actually arrives with
'What to reuse' - reuse as-is / adapt per demo / WRPS-only, so the
next demo does not start by guessing which parts
are method and which are this example
Open items are on the front page rather than buried: no acceptance tests
have been run, there is no deployment toolchain, CI Server state is
unverified, the historian has drifted, the runtime image is
irreplaceable, and live setpoint tuning is not in the repo.
Closes with what was verified against the running systems on 2026-09-02,
so the claims above are traceable rather than asserted.
This commit is contained in:
parent
bed824a9a5
commit
a7db705399
1 changed files with 124 additions and 14 deletions
138
README.md
138
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.*
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue