wrps-demo-kit/README.md
Clio Liu a7db705399 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.
2026-09-02 17:29:30 +10:00

7 KiB

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.