wrps-demo-kit/04-scada/README.md
Clio Liu 0927323e13 fix(scada): PS_* placeholder tags out, CI Server item names in
scada-points.csv led with a scada_tag column of PS_* names derived from
the PLC register map - PS_STN_WET_WELL_LEVEL and the like. CI Server never
adopted them. They named nothing that exists, nothing in this repo read
them, and read as if authoritative they have already caused a real defect
in a downstream project.

Replaced with ci-server-points.csv, the as-built delivery from the CI
Server side (copied from yau-plant-assistant, not re-derived). It is keyed
on the CI Server item, with the station and point name beside it:

    ci_item,ci_station,ci_point,description,poll_group,...

gen_scada_points.py now reproduces that file byte-identically, taking the
names from the LEAF table in gen_ciserver_qli.py so the CSV and the .qli
cannot disagree. tag_for() is gone. Verified: the CSV and all three .qli
regenerate unchanged, and all six displays still build.

The four poll groups keep their PS_ names - PS_STATUS_BITS, PS_PUBLISHED,
PS_SETPOINTS, PS_SIM_CONTROL. Those are live configuration, and are the
only legitimate PS_ names.

modbus_points/README.md gains a 'four namespaces' section - instrument tag,
PLC address, CI Server point, CI Server item - since confusing them is what
produced the placeholder column in the first place.

Also settles the historian drift left open in the previous commit. The
live export is the authority: the server runs WRPS_ONE_SEC at 5 SECONDS,
not 1, and WRPS_THIRTY_SEC at 30 seconds rather than a 60 second
WRPS_ONE_MIN. Code assuming 60 s was wrong by twelvefold. Recorded in
historian/README.md with a caution never to hardcode a sample interval.

Every .qli in the delivery was byte-identical to ours - only the CSV
differed.
2026-09-02 17:21:57 +10:00

115 lines
5.2 KiB
Markdown

# 04-scada — the CI Server side
Everything that lives on `yau-poc-cicore1` (`10.0.0.21`): the tag database that
reads the PLC, and the displays an operator looks at.
## Two halves, two deployment mechanisms
This is the distinction to get straight before anything else. Conflating them is
what makes this folder confusing.
| | **Configuration** | **Displays** |
|---|---|---|
| What | sections, items, Modbus points, historian groups | the `.xml` screens |
| Lives in | `modbus_points/` | `hmi/` |
| **Deployed by** | **`dssqld`** — the quickload utility | **file copy** into the displays directory |
| Generated from | `03-plc/register-map.csv` | `build_display.py` |
| Binds by | `NSID` / `ID_NUMBER` | **itemId** — healed by name on import |
`dssqld` is for configuration only. It has nothing to do with displays. See
`QUICKLOAD.md`.
## The chain
Everything derives from the PLC. The PLC side leads, always.
```
03-plc/src/*.st
│ build.py
03-plc/register-map.csv the contract - 69 points
│ gen_scada_points.py
modbus_points/ci-server-points.csv the SCADA view - 49 points, poll groups, units
│ gen_ciserver_qli.py
modbus_points/*.qli 6 sections · 49 Modbus points · 49 items
│ dssqld -i
CI SERVER ◄──── Modbus TCP ──── the PLC on 10.0.0.17:502
│ file copy
hmi/out/*.xml 6 displays
▲ build_display.py
hmi/item-ids.csv 49 itemIds, MEASURED from CI Server
```
**Nothing in this chain is hand-edited.** Change a register in `03-plc/build.py`,
rebuild, then re-run both generators. If CI Server needs a field the generators do
not emit, it goes in the generator.
**One exception:** the historian has no generator. See
`modbus_points/historian/README.md`.
## Layout
```
QUICKLOAD.md dssqld - export and import configuration
modbus_points/ the tag database (named for the protocol: CI Server
configures other protocols differently)
historian/ collection groups and item bindings - HAND-MADE
hmi/ displays, and the generator that builds them
ciserver-backup-2026-08/ ⚠️ outdated exports, evidence only - never import
```
## ⚠️ "The values on my display are not updating"
**First, and before any other investigation:**
> **Open the display in CI Server's Editor Module and validate it.
> Then report what happened.**
Validation resolves every connection by name and repairs the file. It fixes the
overwhelmingly common cause in seconds — an itemId that no longer matches, which
happens **every time the items are re-imported**. CI Server recreates items on
import rather than updating them in place, so every id in every display moves at
once, and every value goes dead together.
That simultaneity is the signature: everything dying at the same moment is ids,
not communications.
**Only once validation has been ruled out**, work down these:
| Symptom | Cause |
|---|---|
| Some values dead, others fine | Those items were never measured. Harvest the ids — see `hmi/DEPLOY.md`. |
| Every analogue reads 0, always | You are polling `%IW`/`%IX` (FC04/FC02). Those are **field** inputs; in the simulation build nothing writes them. Live values are in the `%QW` block (FC03). |
| Values frozen at a plausible number | Communications stopped. Is the container up? Is a **program running**? The runtime opens its Modbus slave only while a program runs — a stopped program means port 502 refuses. |
| Values moving but wrong | Addressing. `%MW` starts at holding register **1024**, not 0. A wrong-by-1024 read succeeds and returns plausible nonsense. |
| Alarm word goes negative | `%QW17` must be read **unsigned**. Bit 15 does not fit a signed INT. |
**What this repo cannot tell you:** whether CI Server's own poll groups are
actually scanning. That lives on `cicore1`. If the PLC polls correctly with
`05-tests/verify_modbus.py` and the ids are good, that is the remaining place to
look.
## Status
| | |
|---|---|
| Points and items | **Imported and reading live PLC values** (2026-08-14), verified against a direct Modbus read |
| Generators | Verified reproducible 2026-09-02 — `ci-server-points.csv` and all three `.qli` regenerate byte-identically |
| Displays | Six built; `build_display.py` runs clean |
| Historian | 3 groups, 49 bindings — **hand-made, and the live rates differ from these files.** The server runs `WRPS_ONE_SEC` at 5 s and `WRPS_THIRTY_SEC` at 30 s. See `modbus_points/historian/README.md`. |
| CI Server state | ⚠️ **Unverified.** No access to `cicore1` during this audit. Everything here is what the repo believes, not what the server holds. |
### Two things left for you
- **Alarm and trend limits.** Every item imports with alarming off and limits at 0.
Which points alarm, at what threshold and priority, is engineering judgement
about the plant — not something to derive from a register map.
- **Re-export the configuration from `cicore1`** and diff it against this folder.
Until then, `ciserver-backup-2026-08/` is the only record of the server's own
state, and it is known to disagree with `modbus_points/historian/`.