Phase 6: PROCESS Service classes and objects (L10, P10, P11) #6

Merged
cliu merged 5 commits from docs/phase-6-classes into main 2026-09-30 17:58:36 +10:00
6 changed files with 391 additions and 29 deletions
Showing only changes of commit f99490378c - Show all commits

View file

@ -1,20 +1,38 @@
--- ---
name: class-object-engineering name: class-object-engineering
description: >- description: >-
CI Server R1.03 PROCESS Service classes, objects and trigger groups. Use when the user wants CI Server R1.03 PROCESS Service: trigger groups, classes and objects. Use when the user wants
to create a trigger group, write or compile a class (traditional language), create objects to create a trigger group, write, compile or change a class (Traditional PROCESS language or
from a class, or load classes and objects with quick load files. Sources IM 36K10G10-01EN Java), use signals, attributes, prompts, timers or template items in a class, create objects
(Tutorial) and IM 36K10J10-01EN; to be extended with IM 36K10L10, P10 and P11 in phase 6. from a class (with prompt answers), let an object create its own items, reset a class after a
change, read or change object attribute values, see which item a signal uses, load or remove
classes and objects with quick load files, or check PROCESS Service with opcdbg (database
check, object status, executor statistics). Sources IM 36K10L10-01EN, IM 36K10P10-01EN,
IM 36K10P11-01EN, IM 36K10G10-01EN (Tutorial).
--- ---
# Class and object engineering (R1.03) # Class and object engineering (R1.03)
Background: `knowledge/manuals/IM36K10G10-01EN.md` (chapter 5) and Background: `knowledge/manuals/IM36K10L10-01EN.md` (processes, `opc.sup`, `opcdbg`, data
`knowledge/manuals/IM36K10J10-01EN.md` (sections 2.4 and 5.3). Known issues for PROCESS store), `IM36K10P10-01EN.md` (Traditional language), `IM36K10P11-01EN.md` (Java),
Service: `knowledge/manuals/IM36K01A50-01EN.md`. `IM36K10G10-01EN.md` chapter 5 (Tutorial). Known issues for PROCESS Service:
`knowledge/manuals/IM36K01A50-01EN.md` (PROCESS 1.10).
Rules: run `system-check` first; creating classes and objects changes the running system. Rules:
- Run `system-check` first; follow the user's chosen way of working.
- Creating, changing or resetting classes and objects changes the running system; objects can
write to items (and through them to devices). Test on a test system or with the user's
approval.
- Deleting an object deletes the items it created (and their history links).
- `opcdbg`: read-only actions only, unless the user approves (task `opcdbg-checks.md`).
| Task | File | Status | | Task | File | Status |
|---|---|---| |---|---|---|
| Trigger group, class (compiled on load) and object via quick load | `tasks/load-class-object.md` | verified | | Trigger group, class (compiled on load) and object via quick load; compile errors; remove | `tasks/load-class-object.md` | verified |
| Traditional class with prompts/attributes, signals from the object's section, items created from a template, input check; change an attribute at runtime | `tasks/traditional-class.md` | verified |
| Java class: attach and create items, signal trigger, system log message | `tasks/java-class.md` | verified |
| Change a class that has objects: compile, reset with `opcdbg` | `tasks/change-reset-class.md` | verified (`dssqld` reset does not work, ISS-028) |
| `opcdbg` checks: database check, class/object details, object status, executor statistics, dumps | `tasks/opcdbg-checks.md` | verified |
| Language reference (statements, operators, built-ins, DSS routines; Java API) | P10 / P11 summaries | from docs, main parts verified |
| `opc.sup` settings (executors, priorities, statistics, PRINT) | L10 summary §2 | read-only check; changing not practiced |
| Containment, inheritance, timers, alarms, STEP state machines, DSS routines | P10 / P11 summaries | from docs |

View file

@ -0,0 +1,64 @@
---
task: Change a class that already has objects (compile and reset)
version: R1.03
source: IM 36K10L10-01EN §4 (Reset class); IM 36K10P11-01EN §2.3, §10.4; practiced with CI-Eng-Sample class CIENG_TANK 2026-09-30
status: verified
---
# Change a class that has objects
Objects keep running the **old** compiled code until their class is reset (verified). A reset
restarts every object of the class; by the manual, `ONCE` trigger groups fire again. Items the
objects created were kept, with their values (verified).
## 1. New source and compile
`dssqld -m` with the class name, `COMPILE` 1 and the new `METHOD`:
```
@FIELDS
NAME,COMPILE,METHOD
@CLASS_DF
"CIENG_TANK",1,@"
CLASS CIENG_TANK;
…
ENDCLASS CIENG_TANK;
"@
```
`Errors` 0 = compiled. On a compile error the old code stays; see `load-class-object.md` for
reading the error listing.
Export the class (`load-class-object.md` §2): after the compile `RESET_REQUIRED` = 1 and
`OBJECTS_RESET` = 0 of `OBJECTS` n. Verified: the object still printed the old text.
## 2. Reset the class
**Works** — `opcdbg`, Classes, action 8 *Reset class*: class name, node (0 = this node), file
for a reset report (empty = none; this prompt is not in the manual):
```
printf '2\n8\n<CLASS>\n0\n\n-1\n-1\n' | timeout 30 opcdbg 0
```
(Bash. Interactively, type the same answers.) Keep the time-out: `opcdbg` repeats its last
prompt forever when the input runs out.
Afterwards `RESET_REQUIRED` = 0 and the object used the new code at once (verified).
The Engineering Module also resets classes (class properties); not practiced.
**Does not work** (verified 2026-09-30, ISS-028):
- `dssqld -m` with `NAME,RESET` = `"<class>",1` (also with `RESET_NODE` = the host node name):
accepted with 0 errors but nothing happens.
- `dssqld -m` on `OBJECT_DF` with `RESET` 1: refused with `OPC-E-OBJ_RSTREQ, The class needs to
be reset first`.
- `CLASS_RESET_COMMAND` (system-wide reset used by the Engineering Module) was not tried.
## 3. Changing prompts or signals
Only a change in the method bodies was practiced. When the class **prolog** changes (new
signals, prompts, attributes, created items), check `EXTENDED_RESET_REQUIRED` in `CLASS_DF`
after compiling; what an extended reset does was not tested. The safe way is to recreate the
objects (remove, load) — note that removing an object deletes the items it created, with their
history links.

View file

@ -0,0 +1,87 @@
---
task: Write and load a Java PROCESS Service class
version: R1.03
source: IM 36K10P11-01EN §1–§5, §10; IM 36K10L10-01EN §1; practiced with CI-Eng-Sample class CIENG_JFLOW 2026-09-30
status: verified
---
# Java class
Worked example: class `CIENG_JFLOW`, object `CI-Eng-Sample.TANK1.JFLOW`
(`examples/CI-Eng-Sample/06_class_df.qli`, `07_object_df.qli`). Language reference:
`knowledge/manuals/IM36K10P11-01EN.md`. Loading and removing: `load-class-object.md`.
Choose Java when you need loops, arrays of signals, inheritance, libraries, or signals that may
stay unattached; otherwise the Traditional language is simpler.
## 1. Class record
Same as a Traditional class but `PF_LANGUAGE` = `"Java"` (cannot be changed later) and Java
source in `METHOD`. The Java class name must equal the `CLASS_DF` name exactly.
```
@FIELDS
NAME,DESCRIPTION,PF_LANGUAGE,TRIGGER_1,COMPILE,LISTING_WANTED,METHOD
@CLASS_DF
"CIENG_JFLOW","Inflow per minute (Java)","Java","",1,0,@"
import fasttools.processfast.*;
public class CIENG_JFLOW extends ProcessClass {
Signal sInflow = defineDoubleSignal(Signal.Trigger.VALUE);
Signal sPerMin = defineDoubleSignal();
public void create() {
attachItemToSignal(sInflow, getSectionsPath() + ".INFLOW");
createItemForSignal(sPerMin, "CI-Eng-Sample.TEMPLATES.TPL_REAL",
"DESCRIPTION", "Tank inflow (m3/min), class CIENG_JFLOW");
}
public void signalEvent(Signal signal) {
double mInflow = sInflow.getValueAsDouble();
sPerMin.setValue(mInflow / 60.0);
systemMessage("CIENG_JFLOW " + getTag() + ": inflow " + mInflow);
}
}
"@
```
`OPCMAN` starts the Java compiler `OPCJCOM` on demand; allow ~15 s, then export the class and
check `VALID_METHOD` = 1 (as in `load-class-object.md`).
## 2. Object
```
@FIELDS
NAME,DESCRIPTION,CLASS
@OBJECT_DF
"CI-Eng-Sample.TANK1.JFLOW","Tank 1 inflow per minute (Java)","CIENG_JFLOW"
```
Prompts: `Attribute aX = defineStringAttributeForPrompt("…");` → answer in `ATTR_VALUE_n`;
refuse bad answers with `cancel("text")` in `create()`.
## 3. Results on the test system
- Signals: `sInflow` → `CI-Eng-Sample.TANK1.INFLOW`; `sPerMin` → created item
**`CI-Eng-Sample.TANK1.JFLOW.sPerMin`** — automatic names keep the identifier's case, and
`itmval` needs that exact case (`…SPERMIN` is not found). Give a `"NAME"` if you want
upper-case item names.
- `INFLOW` 120 → `sPerMin` 2; 90 → 1.5.
- `systemMessage()` appears in `umhlog.log` from `OPCJEXE` as
`UMH-I-INFO, (<object>: <text>)`.
- The first object ran without resetting the new class, although the manual asks for a reset
(ISS-029). After changing a Java class that has objects, compile **and reset** it
(`change-reset-class.md`).
- Deleting the object deleted `…JFLOW.sPerMin` and section `…TANK1.JFLOW`; `INFLOW` stayed.
## 4. Pitfalls (manual)
- Definitions (`define…()`) only at class level, without `public`/`private`.
- `create()`/`destroy()` run in a different process than the trigger methods: only attributes
keep values between them; no timers or signal values in `create()`.
- Never sleep or loop long in a trigger method: it blocks all objects of the same priority.
- No anonymous listener classes (fail at object start with `NoClassDefFoundError`).
- Dataset access: get the session with `getDssSession()` and never close it (release notes).

View file

@ -1,16 +1,18 @@
--- ---
task: Create a trigger group, class and object with quick load files task: Create trigger groups, classes and objects with quick load files
version: R1.03 version: R1.03
source: IM 36K10G10-01EN §5.1–5.3; IM 36K10J10-01EN §5.6; practiced with CI-Eng-Sample 2026-09-30 source: IM 36K10G10-01EN §5.1–5.3; IM 36K10L10-01EN §3; IM 36K10J10-01EN §5.6; practiced with CI-Eng-Sample 2026-09-30 (full remove/reload with three classes and objects)
status: verified status: verified
--- ---
# Trigger group, class and object via quick load # Trigger groups, classes and objects via quick load
Load in this order (each with `dssqld -i <file>.qli -l <file>.qlo`, check `Errors` = 0); Load in this order, each with `dssqld -i <file>.qli -l <file>.qlo` and check `Errors` = 0:
remove in reverse (object, class, trigger group) with `NAME`-only files and `dssqld -r`. items the classes need (template items, attached items) → trigger groups → classes → objects.
Remove in reverse (objects, classes, trigger groups) with `NAME`-only files and `dssqld -r`.
Run `dssqld` from the folder that holds the files and use short relative names (ISS-020).
**1. Trigger group** (`TRIGGER_DF`, name max 16 characters): ## 1. Trigger group (`TRIGGER_DF`, name max 16 characters)
``` ```
@FIELDS @FIELDS
@ -20,17 +22,23 @@ NAME,DESCRIPTION,TRIGGER_INTERVAL
"CIENG_1S","CI-Eng-Sample one second trigger","1 seconds" "CIENG_1S","CI-Eng-Sample one second trigger","1 seconds"
``` ```
**2. Class** (`CLASS_DF`, name max 16 characters). The source goes in the `METHOD` memo field, Other fields: `ACTIVATION_TIME`, `DEACTIVATION_TIME`, `TRIGGER_ONCE` (1 = run once when an
between `@"` and `"@` on their own lines; `COMPILE` 1 compiles it on load; `TRIGGER_1` object is activated), `CORRECT_DAYLIGHT`. A class or object without a trigger group only runs
attaches a trigger group: on signal triggers, timers and `TRIGGER` statements.
## 2. Class (`CLASS_DF`, name max 16 characters; the language allows 15)
The source goes in the `METHOD` memo field between `@"` and `"@` on their own lines.
`COMPILE` 1 compiles it on load; `PF_LANGUAGE` is `Traditional` (default) or `Java` and cannot
be changed later; `TRIGGER_1`…`TRIGGER_8` attach trigger groups; `PRIORITY` 0–999 picks the
executor (installed: `OPCEXE1` 0–249 … `OPCEXE4` 750–999).
``` ```
@FIELDS @FIELDS
NAME,DESCRIPTION,PF_LANGUAGE,TRIGGER_1,COMPILE,LISTING_WANTED,METHOD NAME,DESCRIPTION,PF_LANGUAGE,TRIGGER_1,COMPILE,LISTING_WANTED,METHOD
@CLASS_DF @CLASS_DF
"CIENG_INCR","Count CI-Eng-Sample.DEMO.COUNTER 1..100","Traditional","CIENG_1S",1,1,\ "CIENG_INCR","Count CI-Eng-Sample.DEMO.COUNTER 1..100","Traditional","CIENG_1S",1,1,@"
@"
CLASS CIENG_INCR; CLASS CIENG_INCR;
PROLOG PROLOG
SIGNAL INPUT := "CI-Eng-Sample.DEMO.COUNTER"; SIGNAL INPUT := "CI-Eng-Sample.DEMO.COUNTER";
@ -46,23 +54,70 @@ ENDCLASS CIENG_INCR;
"@ "@
``` ```
The item a `SIGNAL` names must exist first (or be created by the class with `FROM … MAKE`). Several classes (also mixed languages) can share one file and one `@FIELDS` line; leave unused
fields as `""`. See `examples/CI-Eng-Sample/06_class_df.qli`.
**3. Object** (`OBJECT_DF`): full name in its section, and the class: **Check the compile**: export the class with a filter file and read the flags after the source:
`VALID_METHOD` = 1 means compiled; `ATTR_PROMPT_n` shows the class's prompts;
`RESET_REQUIRED`, `OBJECTS`, `OBJECTS_RESET` matter when changing a class (`change-reset-class.md`).
```
@SELECT
'NAME = "CIENG_TANK"'
@CLASS_DF
```
`dssqld -e cls.qli filter.qli -l cls.lst`
**Compile errors** (verified, Traditional): with `COMPILE` 1 on insert, a class that does not
compile is **not inserted at all** — the `.qlo` only says `DSI-E-OPCCOMERR, Compilation failed
with n error(s)`. To see the errors, insert the class with `COMPILE` 0, then update it with
`dssqld -m` and `NAME,COMPILE,LISTING_WANTED` = `"<class>",1,1`: the update fails the same way
but the class's `LISTING` field (export as above) now shows the numbered source with
`**** ERROR: …` after the faulty line. Fix the `METHOD` field and compile again.
Class language: `traditional-class.md` and `java-class.md`.
## 3. Object (`OBJECT_DF`)
Full name (its section must exist), class, and `ATTR_VALUE_n` = answer to the class's n-th
prompt:
``` ```
@FIELDS @FIELDS
NAME,DESCRIPTION,CLASS NAME,DESCRIPTION,CLASS,ATTR_VALUE_1
@OBJECT_DF @OBJECT_DF
"CI-Eng-Sample.DEMO.INCR1","Counts DEMO.COUNTER every second","CIENG_INCR" "CI-Eng-Sample.DEMO.INCR1","Counts DEMO.COUNTER every second","CIENG_INCR",""
"CI-Eng-Sample.TANK1.CALC","Tank 1 volume (capacity 200 m3)","CIENG_TANK","200"
``` ```
Other fields: `TRIGGER_1`…`8` (object's own trigger groups), `PRIORITY`, `CONTROL_ITEM`
(item whose status blocks/stops the object and whose value is `STEP`), `ITEMS_MAY_EXIST`
(allow linking to items that already exist instead of creating them), `LEAVE_ITEMS` (keep
created items when the object is deleted), `DEBUG`.
The item a plain `SIGNAL` names must exist. If the class refuses the object (`ERROR` in the
prolog, Java `cancel()`, missing item) the row counts as an error and the `.qlo` shows the
reason (e.g. `OPC-E-INFO, <text>`); nothing is left behind.
## Check ## Check
Run `itmval -g -n CI-Eng-Sample.DEMO.COUNTER` a few seconds apart: the value rises by one per - Items the object writes: `itmval -g -n <item>` a few seconds apart (sample: `DEMO.COUNTER`
second. To inspect a class, export it with a filter file (`@SELECT`, then rises by one per second).
`'NAME = "<class>"'`, then `@CLASS_DF`). - Which item each signal got: export `OBJECT_SIGNAL_DF` with filter
`'OBJECT = "<object>"'`; attribute values: `OBJECT_ATTR_VAL` (same filter).
- Run state: `opcdbg` (task `opcdbg-checks.md`).
Example files: `examples/CI-Eng-Sample/05_trigger_df.qli`, `06_class_df.qli`, `07_object_df.qli`, ## Remove
`86_` to `88_remove_*.qli`.
```
@FIELDS
NAME
@OBJECT_DF
"CI-Eng-Sample.TANK1.CALC"
```
Then `CLASS_DF`, then `TRIGGER_DF`. Deleting an object also deletes the items and sections it
created (verified); items it only attached stay.
Example files: `examples/CI-Eng-Sample/05_trigger_df.qli`, `06_class_df.qli`,
`07_object_df.qli`, `86_` to `88_remove_*.qli`.

View file

@ -0,0 +1,47 @@
---
task: Check PROCESS Service with opcdbg (database check, classes, objects, performance)
version: R1.03
source: IM 36K10L10-01EN §4; practiced on the test system with CI-Eng-Sample 2026-09-30
status: verified (read-only actions and Reset class)
---
# PROCESS Service checks with `opcdbg`
`opcdbg` is interactive. Answers can be piped in, one per line, in Bash; always add a time-out
and end with `-1` lines, because on end of input it repeats the last prompt forever. `opcdbg 0
<menu>` starts in a menu on node 0 (this node). Times are printed in **GMT**.
Only the actions below are read-only. Do not use insert/modify/delete, *Database repair*,
*Reset* (utilities) or *Modify attribute value* without the user's approval.
| Check | Command (Bash) | Output |
|---|---|---|
| Database consistency | `printf '5\n-1\n-1\n' \| timeout 60 opcdbg 0 5` | Counts per store (trigger groups, classes, sources, code, objects, signals, attributes), then checks; warnings such as `No compiled code found for class (cls_id = …)` |
| Class details | `printf '2\n5\nN\n<CLASS>\n-1\n-1\n' \| timeout 20 opcdbg 0` | Language, priority, modified/compiled times, cls_id, objects derived / reset, hidden |
| Class by id (from a warning) | `printf '2\n5\nI\n<sec>\n<seq>\n-1\n-1\n' \| timeout 20 opcdbg 0` | Same; `Compiled at : 0` = never compiled |
| Object details | `printf '3\n4\nN\n<object>\n-1\n-1\n' \| timeout 20 opcdbg 0` | Priority, control item id, times, cls_id/obj_id |
| Object status | `printf '3\n18\n<object>\n-1\n-1\n' \| timeout 20 opcdbg 0` | Total runs, executor (`Running in : 1, OPCEXE1`) |
| Object signals / attributes | `3` then `14` / `12`, object name | Signal → item id; attribute → type (LOCAL/GLOBAL) |
| Executor statistics | `printf '0\nOPCEXE1\n1\n-1\n-1\n' \| timeout 20 opcdbg 0 4` | Active classes/objects/trigger groups/items, number of executions; times stay 0 unless `STATISTICS = YES` in `opc.sup` |
Executor names on R1.03: `OPCEXE1`…`OPCEXE4` (Traditional, priority ranges 0–249 … 750–999)
and `OPCJEXE` (Java). The prompt's default `OPCEXE` does not exist.
Easier for names: the datasets `OBJECT_SIGNAL_DF` (object:signal → item name) and
`OBJECT_ATTR_VAL` / `CLASS_ATTR_VAL` (attribute values) via `dssqld -e` with a filter — see
`traditional-class.md` §3.
## Dumps
*Utilities* 3 / 4 (`printf '3\nclasses.qli\n4\nobjects.qli\n-1\n-1\n' | timeout 30 opcdbg 0 5`)
write all class sources / objects to files in the current folder. The format is an old one
(`NAME,"description",priority` then source lines starting with `- `) and **`dssqld -i` rejects
it** (`DSS-E-UNK_DS_NM`, ISS-027). To move classes and objects to another system, export
`CLASS_DF` and `OBJECT_DF` with `dssqld -e` instead (task `load-class-object.md`; migration in
`knowledge/manuals/IM36K10J10-01EN.md` §5.6).
## Observed on the test system
The database check reported one warning for a class `SINUS` (not from this repo) that was
never compiled; everything else was consistent. A class like that is harmless but can be
compiled or removed by its owner.

View file

@ -0,0 +1,91 @@
---
task: Write a Traditional class with prompts, signals built from the object name, and items created from a template
version: R1.03
source: IM 36K10P10-01EN §1–§5, §8, §10, §11.2.8; IM 36K10L10-01EN; practiced with CI-Eng-Sample class CIENG_TANK 2026-09-30
status: verified
---
# Traditional class: prompts, signals, created items
Worked example: class `CIENG_TANK` and object `CI-Eng-Sample.TANK1.CALC`
(`examples/CI-Eng-Sample/06_class_df.qli`, `07_object_df.qli`). Loading and removing: task
`load-class-object.md`. Language reference: `knowledge/manuals/IM36K10P10-01EN.md`.
## 1. Design
One class = one kind of equipment; one object per piece of equipment. Put the variable parts
(which items, which settings) in the **class prolog** so they are fixed when each object is
created:
| Need | Use |
|---|---|
| Setting per object, asked at creation | `ATTRIBUTE x OBJECT FLOAT;` + `PROMPT x QUERY "…";` → `ATTR_VALUE_n` in `OBJECT_DF` |
| Setting shared by all objects | `ATTRIBUTE x CLASS …;` |
| Existing item next to the object | `SIGNAL s := SECTION || ".<TAG>" TRIGGER VALUE;` |
| New item per object | `SIGNAL s := FROM "<template item>" MAKE "FIELD" EQUAL value …;` |
| Refuse bad input | `IF … THEN ERROR "text"; ENDIF;` in the class prolog |
| Periodic run | trigger group on the class (`TRIGGER_1`) or object |
| Run on a change | `TRIGGER VALUE` (or STATUS, UPDATE …) on the signal |
Template items: keep them in their own section (sample: `CI-Eng-Sample.TEMPLATES.TPL_REAL`)
and load them before the class.
## 2. Example
```
CLASS CIENG_TANK;
PROLOG
ATTRIBUTE CAPACITY OBJECT FLOAT;
PROMPT CAPACITY QUERY "Tank capacity (m3)";
SIGNAL LEVEL := SECTION || ".LEVEL" TRIGGER VALUE;
SIGNAL VOLUME := FROM "CI-Eng-Sample.TEMPLATES.TPL_REAL" MAKE
"DESCRIPTION" EQUAL "Tank volume (m3), class CIENG_TANK";
IF CAPACITY <= 0 THEN
ERROR "CIENG_TANK: capacity must be > 0";
ENDIF;
BODY
METHOD CALC;
BODY
VOLUME := LEVEL * CAPACITY / 100.0;
IF LEVEL >=: 80.0 THEN
PRINT "CIENG_TANK ", SECTION, ": level reached 80 pct, volume ", VOLUME;
ENDIF;
ENDMETHOD CALC;
ENDCLASS CIENG_TANK;
```
Object `CI-Eng-Sample.TANK1.CALC` with `ATTR_VALUE_1` = `"200"`:
- `SECTION` = `CI-Eng-Sample.TANK1`, so `LEVEL` → `CI-Eng-Sample.TANK1.LEVEL`.
- `VOLUME` has no `"NAME"`, so the item is `CI-Eng-Sample.TANK1.CALC.VOLUME`; the section
`TANK1.CALC` is created too. Both are deleted with the object.
- `LEVEL` 50 → `VOLUME` 100; 82 → 164.
- `>=:` prints once when the level **crosses** 80 (message in `umhlog.log`, sender `OPCEXE1`).
Rules learned on the test system:
- Do not put `%` in `PRINT` text: the message is cut at that point (ISS-025).
- An object at root level has an empty `SECTION`; build names with an `IF` then (release notes).
- Item names in `SIGNAL` strings must match exactly; an item that does not exist makes the
object creation fail.
## 3. Check and operate
- Signals → items: export `OBJECT_SIGNAL_DF` with filter `'OBJECT = "<object>"'`
(sample: `…CALC:LEVEL` → `CI-Eng-Sample.TANK1.LEVEL`, `…CALC:VOLUME` → `…CALC.VOLUME`).
- Attribute values: export `OBJECT_ATTR_VAL` (same filter); record name
`<object>:<attribute>`, field `ATTR_VALUE`.
- **Change an attribute of a running object** (verified): `dssqld -m` on `OBJECT_ATTR_VAL`:
```
@FIELDS
NAME,ATTR_VALUE
@OBJECT_ATTR_VAL
"CI-Eng-Sample.TANK1.CALC:CAPACITY","400"
```
The new value is used from the next run (level 50 → volume 200). Do not change attributes
that were used to build signal names: the signal keeps its old item (P10 §8.3).
- Class-wide attributes: `CLASS_ATTR_VAL` (`<class>:<attribute>`), not practiced.
- Refused object: loading `ATTR_VALUE_1` = `"0"` gave `Errors 1` with
`OPC-E-INFO, CIENG_TANK: capacity must be > 0` in the `.qlo`; nothing was created.
Changing the class source later: task `change-reset-class.md`.