e9e37385aa
Empty square brackets — `<obj>.<arrayAttr>[]` — are the supported
MxAccess reference form for fetching a whole array as a single value.
Verified live:
mxa read 'MESReceiver_001.MoveInPartNumbers[]' --llm-json
-> ok=true, value=["", "11111", "", ..., "15", ...] (50 elements,
quality=192, MxCategoryOk)
The bare reference without brackets continues to return
MxCategoryCommunicationError, Detail=1003 — that's a real proxy-side
rejection, not a CLI bug. Documented separately so the matrix shows
both the working syntax and the failing one.
docs/usage.md:
- Type matrix gains a "bulk array read via []" row (✅) and a
"bare reference" row (❌, kept for reference).
- "Reading arrays" section rewritten around the two reference
shapes — `[]` for whole arrays, `[N]` for elements (1-based,
N <= NumElements).
- Adds a "Discovering array length" note pointing at grdb's
attributes.sql array_dimension column.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
217 lines
12 KiB
Markdown
217 lines
12 KiB
Markdown
# mxa — usage
|
||
|
||
Read, write, and subscribe to AVEVA System Platform tags via MxAccess. The CLI runs in-process: each invocation registers an `LMXProxyServer`, executes, and unregisters cleanly. Errors carry the underlying `MxStatusCategory` so an agent can decide whether the failure is transient (Pending), configurational, or operational.
|
||
|
||
## Common notes
|
||
|
||
- **Tag references** are full attribute paths: `<ObjectName>.<AttributeName>` (e.g. `TestMachine_001.Speed`). For `Galaxy:` references, follow the convention used in InTouch / Object Viewer.
|
||
- **`--client <name>`** sets the client name passed to MxAccess `Register()`. Defaults to `mxa`. Most install logs key on this string.
|
||
- **Timeouts are per-call.** They control how long the CLI waits for a `OnDataChange` (read) or `OnWriteComplete` (write). The default is 5 seconds.
|
||
- **First-event latency.** LMX has to resolve the reference and bind to the hosting engine on each fresh client connection. Empirically the first `OnDataChange` arrives **3-8 seconds** after `Advise()`. Set timeouts and `subscribe --seconds` accordingly: a 3-second `read` may legitimately time out on first contact, then succeed on the next try because LMX has cached the binding.
|
||
- **Subsequent events are fast.** Once a tag is bound, value-change updates propagate within ~100 ms.
|
||
- **Exit codes:** `0` on success, `1` if any operation timed out or returned a non-Ok / non-Pending `MxStatusCategory`, `2` on argument-validation errors.
|
||
|
||
## `mxa info`
|
||
|
||
Print the loaded `ArchestrA.MxAccess` assembly identity, supported `--type` values, and the full `MxStatusCategory` enum. No tag access.
|
||
|
||
```powershell
|
||
mxa info
|
||
```
|
||
|
||
## `mxa read <tag> [<tag>...]`
|
||
|
||
Reads one or more tags by briefly subscribing and capturing the first `OnDataChange` per tag.
|
||
|
||
| Option | Default | Notes |
|
||
| --- | --- | --- |
|
||
| `-t`, `--timeout <seconds>` | `5` | Per-tag timeout. Tags that don't deliver a `DataChange` within the window are reported with `error: timeout`. |
|
||
| `--client <name>` | `mxa` | Passed to `Register()`. |
|
||
| `--llm-json` | off | Emit the JSON envelope. |
|
||
|
||
Examples:
|
||
|
||
```powershell
|
||
mxa read TestMachine_001.Speed
|
||
mxa read TestMachine_001.Speed Reactor1.Level -t 3
|
||
mxa read TestMachine_001.Speed Reactor1.Level --llm-json
|
||
```
|
||
|
||
LLM-JSON envelope:
|
||
|
||
```json
|
||
{
|
||
"query": { "command": "read", "tags": ["TestMachine_001.Speed"], "timeout_s": 5.0, "client": "mxa" },
|
||
"ok": true,
|
||
"results": [
|
||
{
|
||
"tag": "TestMachine_001.Speed",
|
||
"ok": true,
|
||
"value": 1234.5,
|
||
"quality": 192,
|
||
"timestamp": "2026-05-03T19:42:18.001",
|
||
"statuses": [
|
||
{ "Success": 0, "Category": "MxCategoryOk", "DetectedBy": "MxSourceRespondingAutomationObject", "Detail": 0 }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## `mxa write <tag> <value>`
|
||
|
||
Writes one value to one tag and waits for `OnWriteComplete`.
|
||
|
||
| Option | Default | Notes |
|
||
| --- | --- | --- |
|
||
| `--type <kind>` | inferred | Force the .NET type used for the boxed value. One of `bool`, `byte`, `short`, `int`, `long`, `float`, `double`, `string`, `datetime`. |
|
||
| `-t`, `--timeout <seconds>` | `5` | How long to wait for `OnWriteComplete`. |
|
||
| `--user-id <int>` | `0` | Authenticated user id. `0` is unauthenticated; secured attributes will reject. |
|
||
| `--client <name>` | `mxa` | Passed to `Register()`. |
|
||
| `--llm-json` | off | Emit the JSON envelope. |
|
||
|
||
Type inference rules (when `--type` is not set): `true`/`false`/`yes`/`no`/`on`/`off`/`1`/`0` → bool; pure integer → `int` (then `long`); decimals → `double`; everything else → `string`.
|
||
|
||
Examples:
|
||
|
||
```powershell
|
||
mxa write TestMachine_001.Setpoint 42.5 --type double
|
||
mxa write TestMachine_001.RunFlag true
|
||
mxa write TestMachine_001.Label "Hello world"
|
||
mxa write Reactor1.Setpoint 100 --type int -t 10 --llm-json
|
||
```
|
||
|
||
The same JSON envelope shape as `read`, with `results[0]` containing `{ tag, ok, error?, statuses }`. No `value`/`quality`/`timestamp` on the write result — consult a follow-up `mxa read` to confirm.
|
||
|
||
## `mxa subscribe <tag> [<tag>...]`
|
||
|
||
Streams `OnDataChange` events for a duration.
|
||
|
||
| Option | Default | Notes |
|
||
| --- | --- | --- |
|
||
| `-s`, `--seconds <seconds>` | `10` | Wall-clock duration of the subscription. |
|
||
| `--max <int>` | `1000` | Hard cap on emitted events. |
|
||
| `--client <name>` | `mxa` | Passed to `Register()`. |
|
||
| `--llm-json` | off | **JSON Lines** mode — one JSON object per line, no outer envelope. |
|
||
|
||
Human output:
|
||
|
||
```text
|
||
[INFO] Subscribed to 1 tag(s). Streaming for 30.0s. Ctrl-C to stop early.
|
||
[19:42:18.001] [OK ] TestMachine_001.Speed = 1234.5 (q=192)
|
||
[19:42:19.002] [OK ] TestMachine_001.Speed = 1245.7 (q=192)
|
||
...
|
||
[INFO] 30 event(s) emitted; subscription closed.
|
||
```
|
||
|
||
LLM-JSON output (one event per line, no surrounding `[ ... ]`):
|
||
|
||
```jsonl
|
||
{"tag":"TestMachine_001.Speed","ok":true,"value":1234.5,"quality":192,"timestamp":"2026-05-03T19:42:18.001","statuses":[{...}]}
|
||
{"tag":"TestMachine_001.Speed","ok":true,"value":1245.7,"quality":192,"timestamp":"2026-05-03T19:42:19.002","statuses":[{...}]}
|
||
```
|
||
|
||
JSON Lines lets a downstream consumer parse events incrementally rather than buffering the whole stream — the right shape for indefinite or long-running subscriptions.
|
||
|
||
## Type support matrix
|
||
|
||
Verified end-to-end against the live `ZB` galaxy (System Platform 2017 Express, MxAccess `3.2.0.0`). Each row records what the wire shape looks like in the JSON envelope.
|
||
|
||
| `MxDataType` | Read | Write | JSON shape | Notes |
|
||
| --- | :---: | :---: | --- | --- |
|
||
| `MxBoolean` | ✅ | ✅ | JSON `true` / `false` | `--type bool` accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off`. |
|
||
| `MxInteger` (Int32) | ✅ | ✅ | JSON number | `--type int`. Up-cast to `long` if it overflows `int.MaxValue`. |
|
||
| `MxFloat` (single) | ✅ | ⚠️ | JSON number | Read verified on `DevPlatform.CPULoad` family. Write requires a writeable Float UDA — none in the test galaxy, but `--type float` is wired. |
|
||
| `MxDouble` | ❓ | ❓ | JSON number | No accessible Double instance in the test galaxy. Wiring is identical to Float; expected to work. |
|
||
| `MxString` | ✅ | ✅ | JSON string | Default inferred type for non-numeric values. |
|
||
| `MxTime` (DateTime) | ✅ | ⚠️ | JSON string `"YYYY-MM-DDTHH:mm:ss"` | Read verified on `DevPlatform.SystemStartupTime`. Write via `--type datetime` accepts ISO-8601. |
|
||
| `MxElapsedTime` | ❓ | ❓ | JSON number (seconds) | No accessible instance in the test galaxy. |
|
||
| `MxReferenceType` | ✅ | – | JSON string (target object's `Tagname`) | E.g. `TestChildObject.Container` → `"DevTestObject"`. Writing references is not exposed by the CLI. |
|
||
| `MxQualifiedEnum` (13) | ❓ | – | (likely string) | No accessible instance. |
|
||
| `MxQualifiedStruct` (14) | – | – | – | Access via dotted member names: `<obj>.<struct>.<field>`. |
|
||
| `MxInternationalizedString` (15) | ❓ | ❓ | (likely string) | No accessible instance. |
|
||
| `MxBigString` (16) | ❓ | ❓ | JSON string | No accessible instance. |
|
||
| **Array (any type), bulk read** | ✅ | – | JSON array of element type | Reference syntax `<obj>.<arrayAttr>[]` — **empty square brackets**. Verified on `MESReceiver_001.MoveInPartNumbers[]` (String[50]) and `MoveOutWorkOrderNumbers[]`. Returns the entire array as a single value with `MxCategoryOk`. |
|
||
| **Array (bare reference)** | ❌ | – | — | The plain `<obj>.<arrayAttr>` (no brackets) returns `MxCategoryCommunicationError, Detail=1003`. Use `[]` instead. |
|
||
| **Array element by index** | ✅ | ✅ | scalar of element type | Reference syntax `<obj>.<arrayAttr>[<n>]`. **1-based**, runs from `[1]` to `[NumElements]`. `[0]` is invalid. |
|
||
|
||
Legend: ✅ verified live, ⚠️ wiring present but no live instance to write, ❓ wiring present but no live instance found, ❌ not supported by MxAccess at this layer, – not applicable.
|
||
|
||
To test write support for a type, use [`../../grdb/`](../../grdb/) to find a deployed instance whose `dynamic_attribute.mx_data_type` matches and whose `mx_attribute_category` is in `(2-11, 24)` ([Writeable_*](../../aot/dev-guide/appendix-f-attribute-categories.md) family).
|
||
|
||
## Errors and statuses
|
||
|
||
Every `result` carries a `statuses` array — the elements of the COM `MXSTATUS_PROXY[]` MxAccess passes back. Field names match the C# struct exactly:
|
||
|
||
| Field | Type | Meaning |
|
||
| --- | --- | --- |
|
||
| `Success` | int16 | 0 = Ok, non-zero = error code |
|
||
| `Category` | enum | `MxCategoryOk`, `MxCategoryPending`, `MxCategoryWarning`, `MxCategoryCommunicationError`, `MxCategoryConfigurationError`, `MxCategoryOperationalError`, `MxCategorySecurityError`, `MxCategorySoftwareError`, `MxCategoryOtherError`, `MxStatusCategoryUnknown` |
|
||
| `DetectedBy` | enum | `MxSourceRequestingLmx`, `MxSourceRespondingLmx`, `MxSourceRequestingNmx`, `MxSourceRespondingNmx`, `MxSourceRequestingAutomationObject`, `MxSourceRespondingAutomationObject`, `MxSourceUnknown` |
|
||
| `Detail` | int16 | Additional error-code detail |
|
||
|
||
A result is considered `ok` only if every `statuses` element has `Category in (MxCategoryOk, MxCategoryPending)`.
|
||
|
||
Common failure shapes:
|
||
|
||
- **`Category: MxCategoryConfigurationError`** — usually a typo'd reference or the attribute doesn't exist on the deployed instance. Sanity-check via `graccesscli object snapshot`.
|
||
- **`Category: MxCategoryCommunicationError`** — engine isn't running, object is OffScan, or LMX can't reach the platform hosting the object.
|
||
- **`Category: MxCategorySecurityError`** — secured attribute, `--user-id 0`. Use `WriteSecured` semantics (not yet exposed by this CLI) or target a `Writeable_USC_*` attribute.
|
||
- **Timeout** — most likely the tag is genuinely silent (no value updates) or the reference is wrong. With `--llm-json` you'll see `"error": "timeout"` and an empty `statuses`.
|
||
|
||
## Reading arrays
|
||
|
||
MxAccess accepts **two** reference forms for arrays — pick by what you need:
|
||
|
||
### Whole array — `<obj>.<arrayAttr>[]` (empty brackets)
|
||
|
||
```powershell
|
||
mxa read 'MESReceiver_001.MoveInPartNumbers[]' --llm-json
|
||
```
|
||
|
||
Returns the full array as a single JSON value:
|
||
|
||
```jsonc
|
||
{
|
||
"tag": "MESReceiver_001.MoveInPartNumbers[]",
|
||
"ok": true,
|
||
"value": ["", "11111", "", "", /* ... 50 elements total ... */],
|
||
"quality": 192,
|
||
"statuses": [{"Success":-1,"Category":"MxCategoryOk", ...}]
|
||
}
|
||
```
|
||
|
||
The array is fixed-length (sized at deploy time per the template's `array_dimension`). Empty string elements are unset slots, not gaps.
|
||
|
||
### Single element — `<obj>.<arrayAttr>[N]`
|
||
|
||
```powershell
|
||
mxa read 'MESReceiver_001.MoveInPartNumbers[2]' --llm-json
|
||
```
|
||
|
||
Indices are **1-based**: `[1]` is the first element, `[NumElements]` is the last. `[0]` is invalid. Single-element reads are also writeable: `mxa write '<obj>.<attr>[N]' <value>`.
|
||
|
||
### What does *not* work
|
||
|
||
```powershell
|
||
mxa read 'MESReceiver_001.MoveInPartNumbers' # bare ref, no brackets
|
||
# → MxCategoryCommunicationError, Detail=1003
|
||
```
|
||
|
||
The plain reference (no `[]`, no `[N]`) is rejected by the proxy. Always include the brackets — empty for whole-array, indexed for element.
|
||
|
||
### Discovering array length
|
||
|
||
The CLI doesn't (yet) auto-discover element count. Two ways to find it:
|
||
|
||
1. Read with `[]` and count the returned values.
|
||
2. Query the Galaxy Repository's [`../../grdb/queries/attributes.sql`](../../grdb/queries/attributes.sql) — the `array_dimension` column reports the configured size from the template.
|
||
|
||
## Picking a tag for a smoke test
|
||
|
||
If the live galaxy is not familiar:
|
||
|
||
1. Connect to the Galaxy Repository SQL — see [`../../grdb/connectioninfo.md`](../../grdb/connectioninfo.md).
|
||
2. Find a deployed instance with a writeable UDA — [`../../grdb/queries/attributes.sql`](../../grdb/queries/attributes.sql) lists user-defined attributes with their data type. Filter on a `Writeable_*` security classification (see [`../../aot/dev-guide/appendix-e-security-classifications.md`](../../aot/dev-guide/appendix-e-security-classifications.md)).
|
||
3. The reference for MxAccess is `<InstanceName>.<AttributeName>`.
|