ci-server-engineer/.claude/skills/class-object-engineering/tasks/java-class.md
cliu 516729ff24 R1.05 findings from a user project (tested only)
Tested on the R1.05 test server 2026-10-09..12 with a user project (own
objects only); not yet on CI-Eng-Sample (ISS-188). Only results that
were tested are included; inferences, code-only and manual-only points
and the untested sample layout change were left out.
- layouts: 'logout' is a secondary logout, always disabled on the R1.05
  web client; 'exit' closes the window and ends the session (ISS-182).
- create-display: R1.05 web client table (String items in text vs
  textField, fixed text alignment, start-hidden limits ISS-183, two
  visibility conditions, On entry display actions, rectangle blinking,
  verified connections, first load).
- java-class 4a: signal arrays at scale, write on change, attribute
  arrays, offline javac/javap check; change-reset-class: new signals need
  the object recreated, attributes lost (ISS-185); opcdbg reset in cmd,
  TRIGGER_1 via dssqld -m.
- string-items (ISS-187), users-groups, modbus-station (400 registers,
  comm loss), export-dataset (NAME range filter), item-alarming.
- new task system-management/package-project.md; tools/ModbusSim.java
  (tested on R1.05: FC03/04/06/16, exceptions, modes); AMSI (ISS-186).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:57:08 +11:00

7.4 KiB
Raw Permalink Blame History


task: Write and load a Java PROCESS Service class version: R1.05 (first written for R1.03) source: IM 36K10P11-01EN 4th ed. (Feb. 2026) §1–§5, §10; IM 36K10L10-01EN §1; practiced with CI-Eng-Sample class CIENG_JFLOW 2026-09-30 status: verified on R1.03 (2026-09-30); re-tested on R1.05 2026-10-08 (CIENG_JFLOW compiled on load, sPerMin 2 for inflow 120, removed). Public API of fasttools.processfast compared with javap on 2026-10-09: identical to R1.03. systemMessage() output and the reset after a change were not re-tested on R1.05

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. Java on R1.05 (checked 2026-10-09)

  • Runtime: the JDK in %TLS_ROOT_PATH%\tls\jre is Eclipse Temurin 11.0.28 (R1.03: 11.0.18). Same Java 11 language level; write classes for Java 11.
  • API: the public classes and methods of fasttools.processfast (in tls\exe\jopcsrv.jar) are the same as on R1.03 (javap -public; the jar itself was rebuilt).
  • createSection(path, description, blocked, opcVisible) takes four arguments on both versions. The manual's examples pass a fifth (an empty process-area string) and would not compile as printed; R1.05 §10.2 now describes it correctly (ISS-154; not compile-tested).
  • The API has methods that P11 does not describe (ISS-159): getHistory(...) (item history as ValueSample[], several overloads), defineIntegerAttributeForPrompt / defineDoubleAttributeForPrompt (P11 only names the String version), sendAndReceiveMessage(...) (BUS request/reply) and getDssSession() (release notes). Their behaviour is not documented here; test before relying on them.
  • Extra jars: the Java PROCESS processes (OPCJCOM, OPCJACT, OPCJEXE) put tls\exe\* and, on R1.05, also tls\exe\lib\* (bundled third-party jars) on the class path. Your own library jar still goes in tls\exe (P11 §9).
  • OPC UA methods (R1.04+): method objects are Java (or Traditional) objects with the attribute MTD_KEY and signals MTD_TRG, MTD_RET, MTD_IN_ARG_n, MTD_OUT_ARG_n. The installed sample classes are the starting point: UAC_MTD_JBASE (CI Server as OPC UA client; prompts for the method key <station>:<method> and for the input and output argument lists written as /type/description, type f, i, s or b, at most 10 each) and UAS_MTD_JBASE (method offered by the CI Server OPC UA server; your logic runs when MTD_TRG becomes 1 and must set the outputs, then MTD_RET if needed, then MTD_TRG 0 = good or 2 = bad). Use one class with its own name per method object. Procedure: skill data-access, opcua-client.md §7, and the L45 summary (§10.6, §11.4). Not practiced.

4a. Large classes on R1.05 (verified 2026-10-12, user project on the R1.05 test server, 2026-10-09 to 10-12)

A Java class with about 1,600 signals ran on R1.05 every second without problems:

  • Signal arrays: Signal[] t = defineDoubleSignalArray(400); (also defineIntegerSignalArray, defineStringSignalArray), attached in a loop in create(): for (int k = 0; k < 400; k++) attachItemToSignal(t[k], "<Section>.TEMP." + name(k));. Signals without a trigger, plus TRIGGER_1 = a trigger group and triggerGroupEvent(TriggerGroup tg), give one run per second for all inputs.
  • Write only on change: the class compared with getValueAsDouble() / getValueAsString() before setValue() (and also wrote when getStatusNumber() == Signal.Status.NOT_INIT).
  • String signals: setValue(String) on a String item works.
  • Attribute arrays (defineDoubleAttributeArray(Attribute.Scope.OBJECT, n)) keep their values over a class reset; Java fields do not (initialise them in the first trigger run, not in create()). Attributes are lost when the object is deleted (verified: after remove and insert the new object found them empty and logged that it rebuilt the data from items; see change-reset-class.md §3).
  • Compile check before loading (no CI Server change): "%TLS_ROOT_PATH%\tls\jre\bin\javac.exe" -Xlint:all -cp "%TLS_ROOT_PATH%\tls\exe\jopcsrv.jar" -d cls <Class>.java. The exact API: javap -cp %TLS_ROOT_PATH%\tls\exe\jopcsrv.jar fasttools.processfast.ProcessClass (also Signal, Attribute$Scope, Signal$Trigger, Signal$Status constants, e.g. NOT_INIT 13).
  • Keep quick load lines short: put long generated arrays (tables in the class source) on several lines; the class loaded with lines up to 233 characters.

5. 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).