Compare commits
2 commits
5413d0195c
...
42240a86d0
| Author | SHA1 | Date | |
|---|---|---|---|
| 42240a86d0 | |||
| 516729ff24 |
15 changed files with 293 additions and 3 deletions
|
|
@ -139,3 +139,8 @@ No alarm line from `ALDPRT` appeared in `umhlog.log` (ISS-035).
|
|||
test item of a non-shared R1.05 system (expected results as in the table above).
|
||||
- Read `ALARM_CHRONO` only with a filter on item name **and** a time range: big history reads
|
||||
load the alarm storage process that all clients share (ISS-136).
|
||||
|
||||
- 2026-10-12 (user project on the R1.05 test server, 2026-10-09 to 10-12): LOW/HIGH limit alarms on Real items loaded with the item (`LOW_LOW_LIMIT,LOW_LIMIT,
|
||||
HIGH_LIMIT,HIGH_HIGH_LIMIT` = `-100,-5,5,100`, `ALARMING` 1, `ACKN_TYPE` `"MANUAL"`): a process class
|
||||
writing the value raised status 8 (LOW) / 10 (HIGH) and one `ALARM_CURRENT` row each (priority 2
|
||||
from `STATUS_DF`), cleared on return to normal.
|
||||
|
|
|
|||
|
|
@ -128,3 +128,11 @@ and `AUTH_GROUP_DF` keep the same relation, but the DSS processes are arranged d
|
|||
(ISS-117), so treat ISS-073 as still valid until it is re-tested on a separate system (ISS-152).
|
||||
The same applies to the password behaviour of ISS-072 (`USER_DF.PASSWORD` unchanged): keep
|
||||
giving `PASSWORD ""` or setting the password in the Engineering Module.
|
||||
|
||||
## R1.05 (2026-10-12, user project on the R1.05 test server, 2026-10-09 to 10-12)
|
||||
|
||||
A user with `PASSWORD ""`, factory group `OPERATOR`, `LAYOUT` and `INITIAL_DISPLAY` loaded with 0
|
||||
errors and logged on to the web client without a password, landing on the display in the layout
|
||||
(no `?l=` or `?d=` needed). `OPERATOR` includes `ITEM_MODIFY_VALUE` and `ITEM_MODIFY_STRING_VALUE`,
|
||||
so display buttons that write items work for it. Removing the user (`dssqld -r`) after the user
|
||||
had exited the web session gave 0 errors.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,10 @@ 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
|
||||
(Bash. Interactively, type the same answers.) In Windows `cmd`, put the answers in a file
|
||||
(`2`, `8`, `<CLASS>`, `0`, empty line, `-1`, `-1`, one per line, CRLF) and run
|
||||
`opcdbg 0 < reset_class.txt` (verified on R1.05 2026-10-12). A `dssqld -m` with `NAME,TRIGGER_1,COMPILE,METHOD`
|
||||
also changes the class's trigger group (verified on R1.05). 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).
|
||||
|
|
@ -63,8 +66,17 @@ and a reset report; click **Reset** (verified by the user 2026-10-01: 1 of 1 obj
|
|||
|
||||
## 3. Changing prompts or signals
|
||||
|
||||
Only a change in the method bodies was practiced. When the class **prolog** changes (new
|
||||
Only a change in the method bodies was practiced on R1.03. 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.
|
||||
|
||||
**R1.05, Java (verified 2026-10-12, user project on the R1.05 test server, 2026-10-09 to 10-12): new signals are refused while objects exist.** `dssqld -m` of
|
||||
the class with extra `define…Signal` fields gives only `DSI-E-OPCCOMERR, Compilation failed with 1
|
||||
error(s)` / `DSSP-E-UPDREC` in the `.qlo`; the reason is in the newest `%TLS_ROOT_PATH%\tls\lst\opc_t*.lst`:
|
||||
`**** ERROR: Attribute or signal definitions cannot be changed while objects are derived from the
|
||||
class`. The old class keeps running. Procedure that worked: remove the object (`@OBJECT_DF`, `-r`),
|
||||
`dssqld -m` the class, wait for the compile, insert the object again. **The object's attributes are
|
||||
lost** with it; the tested class rebuilt its data on the first run of the new object from items it
|
||||
also writes (its log message confirmed the attributes were empty).
|
||||
|
|
|
|||
|
|
@ -104,6 +104,30 @@ refuse bad answers with `cancel("text")` in `create()`.
|
|||
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`.
|
||||
|
|
|
|||
|
|
@ -122,3 +122,18 @@ files deleted), reloaded from the sample files and re-tested: all 0 errors. With
|
|||
`eqpciengslv_start.cmd`/`_stop.cmd` copied to `tls\com` and CI Server restarted, the mirror and
|
||||
`EQPCIENG` started under the CI Service, `COMM_OK` = 1, and the saved setpoint (2468) was
|
||||
re-sent to the freshly zeroed slave memory on connect (output method *On change and connect*).
|
||||
|
||||
## R1.05: a 400-register device (2026-10-12, user project on the R1.05 test server, 2026-10-09 to 10-12)
|
||||
|
||||
- Own line and equipment manager (`EQUIPMENT_MAN` e.g. `EQP<NAME>`) next to another project's
|
||||
line: loaded, removed and loaded again with 0 errors; removing the line stopped only its manager.
|
||||
- 400 holding registers as points `RO:1`–`RO:400`, `CONV_TYPE` `Linear` with `HAS_SIGN` 1,
|
||||
`ELEC_LOW,ELEC_HIGH` = `-32768,32767` and `PHYS_LOW,PHYS_HIGH` = `-327.68,327.67` (raw ÷ 100):
|
||||
all 400 items read the expected signed values (test pattern from a simulator, quality 0,
|
||||
status 9).
|
||||
- Communication loss: status item 0 (status 4), inputs keep their last value with the `off` option;
|
||||
values and status item recover on the next scan after the device answers again.
|
||||
- Test without the device: `tools/ModbusSim.java` (Java, runs with CI Server's own JDK; holding
|
||||
registers with read and write, scenarios through a mode file). A PowerShell script that opens a
|
||||
TCP listener was blocked by Windows antivirus (AMSI) as malicious content on the engineering PC
|
||||
(Windows 10).
|
||||
|
|
|
|||
|
|
@ -71,6 +71,21 @@ Module, the Operator Interface and the HTML display, if these rules are kept (sa
|
|||
| Server-side command | `<action type="actionExecuteCommand">` with `<command>` and `<onServer>true</onServer>` runs on the CI Server node as the service account `CIX_PROCESS`, also from the web; a user in the factory group `OPERATOR` may use it (tested 2026-10-06, sample live-data *Start*) |
|
||||
| Alarm overview | `autoFit` true in a tab that is hidden while loading squeezes all columns to ~0 px on the web: set `autoFit` false and give `column<Name>Width`; `column…Visible` for `AlarmAttribute` is an array property (a plain value fails the HTML export with `ClassCastException`); the empty part of the list stays **white** on both clients (no property; web: `jquery.dataTables.css` forces it); web rows are 20 px at a 14 px font (2026-10-02/06) |
|
||||
|
||||
## R1.05 web client: findings from a generated display (verified 2026-10-12, user project on the R1.05 test server, 2026-10-09 to 10-12)
|
||||
|
||||
A generated display with about 1,900 components (6 MB XML, 1 MB HTML) on the R1.05 web client:
|
||||
|
||||
| Topic | Finding |
|
||||
|---|---|
|
||||
| Text bound to a String item | `<text>` with `text.value` ← `ItemValue` works. `keepOriginalSize` false stretches it to the box (as on R1.03, unreadable for changing text); `true` draws it centred at natural size, good for centred values. For **left-aligned** changing text use a `<textField>`: `textField.value` ← `ItemValue`, `<editable>false`, `<borderType type="border" value="BorderEmpty"/>`, justification `TextJustificationLeft` (or `Right`). `<opaque>false` is ignored: set `backgroundColor` to the colour behind it, or connect `textField.backgroundColor` through a threshold with output type `ColorSet` when the background changes. |
|
||||
| Fixed text that must align | A `<text>` is centred on its box at its natural width; on one viewer's client long lines started left of their box. A `<textField>` with a fixed `<value>` (no connection) is left-aligned (checked in the test browser). |
|
||||
| Hidden at start | `<visible>false</visible>` plus a `*.visible` connection: rectangles, texts and numbers appear when the condition becomes true; **ellipses, textFields and numberFields stay hidden**. Leave those visible at start. Without a start value every conditional component is drawn until the first values arrive. |
|
||||
| Two conditions | Two connections to the same `*.visible` property do not combine (numbers stayed visible after one of their two conditions became false). Encode the condition in one item (e.g. a state item the calculation writes). |
|
||||
| Display actions on open | Actions placed directly in `coreObjectDefinition` (before the first layer, as in the factory `AOG_ItemFaceplate`) with `<event type="event" event="On entry"/>` run when the display opens **and** on *Reload*; e.g. `actionSetItemAttribute` to reset a selection item. |
|
||||
| Blinking | `rectangle.blinking` ← item through a threshold (output Boolean) plus a `blinkingPaint` blinks (the component toggles visibility at the display's `blinkDelay`, 500 ms). |
|
||||
| Also verified | `numberField` with `asDateTime` on `ItemValue` (epoch seconds); `text.fillPaint`, `rectangle.strokePaint`, `ellipse.strokePaint` through thresholds; `actionSetItemAttribute` with event `Clicked b1` on a rectangle and on a number (a number drawn on a clickable shape needs its own action, it catches the click). |
|
||||
| Size and first load | Once the first values took about a minute on a display this size; later loads took 3–10 s. |
|
||||
|
||||
## R1.05: component changes since R1.03 (from docs, IM 36K10C50-01EN 5th ed.)
|
||||
|
||||
| Component / sheet | Change (version) |
|
||||
|
|
|
|||
|
|
@ -193,12 +193,14 @@ browser and in the web client code. "n/a" = the browser window is the main layou
|
|||
| `toolbar` (check) | hides/shows **all** toolbars; `toolbar false` behaves as a toggle too | no effect |
|
||||
| `statusline` (check) | hides/shows (a piece of the date field stays painted) | no effect |
|
||||
| `print …` | preview / setup dialogs open | disabled |
|
||||
| `login` / `logout` / `exit` | — / — / closes | login disabled; logout, exit work |
|
||||
| `login` / `logout` / `exit` | — / — / closes | login disabled; **`logout` disabled on R1.05** (greyed; see note); `exit` closes the window and ends the session |
|
||||
| `shell client <cmd>` | runs on the client (Notepad opened) | sent to the server; `shell client ACTION <layout command>` runs a layout command |
|
||||
| `lookfeel <class>` | — | no effect |
|
||||
| `playback record` (check) / `view` | — | not supported (view disabled) |
|
||||
| `help` | **error** *Unrecognized keyword* | — |
|
||||
|
||||
**`logout` is a secondary logout (R1.05, verified 2026-10-12).** The factory layout labels it *Secondary logout* (pair of `login`). The R1.05 web client always disables it (`LayoutWindow.js`: `setEnabled(false)` for `logout`; the command calls `secondaryLogout()`), so a toolbar or menu item with `logout` is greyed out. To let a user leave on the web, use **`exit`**: it closes the layout window and, for the last window, ends the session; the browser then closes the tab (reopening the URL shows the login page). Label the item *Exit* rather than *Log out*. Tested on the web client; the desktop Operator Interface was not tried. The sample layout `CIENG_Operator` still has a *Log out* (`logout`) menu item.
|
||||
|
||||
"—" = not checked.
|
||||
|
||||
### 6.5 Layouts opened from a display, translations, debug key
|
||||
|
|
|
|||
|
|
@ -37,3 +37,8 @@ After a write, check the status: 12 means the text was cut.
|
|||
|
||||
The representation (type) and length cannot be changed on an existing item: remove and
|
||||
load it again.
|
||||
|
||||
**Mixed item files (R1.05, 2026-10-12).** `STRING_LENGTH` is checked on every record of an `@FIELDS`
|
||||
block: `0` (or empty) on a Real/Integer item is refused with `DSS-E-VAL_RANGE ... (Field:
|
||||
STRING_LENGTH)`. Put String items in their own `@FIELDS` block with `STRING_LENGTH`, and the other
|
||||
items in a block without it.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ Rules for every task:
|
|||
|
||||
| Task | File | Status |
|
||||
|---|---|---|
|
||||
| Package a project for another CI Server (quick load, display, layout, user, README, pre-check) and test the install | `tasks/package-project.md` | verified on R1.05 with a user project 2026-10-12 (not on CI-Eng-Sample) |
|
||||
| Export dataset contents (whole or filtered) / dump the data dictionary | `tasks/export-dataset.md` | verified (R1.03; re-tested on R1.05 2026-10-08) |
|
||||
| Load or remove records with quick load files (`.qli`), e.g. sections and items | `tasks/load-remove-records.md` | verified (R1.03; load/remove used on R1.05 in the CI-Eng-Sample test 2026-10-08) |
|
||||
| Read / write item values and read limits | `tasks/read-write-item.md` | verified (R1.03; `-g`/`-p` used on R1.05 in the CI-Eng-Sample test, read re-checked 2026-10-08) |
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ dssqld -e <output>.qli <filter>.qli [-c] -l <listing>.lst
|
|||
REST API keys out on purpose. `-d API_KEYS_DF` still exports it; treat that file as a secret,
|
||||
or create new keys on the target system instead (skill `data-access`).
|
||||
|
||||
- **Range on `NAME`** (R1.05, 2026-10-12): `'NAME > "<Section>." and NAME < "<Next>"'` (`<Next>` = the section
|
||||
name with its last character raised by one, e.g. `TANK1.` → `TANK2`) selects all items below a
|
||||
section on `ITEM_DF` (tested with 1,751 items); a `NAME = "A*"` wildcard does not work.
|
||||
The same works on `ALARM_CURRENT` with `ITEM_NAME`, to list the current alarms of one section.
|
||||
|
||||
## Clean up: `dss_*.tmp` files (R1.05)
|
||||
|
||||
Exporting `OBJECT_DF` leaves 0-byte `dss_<n>_<id>.tmp` files in `%TLS_LST_PATH%` (`tls\lst`),
|
||||
|
|
|
|||
38
.claude/skills/system-management/tasks/package-project.md
Normal file
38
.claude/skills/system-management/tasks/package-project.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
task: Package a project for another CI Server (quick load files, display, layout, user) and check the install
|
||||
version: R1.05
|
||||
source: IM 36K10J10-01EN §5.6 (moving engineering data); IM 36K10L50-01EN §3; practiced with a user project on the R1.05 test server 2026-10-12 (remove and reload from the package, twice)
|
||||
status: verified on R1.05 with a user project (not on CI-Eng-Sample)
|
||||
---
|
||||
|
||||
# Package a project for another CI Server
|
||||
|
||||
Goal: a folder (zip) a junior engineer can install from, step by step, on another server.
|
||||
|
||||
## 1. What goes in
|
||||
|
||||
| Part | Notes |
|
||||
|---|---|
|
||||
| Quick load files `01_…` to `nn_…` in load order + `load.cmd` | sections, status items, line, station/points, items, trigger, class, object, users. **No node names** (leave `EQUIPMENT_NODE`, `FRONT_END_NODE` out) and no `NSID` / `ID_NUMBER`: the target assigns them. Insert (`-i`) only. |
|
||||
| Removal files in reverse order + `remove.cmd` | so the install can be undone |
|
||||
| Displays + a deploy script | copy to `DEPLOY\displays`, `jwuiutl -d -v -i -l … <display>` (must say *No problems found*), copy again (validation re-saves), `jwuiutl -e HTML -i -f DEPLOY <display>`. Displays without `<itemId>` bind by name, so new item numbers on the target do not matter. |
|
||||
| Layout block + script | only the project's layout(s) between `<!-- <P>-BEGIN` / `<!-- <P>-END -->`, inserted before the first `<translations>`; the script makes a timestamped backup and checks the XML (sample: `examples/CI-Eng-Sample/layouts/layout.ps1`). |
|
||||
| README | numbered steps with the exact command, the expected output and what to do if it differs |
|
||||
| Pre-check script (read-only) | CI Server version (`%TLS_ALLUSERS_PATH%\lst\tls_versions.lst`), `dssqld` answers, longest file path ≤ 79 (ISS-020), `tls\jre\bin\javac.exe` present (Java classes compile on load), device reachable (TCP connect), each name free (filtered export per dataset, e.g. `'NAME = "<LINE>"'` on `MODBUS_LINE_DF`, `'EQUIPMENT_MAN = "<EQPNAME>"'`), layout name not used outside the project block |
|
||||
| Post-install check | `itmval -g` of the station status item, one value to compare with the device's own software, a value the calculation updates (read twice), learn/initial states |
|
||||
|
||||
## 2. Script rules (found by testing the README in one Command Prompt)
|
||||
|
||||
- **`pushd "%~dp0"` … `popd`**, not `cd /d "%~dp0"`: the engineer runs the scripts one after the other
|
||||
in the same prompt; a script that leaves the prompt in its own folder breaks the next command.
|
||||
- `load.cmd`: delete the old `0*.qlo` first and end with `findstr /c:"_DF " 0*.qlo` (one result
|
||||
line per data set; the last column is *Errors*). Otherwise old results (e.g. of an undo) are listed too.
|
||||
- Wait after the line (`powershell -NoProfile -Command "Start-Sleep -Seconds 15"`, the equipment
|
||||
manager starts) and after a class (about 20 s, compile) before the next file.
|
||||
|
||||
## 3. Test the package
|
||||
|
||||
On a test server: run the README's undo, then every step exactly as written, in one Command Prompt,
|
||||
from a fresh copy of the zip; point the device address at a simulator (`tools/ModbusSim.java`) if
|
||||
the device is not reachable. Compare the server against the package afterwards (filtered `ITEM_DF`
|
||||
export by name range, class source from a `CLASS_DF` export, display file hash).
|
||||
|
|
@ -179,3 +179,10 @@ development system (see "Test system" in `progress.md`); they do not exist on ot
|
|||
| ISS-179 | 2026-10-09 | PYTOOLS (login method) | pyTOOLS supports only *Manual CI Server* (J10 §2.9.1); the installed R1.05 *Python Interface* lines are Manual CI Server. Behaviour with another method or with the lines missing after an upgrade (ISS-149) is not documented. | open | From docs plus the installed file. | P40 summary (done); `.claude/skills/authorization/tasks/login-methods.md` (done) |
|
||||
| ISS-180 | 2026-10-09 | PYTOOLS (BUS name routing) | `dss.sup` routes only `PYDSS*` clients to DSSSDAPY; a script that sets its own BUS name with `busSetName` probably falls back to the general DSSSDA00–03 routing. Not in P40. | open | Untested (no Python). | `.claude/skills/pytools/tasks/event-notification.md`, P40 summary (done) |
|
||||
| ISS-181 | 2026-10-09 | PYTOOLS (event queue) | IM 36K10P40 §3.10 says the event queue size is set in the DSS setup file without naming the keyword: it is `EVENT_Q_SLOTS` in `dss.sup` (default 100, no explicit line). | open | From docs and the `dss.sup` help. | P40 summary, `.claude/skills/pytools/tasks/event-notification.md` (done) |
|
||||
| ISS-182 | 2026-10-12 | GRAPHICS (layouts, web) | Layout command `logout` is a *secondary* logout and is always disabled on the R1.05 web client (`LayoutWindow.js`); the R1.03 table said it works on the web. | closed | Use `exit` (closes the window; the last window ends the session, the browser closes the tab). Verified on the R1.05 web client 2026-10-12. The sample layout `CIENG_Operator` still has a `logout` item: change it after re-testing on the sample (ISS-188). | `graphics/tasks/layouts.md` §6.4, `examples/CI-Eng-Sample/layouts/CIENG_Operator.xml` |
|
||||
| ISS-183 | 2026-10-12 | GRAPHICS (web, visibility) | Ellipses, textFields and numberFields that start with `<visible>false</visible>` stay hidden on the R1.05 web client when their visibility connection becomes true (rectangles, texts, numbers work). | closed | Leave those components visible at start (workaround). Verified 2026-10-12. | `graphics/tasks/create-display.md` §R1.05 web client |
|
||||
| ISS-184 | 2026-10-12 | GRAPHICS (web, String items) | A `<text>` bound to a String item is stretched to its box with `keepOriginalSize` false and centred with true; `textField` `opaque false` is ignored (white background). | closed | Left-aligned text: borderless read-only `textField` with the background colour set or connected. Verified 2026-10-12. | `graphics/tasks/create-display.md` |
|
||||
| ISS-185 | 2026-10-12 | PROCESS (Java, signals) | Changing the signal or attribute definitions of a Java class while an object exists fails with only `DSI-E-OPCCOMERR` in the `.qlo`; the reason is in `tls\lst\opc_t*.lst`. Recreating the object loses its attributes. | closed | Remove object, modify class, insert object; rebuild attribute data from items. Verified on R1.05 2026-10-12. | `class-object-engineering/tasks/change-reset-class.md` §3, `java-class.md` §4a |
|
||||
| ISS-186 | 2026-10-12 | TOOLS (Windows security) | A PowerShell script that opens a TCP listener (Modbus test server) is blocked by Windows antivirus/AMSI: *This script contains malicious content and has been blocked* (engineering PC, Windows 10). | closed | Use the Java test server `tools/ModbusSim.java` (CI Server JDK). Seen 2026-10-12. | `tools/README.md`, `equipment/tasks/modbus-station.md` |
|
||||
| ISS-187 | 2026-10-12 | ITEM (quick load) | `STRING_LENGTH` 0 on a Real item in a mixed `@FIELDS` block is refused (`DSS-E-VAL_RANGE`). | closed | Separate `@FIELDS` blocks for String and other items. Verified on R1.05 2026-10-12. | `item-engineering/tasks/string-items.md` |
|
||||
| ISS-188 | 2026-10-12 | SAMPLE (R1.05) | The R1.05 findings of 2026-10-12 were verified with a user project, not with CI-Eng-Sample (displays, layouts and users of the sample are not loaded on the shared R1.05 server). | open | Re-test on CI-Eng-Sample when the full sample can be loaded on an R1.05 system, including replacing the sample layout `logout` item with `exit`. | `knowledge/progress.md` R1.05 table |
|
||||
|
|
|
|||
|
|
@ -66,6 +66,16 @@ screenshot.
|
|||
| `reporting` | none listed (E10 one sentence removed, ISS-166) | re-checked (read-only) | 2026-10-09: report definitions, setup and `.rpt` files identical; hooks (ISS-121); 28 system reports, 6 factory destinations; interactive RQL re-run, new `ITEM_VAL` fields (ISS-126, 167); `API_KEYS_DF` not queried (ISS-168). Generate, print, item trigger, `rptstp`, SPOOL re-tested on R1.05 (V12, 2026-10-09). **Not tested:** Excel Add-in (no Office), report component, hardcopy printers. |
|
||||
| `examples/CI-Eng-Sample` | — | re-tested | Reduced load (no history group, user or layouts) loaded, tested and removed on the shared R1.05 server 2026-10-08 (V4); scripts in `shared\`. Full load not tested on R1.05. |
|
||||
|
||||
### Findings from a user project (2026-10-12)
|
||||
|
||||
A Modbus TCP device with 400 registers, a large Java process class, a generated display with a
|
||||
pop-up, a minimal layout, a password-less user and an install package were built and tested on the
|
||||
R1.05 test server (own objects only). Added to the skills: `layouts.md` (`logout` vs `exit`),
|
||||
`create-display.md` (R1.05 web client table), `java-class.md` §4a, `change-reset-class.md` (new
|
||||
signals, `opcdbg` in cmd), `string-items.md`, `users-groups.md`, `modbus-station.md`,
|
||||
`export-dataset.md` (name range), `item-alarming.md`, new task `system-management/tasks/package-project.md`,
|
||||
`tools/ModbusSim.java`. Issues ISS-182 to ISS-188. Not re-tested on CI-Eng-Sample (ISS-188).
|
||||
|
||||
## Manuals, in working order
|
||||
|
||||
| Phase | Manual | Title | Status |
|
||||
|
|
|
|||
142
tools/ModbusSim.java
Normal file
142
tools/ModbusSim.java
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* ModbusSim: a small Modbus TCP server (slave) for testing a CI Server Modbus station without the
|
||||
* real device. Java 11 single-file program: runs with the JDK that ships with CI Server, nothing
|
||||
* to install (a PowerShell TCP listener script is blocked by Windows antivirus/AMSI as
|
||||
* "malicious content", see knowledge/issues.md).
|
||||
*
|
||||
* "%TLS_ROOT_PATH%\tls\jre\bin\java.exe" ModbusSim.java [port] [registers] [folder]
|
||||
* defaults: 15020, 1000, the current folder. Listens on 127.0.0.1 only.
|
||||
*
|
||||
* One table of holding registers (FC03 read, FC06 / FC16 write); FC04 reads the same table as input
|
||||
* registers (like CI Server's own slave mirror). FC07 answers 0, FC08 echoes, others exception 01.
|
||||
* Register 1 = protocol address 0 (CI Server point RO:1 / RI:1 = 40001 / 30001).
|
||||
*
|
||||
* <folder>/mode.txt is read every 2 s and can be changed while it runs:
|
||||
* pattern raw(k) = ((k * 157) mod 30000) - 5000 (fixed, signed test values; the default)
|
||||
* zero all registers 0
|
||||
* csv values from <folder>/values.csv: one integer per line, line k = register k
|
||||
* hold keep the current values (use with writes from CI Server)
|
||||
* stop close and exit
|
||||
* Messages go to the console and <folder>/sim.log.
|
||||
* To keep it running after an SSH session ends, start it detached, e.g. with PowerShell
|
||||
* Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='...'}
|
||||
* Never run it on a production system or on a port a real device uses.
|
||||
*/
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class ModbusSim {
|
||||
static int n;
|
||||
static int[] reg;
|
||||
static volatile String mode = "";
|
||||
static Path folder;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int port = args.length > 0 ? Integer.parseInt(args[0]) : 15020;
|
||||
n = args.length > 1 ? Integer.parseInt(args[1]) : 1000;
|
||||
folder = Paths.get(args.length > 2 ? args[2] : ".").toAbsolutePath();
|
||||
reg = new int[n + 1];
|
||||
Path modeFile = folder.resolve("mode.txt");
|
||||
if (!Files.exists(modeFile)) Files.writeString(modeFile, "pattern");
|
||||
ServerSocket server = new ServerSocket(port, 10, InetAddress.getLoopbackAddress());
|
||||
log("listening on 127.0.0.1:" + port + ", " + n + " registers, pid " + ProcessHandle.current().pid());
|
||||
|
||||
Thread control = new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
String m = Files.readAllLines(modeFile).stream().findFirst().orElse("").trim().toLowerCase();
|
||||
if (m.equals("stop")) { log("stop requested"); System.exit(0); }
|
||||
if (!m.equals(mode)) { mode = m; log("mode " + m); apply(m); }
|
||||
Thread.sleep(2000);
|
||||
} catch (Exception e) { log("control: " + e); }
|
||||
}
|
||||
});
|
||||
control.setDaemon(true);
|
||||
control.start();
|
||||
while (true) {
|
||||
Socket c = server.accept();
|
||||
new Thread(() -> serve(c)).start();
|
||||
}
|
||||
}
|
||||
|
||||
static synchronized void apply(String m) throws IOException {
|
||||
if (m.equals("pattern")) for (int k = 1; k <= n; k++) reg[k] = ((k * 157) % 30000) - 5000;
|
||||
else if (m.equals("zero")) java.util.Arrays.fill(reg, 0);
|
||||
else if (m.equals("csv")) {
|
||||
java.util.List<String> lines = Files.readAllLines(folder.resolve("values.csv"));
|
||||
for (int k = 1; k <= n && k <= lines.size(); k++) reg[k] = Integer.parseInt(lines.get(k - 1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
static void serve(Socket c) {
|
||||
String who = String.valueOf(c.getRemoteSocketAddress());
|
||||
log("connect " + who);
|
||||
try (c; DataInputStream in = new DataInputStream(new BufferedInputStream(c.getInputStream()));
|
||||
OutputStream out = c.getOutputStream()) {
|
||||
while (true) {
|
||||
byte[] h = new byte[7];
|
||||
in.readFully(h);
|
||||
int len = ((h[4] & 0xFF) << 8) | (h[5] & 0xFF);
|
||||
if (len < 2 || len > 256) throw new IOException("bad length " + len);
|
||||
byte[] pdu = new byte[len - 1];
|
||||
in.readFully(pdu);
|
||||
byte[] r = handle(pdu);
|
||||
byte[] resp = new byte[7 + r.length];
|
||||
System.arraycopy(h, 0, resp, 0, 4);
|
||||
resp[4] = (byte) ((r.length + 1) >> 8); resp[5] = (byte) (r.length + 1); resp[6] = h[6];
|
||||
System.arraycopy(r, 0, resp, 7, r.length);
|
||||
out.write(resp);
|
||||
out.flush();
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
log("disconnect " + who);
|
||||
} catch (Exception e) {
|
||||
log("disconnect " + who + " (" + e + ")");
|
||||
}
|
||||
}
|
||||
|
||||
static int u16(byte[] b, int i) { return ((b[i] & 0xFF) << 8) | (b[i + 1] & 0xFF); }
|
||||
|
||||
static synchronized byte[] handle(byte[] pdu) {
|
||||
int fc = pdu[0] & 0xFF;
|
||||
if (fc == 3 || fc == 4) {
|
||||
int start = u16(pdu, 1), qty = u16(pdu, 3);
|
||||
if (qty < 1 || qty > 125) return new byte[]{(byte) (fc | 0x80), 3};
|
||||
if (start + qty > n) return new byte[]{(byte) (fc | 0x80), 2};
|
||||
byte[] r = new byte[2 + 2 * qty];
|
||||
r[0] = (byte) fc; r[1] = (byte) (2 * qty);
|
||||
for (int i = 0; i < qty; i++) {
|
||||
int v = reg[start + i + 1] & 0xFFFF;
|
||||
r[2 + 2 * i] = (byte) (v >> 8); r[3 + 2 * i] = (byte) v;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if (fc == 6) {
|
||||
int a = u16(pdu, 1);
|
||||
if (a >= n) return new byte[]{(byte) 0x86, 2};
|
||||
reg[a + 1] = (short) u16(pdu, 3);
|
||||
return pdu.clone();
|
||||
}
|
||||
if (fc == 16) {
|
||||
int start = u16(pdu, 1), qty = u16(pdu, 3);
|
||||
if (qty < 1 || qty > 123 || start + qty > n) return new byte[]{(byte) 0x90, 2};
|
||||
for (int i = 0; i < qty; i++) reg[start + i + 1] = (short) u16(pdu, 6 + 2 * i);
|
||||
return new byte[]{16, pdu[1], pdu[2], pdu[3], pdu[4]};
|
||||
}
|
||||
if (fc == 7) return new byte[]{7, 0};
|
||||
if (fc == 8) return pdu.clone();
|
||||
return new byte[]{(byte) (fc | 0x80), 1};
|
||||
}
|
||||
|
||||
static synchronized void log(String s) {
|
||||
String line = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + s;
|
||||
System.out.println(line);
|
||||
try {
|
||||
Files.writeString(folder.resolve("sim.log"), line + System.lineSeparator(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
}
|
||||
|
|
@ -6,4 +6,5 @@ location; take it from an argument or environment variable, with
|
|||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `ModbusSim.java` | Modbus TCP test server (holding registers, read and write, scenarios through `mode.txt`) for testing a Modbus station without the device; runs with CI Server's JDK (`java ModbusSim.java [port] [registers] [folder]`). Verified on R1.05 2026-10-12 |
|
||||
| `setup_check.ps1` | Read-only check of a machine against the CI Server R1.03 setup and security requirements (skill `installation-security`) |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue