Full manual diffs (L15, C20, E30, L25, J40, C10 §2, L10, P10, P11, J20, C30, P20, R40, E10) and read-only checks on the shared R1.05 test server. Skills and summaries moved to R1.05; new skill pytools (IM 36K10P40) from docs (no Python on the test server). Issues ISS-132..181; corrections to version-differences (E10, E30, P11, dss_ath_grp_u0104); C10 summary lists the 3 new authorization actions; r105-verification V10-V17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
203 lines
12 KiB
Markdown
203 lines
12 KiB
Markdown
---
|
|
manual: IM 36K10P40-01EN
|
|
title: pyTOOLS Programmer's Guide
|
|
edition: 2nd Edition, Feb. 2026 (R1.05); new in R1.04 (1st Edition, Jan. 2025). Revision table: 2nd edition "layouts updated" only (manual new, no diff against R1.03)
|
|
version: R1.05
|
|
status: from docs, plus read-only file checks on the R1.05 test server (2026-10-09) - `dss.pyd`, `DSSSDAPY`, `dss.sup`, `dssana.sup` checked; no Python on the R1.05 test server, so no function was run there (not tested, ISS-178). R1.03 `dss.pyd` loaded on the development machine with Python 3.12 (`version()`, `busGetDirs()` only; CI Server not running)
|
|
---
|
|
|
|
# pyTOOLS Programmer's Guide (R1.05)
|
|
|
|
pyTOOLS is a **Python extension module (`dss`)** for scripts that read and change CI Server
|
|
data through DSS (Data Set Services), exchange BUS Service messages and read item history.
|
|
The module is the file `%TLS_ROOT_PATH%\tls\exe\dss.pyd`. Section numbers refer to the manual.
|
|
Practical use: skill `pytools`.
|
|
|
|
## 1. Overview and operating environment (§1)
|
|
|
|
- Runs on **CI Core and CI Portal**, not on CI View.
|
|
- Always uses the **local** DSS: a script cannot reach another CI Core by node number (only the
|
|
item history functions and BUS messages take an optional node number, §3.11, §3.13).
|
|
- Supported Python: **64-bit Python 3.12** (IM 36K01C10-01EN §1.3.3, coexisting software list;
|
|
summary `IM36K01C10-01EN.md`). Python is not part of the CI Server installation.
|
|
|
|
## 2. Setup (§2)
|
|
|
|
1. Install 64-bit Python from python.org on the Core / Portal (run the installer as
|
|
administrator).
|
|
2. Add the Windows environment variable `PYTHONPATH` = the `tls\exe` folder of CI Server. The
|
|
manual gives the default `C:\Program Files (x86)\Yokogawa\CIServer\tls\exe`; use
|
|
`%TLS_ROOT_PATH%\tls\exe` of the actual installation (on the R1.05 test server the install
|
|
root is `C:\Yokogawa\CIServer`, ISS-177).
|
|
3. In Python: `import dss`.
|
|
|
|
**Installed (checked 2026-10-09):** the module links `python3.dll` (stable ABI), `buslib64.dll`
|
|
and `databaselib64.dll` (both in `tls\exe` next to the module), so it is not tied to one Python
|
|
3.x minor version; Yokogawa only lists 3.12. `dss.pyd` already shipped with R1.03 (module 3.6,
|
|
r10200M, Aug. 2023, 124,928 bytes; no dedicated DSS server, no *Python Interface* entry in
|
|
`dssana.sup`); R1.05 has module **3.6.1** (r11609M, Jun. 2026, 137,728 bytes) with session-based
|
|
logon, the `PY_DSS_NOT_LOGGED_IN` exception and the `aggr_period` history argument (ISS-174).
|
|
|
|
## 3. DSS interface (§3)
|
|
|
|
### 3.1 Function groups (§3.1)
|
|
|
|
Connect/logon, DSS properties, open/close datasets, read, modify, indexing, filtering, locking,
|
|
event notification, BUS messages, date/time, item history, miscellaneous. All functions take the
|
|
**connection handle** first (except `version()`, `busGetDirs()`, `busSetName()`, `busSnap()`).
|
|
|
|
### 3.2 Connecting (§3.2)
|
|
|
|
| Function | Purpose |
|
|
|---|---|
|
|
| `dss.connect([user], [password])` | Attach to BUS Service and DSS; returns the connection handle (a dict with the DSS connection id and optionally user/password). With a user, a password is required and a logon is done at once |
|
|
| `dss.logon(conn, user, password)` | Log on (or switch to another user); a failed logon keeps the old rights |
|
|
| `dss.logout(conn)` | Log out |
|
|
| `dss.close(conn)` | Disconnect and free all DSS resources (also logs out) |
|
|
|
|
- One connection per script; a second `connect()` raises `PY_DSS_ALREADY_CONN`.
|
|
- Without a logon DSS gives **no data** (default); a logon without user/password is refused.
|
|
- The user name is used in the **audit trail**; pyTOOLS operations appear with source
|
|
`PYTOOLS`. Installed R1.05: `PYTOOLS` audit classifiers exist for login / logout / login
|
|
attempt, dataset insert / modify / delete and item value / status / quality changes (skill
|
|
`audit-trail`, `audit-checks.md`, ISS-141).
|
|
- Server side: pyTOOLS clients have their **own DSS data access server**, so they do not compete
|
|
with other DSS clients (§3.2 figure). Installed R1.05: `database_start.cmd` starts
|
|
`dsssda -n dsssdapy`; `dss.sup` routes `DSSSDA_SRV = -1, "PYDSS*", "DSSSDAPY"` (ISS-117).
|
|
- Login method: `dssana.sup` *User Interfaces* tab has `"Python Interface", "PYDSS"` (audit id 18);
|
|
*Login control* has `<local>` and `<remote>` → `Manual CI Server` (verified read-only on the
|
|
R1.05 test server 2026-10-09; R1.03 `dssana.sup` has neither line). J10 §2.9.1: pyTOOLS supports
|
|
only *Manual CI Server* (summary `IM36K10J10-01EN.md`); what happens with another method is not
|
|
documented (ISS-179). A pyTOOLS client that is not in the *User Interfaces* list cannot connect
|
|
(`dssana.sup` help).
|
|
|
|
### 3.3 DSS properties (§3.3)
|
|
|
|
`getAvailableDatasets(conn)` → list of dataset names; `getFieldNames(conn, dataset)` → list of
|
|
field names; `getFieldProperties(conn, dataset, field)` → dict `name`, `type`, `length`.
|
|
(Installed module also has `getDatasetProperties(conn, dataset)`, not in the manual, ISS-176.)
|
|
|
|
### 3.4 Open / close (§3.4)
|
|
|
|
`ds = dss.openDataset(conn, dataset, fields, mode)` — `fields` is the list of fields of interest
|
|
(only these are read and can be set); `mode` any of `r`, `u`, `d`, `i`, plus `t` = handle all
|
|
time fields as UTC (default local civil time). Open the same dataset several times if needed.
|
|
`dss.closeDataset(conn, ds)` frees it.
|
|
|
|
### 3.5 Reading (§3.5)
|
|
|
|
| Function | Returns |
|
|
|---|---|
|
|
| `readNext(conn, ds)` | Next record (dict field → value) in the order of the active index, honouring an active filter; raises `DSS-E-EOS` at the end |
|
|
| `readEqual(conn, ds, key)` | The record whose active index equals `key` (string, or dict for a composed index); first match on a non-unique index; filters ignored; the cursor moves past it |
|
|
| `readRecords(conn, ds)` | List of all records (the manual's "Returns: None" is wrong, ISS-175) |
|
|
| `rewindDataset(conn, ds)` | Cursor back to the first record |
|
|
|
|
### 3.6 Modifying (§3.6)
|
|
|
|
`insertRecord` / `insertRecords` (dict or list of dicts; fields not opened get defaults),
|
|
`updateRecord` / `updateRecords` (must contain the current index field), `deleteRecord(conn, ds,
|
|
key)`. The dataset must be opened with the matching mode letters (`i`, `u`, `d`). Changes go
|
|
through DSS like any other client (validation, audit trail).
|
|
|
|
### 3.7 Indexing (§3.7)
|
|
|
|
`getIndex(conn, dataset)` → index names (e.g. `STATUS_DF`: `NAME`, `STATUS_NUMBER`);
|
|
`setIndex(conn, ds, index_name)` changes the active index and thus the read order and the key
|
|
used by `readEqual`. Default: the primary index.
|
|
|
|
### 3.8 Filtering (§3.8)
|
|
|
|
`createFilter(conn, ds, expr)` → `activateFilter` → reads → `deactivateFilter` → `deleteFilter`
|
|
(an active filter cannot be deleted; one filter per open dataset). Expression: either a quoted
|
|
wildcard string (`"*.TANK1.*"`, applied to the current index field; `*` any string, `?` one
|
|
character), or comparisons `<field> <op> <constant>` with `= != < <= > >= LIKE`, joined by `AND` /
|
|
`OR`, brackets allowed. String constants are case-sensitive and in double quotes (so wrap the
|
|
whole expression in single quotes in Python); field names and operators are not; spaces around
|
|
operators are required; maximum 252 characters.
|
|
|
|
### 3.9 Locking (§3.9)
|
|
|
|
`lockRecords(conn, ds, [keys])` / `unlockRecords(...)` — other CI Server processes cannot change
|
|
or delete a locked record; locking an already locked record raises an exception.
|
|
|
|
### 3.10 Event notification (§3.10)
|
|
|
|
- Dataset level: `datasetEventNotify(conn, ds, mode)` with `i`, `u`, `d`; stop with
|
|
`datasetEventStop(conn, ds)`.
|
|
- Record level: `recordEventNotify(conn, ds, key, mode)` with `u`, `d`; stop with
|
|
`recordEventStop(conn, ds, key)`.
|
|
- Poll with `eventRead(conn)` → list of dicts `ds_name`, `event` (`i`/`u`/`d`), `id` (running
|
|
number), `record` (record after the change), `set_hnd`. Empty list when nothing happened.
|
|
- One queue per client; DSS **clears the queue when it overflows** (size from the DSS setup
|
|
file — `EVENT_Q_SLOTS` in `dss.sup`, default 100, ISS-181), so poll often enough.
|
|
- Overlapping requests give one event; a record can only have one request — stop it before
|
|
changing the mode (`PY_DSS_EVT_NOTIFY_DEF`).
|
|
|
|
### 3.11 BUS Service messages (§3.11)
|
|
|
|
- The script attaches to the BUS as `PYDSS_<number>` (new each run). To be addressable, call
|
|
`dss.busSetName('<NAME>')` **before** `connect()`, every time. (Installed `dss.sup` routes only
|
|
`PYDSS*` clients to `DSSSDAPY`; a script with its own name probably uses a general `DSSSDA`,
|
|
not checked, ISS-180.)
|
|
- `busSend(conn, {'process':…, 'node':…, 'code':…, 'message':…})` (node and code optional;
|
|
message always a string); `busSendReceive` waits up to 60 s for the reply (dict with
|
|
`message`); `busReceive(conn[, 'SYNC_WAIT'])` → dict (`process`, `node`, `reply`, `sys_type`,
|
|
`code`, `message`) or `None`; `busReply(conn, msg)` must answer within 60 s, passing `reply` and
|
|
`sys_type` back unchanged.
|
|
- `durstp -p <name>` stops a named pyTOOLS script: the script gets `dss.error` with code
|
|
`PY_DSS_STOP_REQUEST` and should close and exit.
|
|
- `busSnap('GENERAL'|'QUEUE'|'NODE'|'LOCK')` takes a snapshot, then `busSnap('FIRST')` /
|
|
`busSnap('NEXT')` return the entries (`None` at the end). Local node only (on a Portal you see
|
|
the Portal).
|
|
|
|
### 3.12 Date and time (§3.12)
|
|
|
|
- DSS time = seconds since 1970-01-01 as a float with milliseconds.
|
|
- `dateString(conn, t[, fmt])` → text; tokens `YYYY YY MMM MM M DDD DD D hh mm ss p…pppp`;
|
|
presets `ANSI`, `JULIAN`, `EUROPE`, `GENERAL`; default `DD-MMM-YY hh:mm:ss`.
|
|
- `dateDSS(conn, 'dd-mmm-yy hh:mm:ss.ppp')` → DSS time (missing date or time = now);
|
|
`dateRelDSS(conn, '[-]days,[+|-]hh:mm…')` → relative to now (e.g. `-1:00` one hour ago,
|
|
`-1,08:00` yesterday 08:00).
|
|
- `dateConvert(conn, t, 'GMT_TO_LCT' | 'GMT_TO_LST' | 'LCT_TO_GMT' | 'LST_TO_GMT' | 'LCT_TO_LST' |
|
|
'LST_TO_LCT')`. LST = standard time, LCT = with daylight saving.
|
|
- Interval fields (e.g. `REPORT_DF.LIFE_TIME`) are `<count>.<unit>`: `.0` s, `.1` min, `.2` h,
|
|
`.3` days, `.4` weeks, `.5` months, `.6` years (same as `dssutl`, skill `system-management`).
|
|
|
|
### 3.13 Item history (§3.13)
|
|
|
|
Two functions bypass DSS and ask ITEM Service directly (faster than reading `ITEM_HISTORY`):
|
|
|
|
- `getHisGroupItems(conn, group[, node])` → item names in a storage group.
|
|
- `getItemHistory(conn, item, group, start, end, max_sample[, node=, interval=, attribute=,
|
|
aggr_period=])` → list of tuples `(time, value, status, option_bits, quality)`; the list ends
|
|
with `None` when the period is complete, otherwise call again with the last time as start.
|
|
`interval` = interpolation step in **ms**; `attribute` = `VALUE` (default), `DEADBAND`,
|
|
limits, `SCALE_LOW/HIGH`, or for aggregation groups `AGGR_MAX`, `AGGR_MIN`, `AGGR_AVERAGE`,
|
|
`AGGR_INTG`, `AGGR_COUNT`, `AGGR_STDV`, `AGGR_DFMS`; `aggr_period` = `30_MINUTES`, `HOUR`,
|
|
`SHIFT`, `DAY`, `WEEK`, `MONTH`, `YEAR` (only periods configured in the group). The manual's
|
|
parameter list omits `item` (ISS-175).
|
|
|
|
### 3.14 Miscellaneous (§3.14)
|
|
|
|
`getErrorStack(conn)` (UMH stack as `[code, text]` pairs), `version()`, `busGetDirs()` (dict of
|
|
`TLS_*` folders; installed also returns `TLS_WAP`, `TLS_LOG`), `readSetup(conn, 'dur')` (setup
|
|
file without `.sup` as list of `[keyword, args…]`), `logMessage(conn, text)` (max 200 characters
|
|
to the CI Server message log), `umhLog(conn)`.
|
|
|
|
## 4. Exceptions (§4)
|
|
|
|
One exception class, `dss.error`, with `args = (code, message)`:
|
|
- **CI Server errors**: the last UMH message, e.g. `DSS-E-EOS` (end of dataset — the normal way
|
|
to end a `readNext` loop), `VIS-W-DUPLICATE` (insert of an existing key).
|
|
- **Module errors** `PY_DSS_*`: `ALREADY_CONN`, `DS_IS_CLOSED`, `EVT_NOTIFY_DEF`, `FLT_DEF`,
|
|
`FLT_NOT_ACTIV`, `FLT_NOT_FOUND`, `FLT_TOO_LONG` (manual misprints `FLY`), `MISG_IDX`,
|
|
`NO_CHAN_OPEN`, `NO_EVT_DEF`, `NOT_CONN`, `NOT_LOGGED_IN`, `UNK_ERR`, `UNK_FLD`, `UNK_FLD_TYPE`;
|
|
the installed module has more (ISS-176).
|
|
|
|
## Notes on the manual
|
|
|
|
The examples are carried over from an old Python 2 version (`101L` long integers, an old module
|
|
version string, a `FastTools` install path) and contain typos (e.g. `readEqual(con, dss, …)`
|
|
instead of the dataset handle, `msg`/`mesg` mixed up). They use `dss.connect('admin','')`; on a
|
|
real system use a CI Server user with only the rights the script needs. Details: ISS-175.
|