wrps-demo-kit/04-scada/modbus_points/historian/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

71 lines
3.1 KiB
Markdown

# historian — collection groups and item bindings
**Hand-made. There is no generator for this.** Everything else under
`modbus_points/` is generated from `03-plc/register-map.csv`; these two files are
not, and that is the reason they have drifted from the server.
| File | Class | Holds |
|---|---|---|
| `his_group.qli` | `@HIS_GROUP_DF` | 3 collection groups |
| `item_his.qli` | `@ITEM_HIS_DF` | 49 item → group bindings |
## ⚠️ These files are the INTENDED rates. The server runs different ones.
> **Resolved 2026-09-02.** The question of which was correct is now answered:
> `../../ciserver-backup-2026-08/export_his_group.qli`, taken from the running
> server, is what the historian actually did. **The live export wins.**
| | `his_group.qli` says | The running server does |
|---|---|---|
| Fast group | `WRPS_ONE_SEC` at **1 second** | `WRPS_ONE_SEC` at **5 seconds** |
| Second group | `WRPS_ONE_MIN` at **60 seconds** | `WRPS_THIRTY_SEC` at **30 seconds** |
| Event group | `WRPS_EVENT`, on change | `WRPS_EVENT`, on change ✅ |
> [!CAUTION]
> **Never hardcode a sample interval from these files.** Anything assuming
> `WRPS_ONE_MIN` at 60 seconds is wrong by **twelvefold** against a group that is
> actually running at 5 seconds. This has already produced a real defect in a
> downstream project. Read the interval from the item's own configuration.
## The three groups as written here (intended, not live)
| Group | Items | Intended collection |
|---|---|---|
| `WRPS_ONE_SEC` | 5 | 1 s — the fast analogues: level, inflow, discharge |
| `WRPS_ONE_MIN` | 4 | 1 min — slower trends |
| `WRPS_EVENT` | 40 | on change — states, commands, alarms, trips |
Written 2026-08-21.
## Why this matters more than it looks
The demo this project exists to support is built on **historical grounding**. From
`01-design/00-origin/`:
> "Are you sure? What did inflow actually do last time we had rain like this?"
The answer to that question comes from the historian. Layer 1 of the demo — risk
and headroom — works from live values alone. The push-back that earns trust does
not. **Without these two files there is no historian, and the demo loses the layer
it was designed around.**
## Why they disagree
These files are the *source project's* intent. The server was tuned afterwards and
nobody brought the change back. That is the predictable cost of the one link in
this chain with no generator: the points and items cannot drift because they are
regenerated; the historian can, and did.
Before importing these onto a working server, **export what is there first**
(`../../QUICKLOAD.md`) and compare. Importing blind may remove a group something
depends on.
## Worth doing: generate these too
The points and items chain is generated end to end, which is what keeps the PLC
and SCADA from drifting apart. The historian stops short of that, is maintained by
hand, and has drifted — exactly the failure the generated chain exists to prevent.
Extending `../gen_ciserver_qli.py` to emit `@HIS_GROUP_DF` and `@ITEM_HIS_DF`
would close the gap. The item list is already there; what it needs is a table
saying which item belongs to which group at what rate. Not done in this audit.