ci-server-engineer/.claude/skills/class-object-engineering/tasks/java-class.md
cliu f99490378c Extend class-object-engineering skill: language, Java, reset, opcdbg tasks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:49:49 +10:00

3.3 KiB
Raw Permalink Blame History

task version source status
Write and load a Java PROCESS Service class R1.03 IM 36K10P11-01EN §1–§5, §10; IM 36K10L10-01EN §1; practiced with CI-Eng-Sample class CIENG_JFLOW 2026-09-30 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).