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.
71 lines
3.1 KiB
Markdown
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.
|