Add operator layout CIENG_Operator and navigation display CIENG_Menu to CI-Eng-Sample

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Clio Liu 2026-10-01 15:47:38 +10:00
parent a3b1487c33
commit e62d83b024
7 changed files with 536 additions and 198 deletions

View file

@ -1,173 +1,187 @@
# 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. |
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`) |
| `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 (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`, alarm overview, trend of `TANK1.LEVEL` |
| `displays/deploy_displays.cmd`, `remove_displays.cmd` | Copy the display into the operator interface and export its HTML version / delete both |
| `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`.
## Display (optional)
After `load.cmd`, run `displays\deploy_displays.cmd` (no admin rights) and open
`http://localhost:8080/mobileoperator/?d=CIENG_Tank` (CI Server login), or open `CIENG_Tank` in
CI View / the Edit Module. `displays emove_displays.cmd` deletes it again. 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`.
## Planned additions
| Phase | Adds |
|---|---|
| 10 | A symbol (the display `CIENG_Tank` exists) |
| 11 | A report |
## 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.
# 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. |
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`) |
| `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 (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`, alarm overview, trend of `TANK1.LEVEL`, button *Operator layout* |
| `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/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` and `CIENG_Menu` 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).
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`.
## Planned additions
| Phase | Adds |
|---|---|
| 10 | A symbol and a faceplate popup (displays and the operator layout exist) |
| 11 | A report |
## 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.

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8" ?>
<visualization protocolVersion="1.3.0.0">
<globalSection/>
<coreObjectDefinition type="displayDefinition">
<version type="version" value="1.3.0.0"/>
<html>true</html>
<width>200</width>
<height>700</height>
<referenceCheck>2</referenceCheck>
<defaultBgColor type="colorSet" r="255" g="255" b="255"/>
<defaultFgColor type="colorSet" r="0" g="0" b="0"/>
<defaultFont type="font" name="Tahoma" size="11" underline="false" strikethrough="false"/>
<defaultStroke type="stroke" width="1.0"/>
<grid type="grid" gridVisible="true" snappingActive="true" verticalSnapInterval="8" horizontalSnapInterval="8" onTop="false">
<color type="colorSet" r="0" g="0" b="0"/>
</grid>
<revisionHistory type="revisionHistory">
<revision type="revision" who="ADMIN" when="2026.10.01 15:05:12.398 AEST" what="Created" where="PXiSEDev"/>
</revisionHistory>
<blinkDelay>500</blinkDelay>
<blinkEnabled>true</blinkEnabled>
<mousePassThrough>false</mousePassThrough>
<visibilityGroup type="componentData">
<htmlId>2</htmlId>
<name>Overview</name>
<description>Always shown</description>
<minimumZoomEnabled>true</minimumZoomEnabled>
<minimumZoomFactor>10.0</minimumZoomFactor>
</visibilityGroup>
<visibilityGroup type="componentData">
<htmlId>3</htmlId>
<name>Rough</name>
<description>Shown when viewing viewing a large area</description>
<minimumZoomEnabled>true</minimumZoomEnabled>
<minimumZoomFactor>25.0</minimumZoomFactor>
</visibilityGroup>
<visibilityGroup type="componentData">
<htmlId>4</htmlId>
<name>Standard</name>
<description>Shown when using the default view setting</description>
<minimumZoomEnabled>true</minimumZoomEnabled>
<minimumZoomFactor>100.0</minimumZoomFactor>
</visibilityGroup>
<visibilityGroup type="componentData">
<htmlId>5</htmlId>
<name>Detail</name>
<description>Shown only when viewing a small area</description>
<minimumZoomEnabled>true</minimumZoomEnabled>
<minimumZoomFactor>400.0</minimumZoomFactor>
</visibilityGroup>
<visibilityGroup type="componentData">
<htmlId>6</htmlId>
<name>Intricacies</name>
<description>Shown only when viewing a very small area</description>
<minimumZoomEnabled>true</minimumZoomEnabled>
<minimumZoomFactor>1000.0</minimumZoomFactor>
</visibilityGroup>
<visualizationLayer type="componentData">
<htmlId>1</htmlId>
<name>Layer1</name>
</visualizationLayer>
<componentCountHint>3</componentCountHint>
<button type="componentData" x="100.0" y="44.0" top="20.0" bottom="20.0" left="84.0" right="84.0" rotation="0.0" shear="0.0">
<htmlId>7</htmlId>
<label>Tank 1</label>
<data type="data">
<action type="actionActivateDisplay">
<event type="event" event="On action"/>
<display>CIENG_Tank</display>
</action>
</data>
</button>
<button type="componentData" x="100.0" y="108.0" top="20.0" bottom="20.0" left="84.0" right="84.0" rotation="0.0" shear="0.0">
<htmlId>8</htmlId>
<label>Current Alarm</label>
<data type="data">
<action type="actionActivateDisplay">
<event type="event" event="On action"/>
<display>AOG_AlarmCurrent</display>
</action>
</data>
</button>
<button type="componentData" x="100.0" y="180.0" top="20.0" bottom="20.0" left="84.0" right="84.0" rotation="0.0" shear="0.0">
<htmlId>9</htmlId>
<label>Tank 1 popup</label>
<data type="data">
<action type="actionActivateDisplay">
<event type="event" event="On action"/>
<type type="displayActivationType" value="ActivateSpecific"/>
<display>CIENG_Tank</display>
<layout>popup</layout>
<layoutFrame>default</layoutFrame>
</action>
</data>
</button>
</coreObjectDefinition>
</visualization>

View file

@ -1,12 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<visualization protocolVersion="1.3.0.0">
<globalSection/>
<globalSection>
<intRef id="17">
<sharedObject type="data">
<action type="actionInternalServiced"/>
</sharedObject>
</intRef>
<intRef id="20">
<sharedObject type="protection" active="false" password="">
<userProtection type="userProtection" user=""/>
</sharedObject>
</intRef>
</globalSection>
<coreObjectDefinition type="displayDefinition">
<version type="version" value="1.3.0.0"/>
<html>true</html>
<width>1984</width>
<height>760</height>
<referenceCheck>4</referenceCheck>
<referenceCheck>8</referenceCheck>
<defaultBgColor type="colorSet" r="255" g="255" b="255"/>
<defaultFgColor type="colorSet" r="0" g="0" b="0"/>
<defaultFont type="font" name="Tahoma" size="11" underline="false" strikethrough="false"/>
@ -59,7 +70,7 @@
<htmlId>1</htmlId>
<name>Layer1</name>
</visualizationLayer>
<componentCountHint>4</componentCountHint>
<componentCountHint>5</componentCountHint>
<text type="componentData">
<htmlId>7</htmlId>
<value>CI-Eng-Sample Tank 1</value>
@ -92,9 +103,7 @@
</numberField>
<alarmOverview type="componentData" x="1020.0" y="296.0" top="72.0" bottom="72.0" left="964.0" right="964.0" rotation="0.0" shear="0.0">
<htmlId>9</htmlId>
<data type="data">
<action type="actionInternalServiced"/>
</data>
<data ref="17"/>
</alarmOverview>
<chart type="componentData">
<htmlId>10</htmlId>
@ -107,9 +116,7 @@
<toolbarPosition type="borderLayoutPosition" value="PositionNorth"/>
<shiftOffset>0</shiftOffset>
<shiftLength>0</shiftLength>
<protection type="protection" active="false" password="">
<userProtection type="userProtection" user=""/>
</protection>
<protection ref="20"/>
<enterpriseGroupLocation type="nodeName" node="$display$"/>
<chartPanel>
<name/>
@ -198,9 +205,7 @@
<useSampleInterval>true</useSampleInterval>
<sampleInterval>0</sampleInterval>
<optimised>true</optimised>
<chartProtection type="protection" active="false" password="">
<userProtection type="userProtection" user=""/>
</chartProtection>
<chartProtection ref="20"/>
<pen type="pen">
<type>256</type>
<color type="colorSet" r="65" g="105" b="225"/>
@ -468,20 +473,20 @@
<right>344.0</right>
<rotation>0.0</rotation>
<shear>0.0</shear>
<data ref="17"/>
</chart>
<button type="componentData" x="404.0" y="68.0" top="20.0" bottom="20.0" left="84.0" right="84.0" rotation="0.0" shear="0.0">
<htmlId>11</htmlId>
<label>Operator layout</label>
<data type="data">
<action type="actionInternalServiced"/>
<action type="actionConnectTo">
<property type="property" name="chart.attribute[00]"/>
<filter type="filter">
<value>0.0</value>
</filter>
<connection type="connection">
<itemName>CI-Eng-Sample.TANK1.LEVEL</itemName>
<itemId>1.1.268.0.0.1.1.7</itemId>
<itemAttribute>ItemValue</itemAttribute>
</connection>
<action type="actionActivateDisplay">
<event type="event" event="On action"/>
<type type="displayActivationType" value="ActivateSpecific"/>
<display>CIENG_Tank</display>
<layout>CIENG_Operator</layout>
<layoutFrame>default</layoutFrame>
</action>
</data>
</chart>
</button>
</coreObjectDefinition>
</visualization>

View file

@ -7,4 +7,6 @@ cd /d "%~dp0"
set WAP=%TLS_WAP_PATH%
if not defined TLS_WAP_PATH set WAP=C:\Users\Public\Yokogawa\tls\wap
copy /y CIENG_Tank.xml "%WAP%\cfg\operatorInterfaces\DEPLOY\displays\"
copy /y CIENG_Menu.xml "%WAP%\cfg\operatorInterfaces\DEPLOY\displays\"
call jwuiutl.cmd -e HTML -i -f DEPLOY CIENG_Tank
call jwuiutl.cmd -e HTML -i -f DEPLOY CIENG_Menu

View file

@ -1,7 +1,9 @@
@echo off
rem Remove the CI-Eng-Sample displays (operator interface and HTML version). CI Server R1.03.00.
rem Close CIENG_Tank in the Edit Module first.
rem Close CIENG_Tank and CIENG_Menu in the Edit Module first.
set WAP=%TLS_WAP_PATH%
if not defined TLS_WAP_PATH set WAP=C:\Users\Public\Yokogawa\tls\wap
del /q "%WAP%\cfg\operatorInterfaces\DEPLOY\displays\CIENG_Tank.xml"
del /q "%WAP%\mobileoperator\operatorInterfaces\DEPLOY\displays\CIENG_Tank.html"
del /q "%WAP%\cfg\operatorInterfaces\DEPLOY\displays\CIENG_Menu.xml"
del /q "%WAP%\mobileoperator\operatorInterfaces\DEPLOY\displays\CIENG_Menu.html"

View file

@ -0,0 +1,184 @@
<!-- CIENG-SAMPLE-BEGIN: CI-Eng-Sample operator layout (CI Server R1.03.00).
Added and removed by examples\CI-Eng-Sample\layouts\layout.ps1; do not edit inside Layouts.xml.
Window: menu, toolbar, navigation frame "nav" (left), main frame "default" (right),
alarm banner frame (bottom), status line. The main frame is called "default" because the
AOG displays (alarm lists, faceplates) open their targets in a frame named "default". -->
<layout
name="CIENG_Operator"
title="CI-Eng-Sample - $default"
icon="poi.png"
drawBorder="true"
sizingAllowed="true"
minimizingAllowed="true"
closingAllowed="true"
alwaysOnTop="false"
initialPosition="centreScreen"
initialPositionX="0"
initialPositionY="0"
initialSize="pixels"
initialSizeWidth="1600"
initialSizeHeight="900"
maximumOpen="1">
<menu>
<menu-item
name="cieng.menu.file"
label="File"
mnemonic="f">
<menu-item
name="cieng.menu.file.print"
label="Print..."
mnemonic="p"
tooltip="Print $"
command="print frameWithSetup"/>
<menu-separator/>
<menu-item
name="cieng.menu.file.exit"
label="Exit"
mnemonic="x"
tooltip="Close this window"
command="exit"/>
</menu-item>
<menu-item
name="cieng.menu.view"
label="View"
mnemonic="v">
<menu-item
name="cieng.menu.view.reload"
label="Reload"
mnemonic="r"
accelerator="F5"
tooltip="Reload $"
command="reload default"/>
<menu-item
name="cieng.menu.view.reload.all"
label="Reload All"
mnemonic="a"
tooltip="Reload all displays"
command="reload all"/>
<menu-separator/>
<menu-item
name="cieng.menu.view.fullscreen"
label="Full screen"
mnemonic="u"
accelerator="F11"
tooltip="Toggle full screen"
command="toggleSize fullScreen"/>
</menu-item>
<menu-item
name="cieng.menu.displays"
label="Displays"
mnemonic="d">
<menu-item
name="cieng.menu.displays.tank"
label="Tank 1"
mnemonic="t"
tooltip="Tank 1 overview"
command="navigate to default CIENG_Tank fitDisplay"/>
<menu-separator/>
<menu-item
name="cieng.menu.displays.alarmCurrent"
label="Current alarms"
mnemonic="c"
tooltip="Current alarm list"
command="navigate to default AOG_AlarmCurrent"/>
<menu-item
name="cieng.menu.displays.alarmHistory"
label="Alarm history"
mnemonic="h"
tooltip="Historical alarm list"
command="navigate to default AOG_AlarmHistory"/>
<menu-item
name="cieng.menu.displays.trend"
label="Trend"
mnemonic="r"
tooltip="Trend display"
command="navigate to default AOG_TrendOpen"/>
</menu-item>
</menu>
<toolbar
name="cieng.toolbar"
label="CI-Eng-Sample"
floatable="false"
position="top">
<toolbar-item
name="cieng.toolbar.back"
icon="ciNavLeft32.png"
label=""
tooltip="Back to $"
command="navigate back default"/>
<toolbar-item
name="cieng.toolbar.forward"
icon="ciNavRight32.png"
label=""
tooltip="Forward to $"
command="navigate forward default"/>
<toolbar-item
name="cieng.toolbar.home"
icon="ciOverview32.png"
label=""
tooltip="Home to $"
command="navigate home default"/>
<toolbar-item
name="cieng.toolbar.history"
tooltip="History"
label="History"
type="editableCombo"
command="navigate history default"/>
<toolbar-separator/>
<toolbar-item
name="cieng.toolbar.alarmCurrent"
icon="ciProAlar32.png"
label=""
tooltip="Current alarm list"
command="navigate to default AOG_AlarmCurrent"/>
<toolbar-item
name="cieng.toolbar.zoomFit"
icon="ciZoomFull32.png"
label=""
tooltip="Fit display"
command="zoom 0 default"/>
</toolbar>
<frame-set
rows="1*,fitDisplay"
borderStyle="none">
<frame-set
cols="fitDisplay,1*"
borderStyle="none">
<frame
name="nav"
stackSize="1"
zoomingAllowed="false"
scrollingAllowed="false"
initialDisplay="CIENG_Menu"
borderStyle="none"/>
<frame
name="default"
stackSize="10"
zoomingAllowed="true"
scrollingAllowed="true"
initialDisplay="CIENG_Tank"
borderStyle="line"
zoom="fitDisplay"/>
</frame-set>
<frame
name="alarmBannerFrame"
zoomingAllowed="false"
initialDisplay="AOG_AlarmBanner"
borderStyle="none"
zoom="stretchDisplay"/>
</frame-set>
<status-line>
<status-line-item
type="statusText"/>
<status-line-separator/>
<status-line-item
type="displayName"/>
<status-line-separator/>
<status-line-item
type="userName"/>
<status-line-separator/>
<status-line-item
type="dateTime"/>
</status-line>
</layout>
<!-- CIENG-SAMPLE-END -->

View file

@ -0,0 +1,34 @@
# Add or remove the CI-Eng-Sample layout block in Layouts.xml (CI Server R1.03.00).
# Usage: powershell -ExecutionPolicy Bypass -File layout.ps1 [-Remove]
# The block between <!-- CIENG-SAMPLE-BEGIN and <!-- CIENG-SAMPLE-END --> is inserted after
# the last layout (before the translations; an existing block is replaced). The first run
# saves Layouts-before-CIENG.xml next to Layouts.xml.
# No admin rights needed. Reopen the Operator Interface (or View > Reload All) afterwards.
param([switch]$Remove)
$wap = $env:TLS_WAP_PATH
if (-not $wap) { $wap = 'C:\Users\Public\Yokogawa\tls\wap' }
$file = Join-Path $wap 'cfg\operatorInterfaces\DEPLOY\layouts\Layouts.xml'
$backup = Join-Path $wap 'cfg\operatorInterfaces\DEPLOY\layouts\Layouts-before-CIENG.xml'
$block = Join-Path $PSScriptRoot 'CIENG_Operator.xml'
if (-not (Test-Path $file)) { throw "Not found: $file" }
if (-not (Test-Path $backup)) { Copy-Item -LiteralPath $file -Destination $backup }
$text = [IO.File]::ReadAllText($file)
$pattern = '(?s)[ \t]*<!-- CIENG-SAMPLE-BEGIN.*?<!-- CIENG-SAMPLE-END -->\r?\n?'
$text = [regex]::Replace($text, $pattern, '')
if (-not $Remove) {
$add = [IO.File]::ReadAllText($block) -replace '(?<!\r)\n', "`r`n"
# Insert after the last layout: before the first <translations, else before </layouts>.
$m = [regex]::Match($text, '(?m)^[ \t]*<translations\b')
if ($m.Success) { $end = $m.Index } else { $end = $text.LastIndexOf('</layouts>') }
if ($end -lt 0) { throw 'No </layouts> in Layouts.xml' }
$text = $text.Substring(0, $end) + $add + $text.Substring($end)
}
# Check that the result is still well-formed XML before writing it.
[xml]$null = $text
[IO.File]::WriteAllText($file, $text, (New-Object Text.UTF8Encoding($false)))
if ($Remove) { 'CIENG_Operator layout removed' } else { 'CIENG_Operator layout added' }