wrps-demo-kit/04-scada/QUICKLOAD.md
Clio Liu 947f632d7f feat(scada): CI Server tag database, historian, displays
The old 05-scada/, restructured around the distinction its README never
drew: configuration is deployed with dssqld, displays are deployed by
file copy. Conflating the two is what made the folder confusing.

  modbus_points/          the tag database - named for the protocol,
                          since CI Server configures others differently
  modbus_points/historian/  3 groups, 49 bindings - HAND-MADE, no
                          generator, and drifted from the server
  hmi/                    displays and their generator
  ciserver-backup-2026-08/  outdated exports, evidence only, never import
  QUICKLOAD.md            dssqld export/import, the 5 classes, the import
                          order, and why an item import kills every display
  README.md               the chain end to end, and the not-updating triage

Verified during the move - the whole chain is reproducible:
  scada-points.csv and all three .qli regenerate byte-identically
  all six displays build clean

Removed the K offset machinery from build_display.py, item-ids.meta.json
and DEPLOY.md. K was a consistency check on measured ids, not a source of
them, and diagnosing a dead screen by arithmetic is wasted effort when
validating the display in CI Server's Editor Module fixes it outright.
The guidance now leads with that one action.

Recorded, not fixed: the repo's historian config disagrees with the
2026-08 CI Server export - WRPS_THIRTY_SEC and a FIVE_SECONDS group exist
on the server and not here. cicore1 was unreachable during this audit, so
which is correct is unknown.

Not carried across: __pycache__, out/*.xml, and the top-level
WRPS_Overview.xml that was tracked despite .gitignore declaring the
display XMLs to be build output.
2026-09-02 17:16:18 +10:00

4.7 KiB

Quickload — moving configuration in and out of CI Server

dssqld is CI Server's quickload utility. It exports and imports configuration datasets as .qli files: sections, items, Modbus points, historian groups.

Important

Quickload is for configuration only. It is not how displays are deployed. HMI displays are plain file copy/replace into the deployment's displays directory — see hmi/DEPLOY.md. Do not look for a dssqld command for them; there isn't one.

The two commands

Export — writes every record of one class to a file:

dssqld -d MODBUS_POINT_DF -e modbus-points.qli

-d names the class, -e the output file.

Import — loads a file back in:

dssqld -i modbus-points.qli

The class is read from the file's own @ header, so import takes no -d.

Other classes may need different flags. These two forms are the ones proven on this project. If a command fails, ask — do not guess at flags against a live configuration.

The classes this project uses

Class File Holds Generated?
SECTION_DF modbus_points/wrps_section_df.qli the 6 AID.WRPS.* sections gen_ciserver_qli.py
MODBUS_POINT_DF modbus_points/wrps_modbus_point_df.qli 49 Modbus point definitions
ITEM_DF modbus_points/wrps_item_df.qli 49 items, bound to those points
HIS_GROUP_DF modbus_points/historian/his_group.qli 3 collection groups hand-made
ITEM_HIS_DF modbus_points/historian/item_his.qli 49 item→group bindings hand-made

Import order — it matters

dssqld -i wrps_section_df.qli          # 1. sections
dssqld -i wrps_modbus_point_df.qli     # 2. points
dssqld -i wrps_item_df.qli             # 3. items
dssqld -i historian/his_group.qli      # 4. collection groups
dssqld -i historian/item_his.qli       # 5. item -> group bindings

Each step depends on the one before it:

  • Sections first. CI Server derives its hierarchy from the dots in a name, and a section must exist before an item can be created inside it.
  • Points before items, because an item binds to a point.
  • Groups before bindings, because ITEM_HIS_DF names both an item and a group and needs both to exist.

⚠️ After importing items, every display goes dead

An item import renumbers every item. Proven on 2026-08-14: items were re-imported to change engineering units, keeping every NAME, NSID and ID_NUMBER byte-identical — and every itemId still moved. CI Server recreates items on import rather than updating them in place.

Displays bind by itemId at runtime, so every value on every screen dies at once.

The fix is one action, and it is not a rebuild:

Open each display in CI Server's Editor Module and validate it.

Validation resolves every connection by name and rewrites the ids. Then re-harvest so the next build ships the current ids:

python hmi/build_display.py --harvest <the saved display>.xml

WRPS_TagTest binds every project item, so harvesting that one display measures all 49 in a single pass.

Moving displays to a different CI Server needs no rebuild and no re-harvest: importing a display into the target resolves its connections by name on the way in. Import, then validate. The files built for one server are the files you ship to the next — provided the item names are identical, which means ROOT, INSTALL and STATION in gen_ciserver_qli.py must match on both.

Exporting, to check what the server actually holds

The repo cannot see cicore1, so the only way to confirm the server agrees with this folder is to export and diff:

dssqld -d SECTION_DF       -e check_section_df.qli
dssqld -d MODBUS_POINT_DF  -e check_modbus_point_df.qli
dssqld -d ITEM_DF          -e check_item_df.qli
dssqld -d HIS_GROUP_DF     -e check_his_group.qli
dssqld -d ITEM_HIS_DF      -e check_item_his_df.qli

An export contains every record of that class, not just this project's — the 2026-08 export in ciserver-backup-2026-08/ holds 147 WRPS item records among others. Filter on AID.WRPS when comparing.

This has already caught a real drift. The 2026-08 export shows historian groups (WRPS_THIRTY_SEC, FIVE_SECONDS) that modbus_points/historian/his_group.qli does not define. One of the two is stale and nobody currently knows which.

Format reference

CI Server publishes no documentation for the .qli format. Ten real exports are kept in 99-reference/ciserver-qli-exports/ — every field layout and constant in gen_ciserver_qli.py was copied from them. If you need a class this project does not use, export one record of it and read the header.