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.
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
displaysdirectory — seehmi/DEPLOY.md. Do not look for adssqldcommand 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_DFnames 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.