242 lines
17 KiB
Markdown
242 lines
17 KiB
Markdown
# CI-Eng-Sample
|
||
|
||
A small, invented sample project that anyone with this repo can load into a CI Server
|
||
**R1.03** test system. Skills use its names in their examples, and every skill task is
|
||
re-tested against it so that no other project is needed.
|
||
|
||
Built and tested on CI Server R1.03.00 (unlicensed test system), 2026-09-30.
|
||
|
||
## Load and remove
|
||
|
||
Test systems only: loading writes to the CI Server database. CI Server must be running.
|
||
From an elevated prompt, call the scripts by full path (a bare `load.cmd` may not be found
|
||
from the current folder):
|
||
|
||
```
|
||
cmd /c "<repo>\examples\CI-Eng-Sample\load.cmd"
|
||
cmd /c "<repo>\examples\CI-Eng-Sample\remove.cmd"
|
||
```
|
||
|
||
- Each step prints a summary and writes a `.qlo` listing; check that `Errors` is 0.
|
||
- **Restart CI Server after loading** (stop, then start the CI Service): a new storage group
|
||
stores no history until CI Server restarts.
|
||
- `load.cmd` on a system that already has the project reports `Exists` and changes nothing.
|
||
- `remove.cmd` takes about 1½ minutes: a storage group can only be deleted after its stop time
|
||
has passed, so the script sets a stop time one minute ahead and waits. Removing the group
|
||
also deletes its history.
|
||
|
||
## Current contents
|
||
|
||
| Name | Kind | Details |
|
||
|---|---|---|
|
||
| `CI-Eng-Sample` | Section | Top level |
|
||
| `CI-Eng-Sample.TANK1`, `.PUMP1`, `.SYSTEM` | Sections | Tank, pump, system monitoring |
|
||
| `CI-Eng-Sample.TANK1.LEVEL` | Item, Real | %, format `999.9`, limits LL 10 / L 20 / H 80 / HH 90, scale 0–100, stored in `CIENG_EVENT`; alarming on (ack type `CIENG_ACK`, area `CIENG_TANKS`; `16_item_alarm_df.qli`) |
|
||
| `CI-Eng-Sample.TANK1.INFLOW` | Item, Real | m3/h, format `9999.9`, scale 0–500, stored in `CIENG_EVENT` |
|
||
| `CI-Eng-Sample.PUMP1.RUNNING` | Item, Boolean | |
|
||
| `CI-Eng-Sample.PUMP1.MODE` | Item, Integer | 0 manual, 1 auto |
|
||
| `CI-Eng-Sample.SYSTEM.CPU_LOAD` | Item, Real | Target for `intcpumon` |
|
||
| `CI-Eng-Sample.SYSTEM.DISK_C` | Item, Real | Target for `intdskmon` (reports **used** %) |
|
||
| `CI-Eng-Sample.SYSTEM.PING_LOCALHOST` | Item, Integer | Target for `inttcpmon` (1 = reachable) |
|
||
| `CI-Eng-Sample.SYSTEM.PROC_ALM` | Item, Integer | Target for `intmon` (1 = `alm` process running) |
|
||
| `CI-Eng-Sample.PUMP1.RUNNING.RUN_SECONDS` | Sub-item, Integrator | Running time in seconds (adds value × integration granule, `itm_int_secs` in `itm.sup`, default 1 s) |
|
||
| `CI-Eng-Sample.TANK1.LEVEL.MAX` | Sub-item, Min-Max | Highest level seen |
|
||
| `CI-Eng-Sample.DEMO` | Section | Tutorial examples |
|
||
| `CI-Eng-Sample.DEMO.COUNTER` | Item, Integer | Counts 1…100, one step per second, written by `DEMO.INCR1` |
|
||
| `CIENG_1S` | Trigger group | Every second |
|
||
| `CIENG_INCR` | Class (traditional) | Increments `DEMO.COUNTER`, wraps at 100; compiled on load |
|
||
| `CI-Eng-Sample.DEMO.INCR1` | Object | Instance of `CIENG_INCR` |
|
||
| `CI-Eng-Sample.TEMPLATES` | Section | Template items for classes |
|
||
| `CI-Eng-Sample.TEMPLATES.TPL_REAL` | Item, Real | Template for items that objects create (format `9999.9`, scale 0–1000) |
|
||
| `CIENG_TANK` | Class (traditional) | Prompt *Tank capacity (m3)*; signal `LEVEL` = `<object section>.LEVEL` (trigger on value); creates `<object>.VOLUME` from `TPL_REAL`; volume = level × capacity / 100; logs when the level crosses 80; refuses capacity ≤ 0 |
|
||
| `CI-Eng-Sample.TANK1.CALC` | Object | `CIENG_TANK`, capacity 200; creates section `TANK1.CALC` and item `TANK1.CALC.VOLUME` |
|
||
| `CIENG_JFLOW` | Class (Java) | Attaches `<object section>.INFLOW`; creates `<object>.sPerMin` = inflow / 60; logs each change |
|
||
| `CI-Eng-Sample.TANK1.JFLOW` | Object | `CIENG_JFLOW`; creates item `TANK1.JFLOW.sPerMin` (mixed case, as the Java identifier) |
|
||
| `CIENG_EVENT` | Item storage group | Event/Item, lifetime 1 week, rollover 1 day |
|
||
| `CIENG_SCAN10S` | Item storage group | Scan/Time every 10 s with quality, priority 10 (source for aggregation), lifetime 1 day, rollover 1 hour; `TANK1.LEVEL`, `TANK1.INFLOW` |
|
||
| `CIENG_AGG` | Item storage group | Aggregation 30 min + hourly, lifetime 4 weeks, rollover 1 week; `TANK1.LEVEL` max/min/average (read with `INTERPOLATION = "30Minutes"` / `"Hour"`, `ATTRIBUTE = "Maximum"` …) |
|
||
| `CI-Eng-Sample.PLC1` | Section | Modbus loopback station |
|
||
| `CI-Eng-Sample.PLC1.COMM_OK` | Item, Boolean | Status item of station `CIENG_PLC` (1 = communicating, status 22 when off-scan) |
|
||
| `CI-Eng-Sample.PLC1.SETPOINT` / `.READBACK` | Items, Integer | Written to holding register 1 (`RO:1`) / read from input register 1 (`RI:1`), same memory |
|
||
| `CI-Eng-Sample.PLC1.CMD` / `.FEEDBACK` | Items, Boolean | Written to coil 1 (`DO:1`) / read from input bit 1 (`DI:1`), same memory |
|
||
| `CIENG_MB_LINE` | Modbus line (TCP/IP) | Equipment manager `EQPCIENG` (created and started by loading the line) |
|
||
| `CIENG_PLC` | Modbus station | Open Modbus/TCP to `localhost:5020`, unit 1, output method *On change and connect* |
|
||
| `CIENG_SCAN1S` | Modbus scan type | 1000 ms |
|
||
| `CI-Eng-Sample.UA` | Section | OPC UA loopback station |
|
||
| `CI-Eng-Sample.UA.COMM_OK` | Item, Boolean | Status item of station `CIENG_UA` (1 = connected) |
|
||
| `CI-Eng-Sample.UA.LEVEL_COPY` | Item, Real | `TANK1.LEVEL` read back through CI Server's own OPC UA server |
|
||
| `CI-Eng-Sample.UA.SRV_STATE` | Item, Integer | OPC UA server state node (ns 0, i=2259), 0 = Running |
|
||
| `CIENG_UA_LINE` | OPC UA client line | Client process `OPXCIENG` (created and started by loading the line) |
|
||
| `CIENG_UA` | OPC UA station | `opc.tcp://localhost:34493`, Basic256Sha256 / SignAndEncrypt, anonymous; subscription `CIENG_SUB` (1 s); points `LEVEL`, `SRV_STATE` |
|
||
| OPC visible | Flags | Sections `CI-Eng-Sample`, `TANK1` and item `TANK1.LEVEL` are OPC visible, item read-only (`12_opc_visible_df.qli`) |
|
||
| `CIENG_ACK` | Acknowledgement type | Every alarm needs an acknowledgement; return to normal automatic only if the alarm was acknowledged (same matrix as `MANUAL`) |
|
||
| `CIENG_TANKS` | Area of interest | Label for tank items (`TANK1.LEVEL` `AOI_1`) |
|
||
| `CIENG_TANK1` | Alarm selection area | `SECTION = "CI-Eng-Sample.TANK1"`, compiled on load |
|
||
| `CIENG_VIEWER` | Authorization group | Read-only: `ITEM_READ`, `SECTION_READ`, `ALARM_CURRENT_READ`, `ALARM_CHRONO_READ`. No users are created by the sample. |
|
||
| `CIENG_FILE` | Printer device + destination | FILE output to `%TLS_LST_PATH%\cieng_report.txt` (overwritten by each printed report); destination visible to operators |
|
||
| `CIENG_TANK1` | Report | Values of the `TANK1` items + last-hour statistics of one item (ad-hoc parameter chosen in an item browser, default `LEVEL`); daily 06:00 local time and ad hoc; output `CIENG_FILE`; lifetime 1 week |
|
||
| `CIENG_INFLOW_HI` | Report | Generated 5 s after `TANK1.INFLOW` rises above 400; output `CIENG_FILE` |
|
||
| `CIENG_HTML` | Report | Ad hoc; `TANK1` values as an HTML table (cursor loop) plus the logbook text |
|
||
| `CIENG_AUDIT` | Report | Ad hoc; audit trail events on `CI-Eng-Sample.*` of the last hour |
|
||
| `CIENG_ITEMS` | Audit selection group | Expression `sbid >= "CI-Eng-Sample." and sbid < "CI-Eng-Sample/"`, compiled on load |
|
||
|
||
Items created by objects (`TANK1.CALC.VOLUME`, `TANK1.JFLOW.sPerMin`) and their sections are
|
||
deleted with the objects; they are not in `02_item_df.qli`.
|
||
|
||
All items except `PLC1.*` are internal (no station). The `PLC1` station talks to CI Server's own Modbus slave mirror (see below), so no PLC is needed either.
|
||
New items start with status 13 (`NOT INIT`) until a value is written.
|
||
|
||
## Files
|
||
|
||
| File | Purpose |
|
||
|---|---|
|
||
| `01_section_df.qli` | Sections (`SECTION_DF`); parents before children |
|
||
| `02_item_df.qli` | Items (`ITEM_DF`) |
|
||
| `02b_sub_item_df.qli` | Sub-items (`SUB_ITEM_DF`) |
|
||
| `03_his_group_df.qli` | Item storage groups (`HIS_GROUP_DF`): event, scan, aggregation |
|
||
| `04_item_his_df.qli` | Items in the storage group (`ITEM_HIS_DF`, name `<group>:<item>`) |
|
||
| `05_trigger_df.qli`, `06_class_df.qli`, `07_object_df.qli` | Trigger group; classes (source in the `METHOD` memo, `@"` … `"@`, `COMPILE` 1; Traditional and Java); objects (`ATTR_VALUE_1` = prompt answer) |
|
||
| `08_auth_group_df.qli` | Authorization group (`AUTH_GROUP_DF`) |
|
||
| `09_modbus_line_df.qli`, `10_modbus_station_df.qli`, `11_item_eqp_df.qli` | Modbus line; station, scan type, points; items connected to points |
|
||
| `12_opc_visible_df.qli` | OPC visible / read flags (loaded with `dssqld -m`) |
|
||
| `13_opcua_line_df.qli`, `14_opcua_station_df.qli`, `15_item_opcua_df.qli` | OPC UA client line; station, subscription, points; connected items |
|
||
| `01b_alarm_df.qli` | Alarm definitions: acknowledgement type, area of interest, alarm selection area (`ALARM_ACK_DF`, `ALARM_AOI_DF`, `ALARM_ASA_DF`) |
|
||
| `16_item_alarm_df.qli` | Switch on alarming for `TANK1.LEVEL` (loaded with `dssqld -m`) |
|
||
| `17_printer_df.tpl`, `17_printer_df.ps1` | Report file device and destination; the script fills in this system's node name and `%TLS_LST_PATH%` and writes `17_printer_df.qli` (not in the repo) |
|
||
| `18_report_df.qli` | Reports (`REPORT_DF`, RQL in the `QUERY` memo) |
|
||
| `19_audit_df.qli` | Audit selection group (`AUDIT_SELGRP_DF`) and audit report |
|
||
| `audit/adtlogci.sup` | Setup file for an extra audit event logger `ADTLOGCI` filtered on `CIENG_ITEMS` (copied to `tls\sup` by an administrator, not by `load.cmd`) |
|
||
| `83a_stop_report_df.qli` | Clears the schedule of `CIENG_TANK1` before removal (a scheduled report cannot be deleted) |
|
||
| `equipment/eqpciengslv_start.cmd`, `_stop.cmd` | Start/stop the Modbus slave mirror (copy to `tls\com`) |
|
||
| `76_`…`93_remove_*.qli` | Names to delete, in reverse order (audit `79`, reports and printer `83`/`84` first, alarm definitions last, `93`) |
|
||
| `load.cmd`, `remove.cmd` | Run the steps in the right order |
|
||
| `displays/CIENG_Tank.xml` | Display (HTML mode): title, number field on `TANK1.LEVEL`, `PUMP1.MODE` field with buttons *Manual (0)* / *Auto (1)* (write the item), alarm overview, trend of `TANK1.LEVEL`, button *Operator layout* |
|
||
| `19b_audit_item_df.qli` | *Audit trailing wanted* for `PUMP1.MODE` (loaded with `dssqld -m`) |
|
||
| `displays/CIENG_Menu.xml` | Navigation display (200 x 700): *Tank 1*, *Current Alarm* (main frame), *Tank 1 popup* (layout `popup`) |
|
||
| `layouts/CIENG_Operator.xml`, `layouts/layout.ps1` | Operator layout block and the script that adds/removes it in `Layouts.xml` |
|
||
| `displays/CIENG_TrendLab.xml` | Trend lab (1000 x 860, HTML mode): trend of `TANK1.LEVEL` (no history group, value axis) and `TANK1.INFLOW` (`CIENG_EVENT`), live, last hour, padded with 48 spare pens so it draws in the browser; Micro Trend of both items (ISS-055) |
|
||
| `displays/add_trend_spares.ps1` | Pads every trend in a display file to 50 visible pens (web workaround, ISS-041); safe to run again |
|
||
| `displays/trend_feed.ps1` | Writes sine-wave test values to `TANK1.LEVEL` / `TANK1.INFLOW` every 5 s (`-Seconds`, default 3600) |
|
||
| `displays/deploy_displays.cmd`, `remove_displays.cmd` | Copy the displays into the operator interface and export their HTML versions / delete them |
|
||
| `monitoring/*.sup.txt` | Setup-file lines for the monitoring processes (not applied by `load.cmd`) |
|
||
|
||
## Modbus slave mirror (for station CIENG_PLC)
|
||
|
||
`CIENG_PLC` reads and writes CI Server's own Modbus slave: `eqpmdcmem` (slave memory) and
|
||
`eqpmdcslvtcp` (Modbus TCP on port 5020). Without them the station reports bad communication
|
||
(`COMM_OK` = 0) but everything loads. To start them with CI Server, an administrator copies
|
||
`equipment/eqpciengslv_start.cmd` and `eqpciengslv_stop.cmd` to `%TLS_ROOT_PATH%\tls\com`;
|
||
for a quick test they can also be started by hand from a normal prompt:
|
||
|
||
```
|
||
start eqpmdcmem -n EQPMDCMEM
|
||
start eqpmdcslvtcp -n EQPMDCSLVTCP -p 5020
|
||
```
|
||
|
||
Remove the two files from `tls\com` when the sample is removed. See skill `equipment`, task
|
||
`modbus-station.md`.
|
||
|
||
## OPC UA loopback (station CIENG_UA)
|
||
|
||
`CIENG_UA` is CI Server's OPC UA client connected to CI Server's own OPC UA server, so no
|
||
external server is needed. The **first** connection on a system needs a one-time certificate
|
||
exchange, done by the user (skill `data-access`, task `opcua-client.md` §4):
|
||
|
||
1. After loading, the client logs `OPXC-E-CER_NO_TRU`; trust the server certificate in
|
||
`%TLS_ALLUSERS_PATH%\pki\opxuac\pkiclient\rejected` (Engineering Module, or move it to
|
||
`…\pkiclient\trusted\certs`).
|
||
2. The server then rejects the client; move `CI_SERVER_OPCUA_CLIENT_OPXCIENG […].der` from
|
||
`%TLS_ALLUSERS_PATH%\pki\opxuas\pkiserver\rejected` to `…\pkiserver\trusted\certs`
|
||
(PowerShell: `Move-Item -LiteralPath`, the name contains brackets).
|
||
|
||
Then `UA.COMM_OK` = 1 and `UA.LEVEL_COPY` follows `TANK1.LEVEL`. The trust is kept when the
|
||
sample is removed and loaded again.
|
||
|
||
## Alarms (TANK1.LEVEL)
|
||
|
||
`TANK1.LEVEL` raises HIGH above 80 (Alarm 2, priority 2) and HIGH HIGH above 90 (Alarm 3,
|
||
priority 1). To try it on a test system: `itmval -p -n CI-Eng-Sample.TANK1.LEVEL -v 85`, export
|
||
`ALARM_CURRENT` (filter on `ITEM_NAME`), acknowledge with `dssqld -m` on `ALARM_CURRENT`
|
||
(`NAME`, `ACK` 1), write the level back below 80. See skill `alarm-management`, task
|
||
`item-alarming.md`.
|
||
|
||
## Displays and layout (optional)
|
||
|
||
No admin rights needed. After `load.cmd`:
|
||
|
||
1. `displays\deploy_displays.cmd` copies `CIENG_Tank`, `CIENG_Menu` and `CIENG_TrendLab` into
|
||
the operator interface and creates their HTML versions.
|
||
2. `powershell -ExecutionPolicy Bypass -File layouts\layout.ps1` adds the layout
|
||
`CIENG_Operator` to `Layouts.xml` (first run saves `Layouts-before-CIENG.xml`).
|
||
**Restart the Operator Interface** afterwards: it reads the layouts only at start.
|
||
3. Operator Interface (desktop): open `CIENG_Tank` from the display box and click
|
||
**Operator layout** - a second window opens with the sample menu (File / View / Displays),
|
||
toolbar, `CIENG_Menu` on the left, `CIENG_Tank` in the main frame `default` and the alarm
|
||
banner. HTML display (web): `http://localhost:8080/mobileoperator/?d=CIENG_Tank&l=CIENG_Operator`
|
||
(CI Server login).
|
||
|
||
Trends: `TANK1.LEVEL` and `TANK1.INFLOW` are internal items and stay `NOT INIT` after a CI
|
||
Server start, so trends stay empty. `powershell -ExecutionPolicy Bypass -File
|
||
displays\trend_feed.ps1 -Seconds 3600` writes test values every 5 s. `CIENG_TrendLab` (also
|
||
in the browser: `?d=CIENG_TrendLab`) shows the trend padded to 50 pens, which the HTML display
|
||
needs to draw (ISS-041); `CIENG_Tank`'s trend has 1 pen and draws only on the desktop.
|
||
|
||
Remove: `layouts\layout.ps1 -Remove` (restores the file exactly) and
|
||
`displays\remove_displays.cmd`. Close the displays in the Edit Module first. See skill
|
||
`graphics`.
|
||
|
||
## Monitoring processes (optional)
|
||
|
||
`monitoring/` holds the lines to add to the setup files in `%TLS_ROOT_PATH%\tls\sup\` so that
|
||
`intcpumon`, `intdskmon`, `inttcpmon`, `intmon` and `intspawn` write into the `SYSTEM` items.
|
||
These change the system's own setup files: back the files up first and restore them after
|
||
testing. See the `system-management` skill, task `monitoring-processes.md`.
|
||
|
||
## Reports
|
||
|
||
No admin rights needed. `load.cmd` creates the device, destination and three reports; each report
|
||
gets its own storage group of type *Report*. Try them (skill `reporting`, task `define-reports.md`):
|
||
|
||
- Ad hoc: `REPORT_DF` `NAME,PARAM_VALUE_1,GENERATE` = `"CIENG_TANK1","CI-Eng-Sample.TANK1.INFLOW",1`
|
||
with `dssqld -m`, or in the Engineering Module *Configuration > Reporting > Reports* › right-click
|
||
› *Generate*. The output lands in `REPORT_VAL`; runs from the Engineering Module also print to
|
||
`%TLS_LST_PATH%\cieng_report.txt` (runs from `dssqld` do not print, ISS-045).
|
||
- Trigger: `itmval -p -n CI-Eng-Sample.TANK1.INFLOW -v 450` → `CIENG_INFLOW_HI` 5 s later; write a
|
||
value below 400 before the next test.
|
||
- `CIENG_TANK1` also runs every day at 06:00 (local time).
|
||
|
||
`remove.cmd` clears the schedule, removes the reports (with their generated reports and logbook
|
||
files), the destination and device, and deletes `cieng_report.txt`.
|
||
|
||
## Audit trail
|
||
|
||
No admin rights needed for `19_audit_df.qli`. Every change made by `load.cmd`, `dssqld` or the
|
||
Engineering Module is in the audit trail (skill `audit-trail`):
|
||
|
||
- Generate `CIENG_AUDIT` (ad hoc, like the other reports) to list the last hour of changes on
|
||
`CI-Eng-Sample.*`: time, dataset, field, action, source, user, old and new value.
|
||
- Operator writes: with the displays deployed, click *Manual (0)* / *Auto (1)* on `CIENG_Tank`;
|
||
each click is stored as `ITEM.VALUE.MODIFY.OPER-INTERFACE` because `PUMP1.MODE` has *Audit
|
||
trailing wanted* (`19b_audit_item_df.qli`).
|
||
- Export: `dssqld -e audit.qlo -d AUDIT_TRAIL` (whole trail) or a qli with `@SELECT` (keep `NAME`
|
||
in `@FIELDS`).
|
||
- Event logger (optional, admin): copy `audit\adtlogci.sup` to `%TLS_ROOT_PATH%\tls\sup\`, then
|
||
run `adtlog -n adtlogci` in a console; it prints each new CI-Eng-Sample event on one line
|
||
(Ctrl+C to stop). Delete the file from `tls\sup` when the sample is removed.
|
||
|
||
Audit records themselves cannot be deleted; `remove.cmd` removes only the report and the
|
||
selection group.
|
||
|
||
## Planned additions
|
||
|
||
| Phase | Adds |
|
||
|---|---|
|
||
| 10 | A symbol and a faceplate popup (displays and the operator layout exist) |
|
||
|
||
## Rules
|
||
|
||
- Test systems only; invented names only, no real plant or customer data.
|
||
- Keep the load order: sections → template items → classes (compiled) → objects → other
|
||
items → storage groups → items in storage groups (IM 36K10J10 §5.6). Keep `remove.cmd` in
|
||
reverse order.
|
||
- Every file notes the CI Server version it was built and tested on.
|