2e937228a0
Three new subcommands that take a JSONL file (or '-' for stdin) and reuse a
single MxSession across all entries. The big win is in write-batch: a
two-phase pipeline (Advise all -> drain DataChange to resolve types; Write
all -> drain WriteComplete) reduces wall time from N x (resolve + write_ack)
to ~max(resolve) + ~max(write_ack). Measured 38.2s -> 10.3s (~3.7x) for
four writes against the ZB dev galaxy; the saving grows with N.
Per-item continue-on-error: parse errors are collected line-by-line and
abort with exit 2 before any LMX session opens; runtime failures (resolve
timeout, bad references, coerce errors, write timeouts) get their own
results[] row with a typed `error` string and exit 1. Auth flags mirror
`mxa write` and are resolved once before Phase A.
Shared infra:
- Mx/JsonlInputReader.cs: lazy line reader (skips blank / '#' lines),
bare-string or {"tag":"..."} for read/sub, {"tag","value","type"?} for
write, with array-suffix consistency check at parse time.
- Mx/ValueCoercion.cs: new CoerceJToken(...) wrapper preserves the
single source of truth for type vocabulary.
Docs:
- README run examples extended for each new command.
- docs/usage.md: new "Batch input format" subsection (shared contract),
one section per command with envelope examples and a full
failure-mode table for write-batch, plus a "Batch commands -
verified live" section capturing the 2026-05-10 ZB-galaxy run and
pipelining-timing numbers.
- test-fixtures/ holds the exact JSONL files used in the verified-live
run so the doc numbers are reproducible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83 lines
5.2 KiB
Markdown
83 lines
5.2 KiB
Markdown
# mxaccesscli
|
|
|
|
A `.NET Framework 4.8 / x86` CliFx-based CLI for **reading, writing, and subscribing to AVEVA System Platform tags via MxAccess** (`ArchestrA.MxAccess.LMXProxyServerClass`). Output as scannable text or as a stable JSON envelope for LLM consumption. Built-in per-tag and per-call timeouts; structured surface of MxStatus categories on every error.
|
|
|
|
## Hard constraints
|
|
|
|
- **MxAccess is a 32-bit COM proxy.** The CLI must stay on `net48` / `x86` / `[STAThread]`. Do not retarget to .NET 10 or x64 — `LMXProxyServerClass` is registered for the WoW64 server only.
|
|
- **System Platform must be installed locally** so that `ArchestrA.MxAccess` is registered with COM and the LMX runtime is reachable. Without it `Register()` fails with a COM error.
|
|
- **MxAccess events fire on the registering apartment.** Calls **must** originate from an STA thread, which is why `Program.Main` is `[STAThread]`. If you call from a non-STA thread, events queue but never get pumped and your timeouts will always fire.
|
|
- **Reads are not synchronous in MxAccess.** "Read" is implemented as a brief subscribe → wait for first `OnDataChange` → tear down. If a tag never delivers a `DataChange` (offscan, wrong reference, security denied), the read times out — that's the correct semantic, not a CLI bug.
|
|
- **Writes need user identity for secured attributes.** Default `--user-id 0` is "unauthenticated"; secured attributes will reject. Use `--user-id <id>` after calling `AuthenticateUser()` from another path (or, for a CLI smoke test, target a non-secured `Writeable_USC_*` attribute).
|
|
|
|
## Layout
|
|
|
|
```text
|
|
mxaccesscli/
|
|
README.md this file
|
|
AGENTS.md agent rules for working in this folder
|
|
MxAccess.Cli.slnx
|
|
lib/
|
|
ArchestrA.MxAccess.dll (copied from System Platform's Framework\Bin)
|
|
src/MxAccess.Cli/
|
|
MxAccess.Cli.csproj
|
|
Program.cs [STAThread] entry point
|
|
IsExternalInit.cs net48 polyfill for C# 9 init accessors
|
|
Commands/ read, write, subscribe, info
|
|
Mx/ MxSession, MxItem, MxUpdate, ValueCoercion
|
|
Output/ LLM-JSON envelope writer
|
|
docs/
|
|
usage.md command surface, examples, JSON envelope contract
|
|
api-notes.md reverse-engineered MxAccess API reference (since AVEVA's
|
|
online docs are sparse): types, events, threading, status semantics
|
|
```
|
|
|
|
## Resource index — by task
|
|
|
|
| Task | Go to |
|
|
| --- | --- |
|
|
| Agent rules for editing this CLI | [`AGENTS.md`](AGENTS.md) |
|
|
| Run the CLI / option reference / examples | [`docs/usage.md`](docs/usage.md) |
|
|
| MxAccess API surface, threading model, MxStatus semantics | [`docs/api-notes.md`](docs/api-notes.md) |
|
|
| Open investigation: getting `User_Name` populated on Historian alarm rows | [`2026-05-03-user-attribution-investigation.md`](2026-05-03-user-attribution-investigation.md) |
|
|
| Find a writeable tag in the live galaxy (so smoke tests have a target) | [`../grdb/README.md`](../grdb/README.md) |
|
|
| Read tag values via SQL retrieval (an alternative path) | [`../histdb/README.md`](../histdb/README.md) |
|
|
|
|
### External documentation
|
|
|
|
AVEVA does not publish a single canonical MxAccess reference online. The closest official sources:
|
|
|
|
- `https://docs.aveva.com/` — search "MxAccess", "LMXProxyServer", "Object Viewer". Coverage is partial and depends on which product portal you land on.
|
|
- AVEVA Knowledge Base (subscriber-only): TID-based articles on `LMX` registration, secured writes, and `WriteSecured2`.
|
|
- The shipped `MXAccess32.tlb` type library at `C:\Program Files (x86)\ArchestrA\Framework\Bin\` is the most authoritative source for method signatures. [`docs/api-notes.md`](docs/api-notes.md) summarizes what it exposes.
|
|
|
|
## Build & run
|
|
|
|
```powershell
|
|
dotnet build src/MxAccess.Cli/MxAccess.Cli.csproj -p:Platform=x86 -c Release
|
|
|
|
# Read two tags with a 3-second timeout, JSON envelope:
|
|
dotnet run --project src/MxAccess.Cli/MxAccess.Cli.csproj -- read TestMachine_001.Speed Reactor1.Level -t 3 --llm-json
|
|
|
|
# Write a value with explicit type:
|
|
dotnet run --project src/MxAccess.Cli/MxAccess.Cli.csproj -- write TestMachine_001.Setpoint 42.5 --type double
|
|
|
|
# Subscribe to a tag for 30 seconds, JSON Lines for streaming:
|
|
dotnet run --project src/MxAccess.Cli/MxAccess.Cli.csproj -- subscribe TestMachine_001.Speed -s 30 --llm-json
|
|
|
|
# Batch read from a JSONL file (one tag string or {"tag":"..."} per line):
|
|
mxa read-batch tags.jsonl --llm-json
|
|
|
|
# Batch write {tag,value,type?} pairs piped from stdin:
|
|
echo '{"tag":"TM.Setpoint","value":42.5,"type":"double"}' | mxa write-batch - --llm-json
|
|
|
|
# Batch subscribe from a JSONL file, streaming for 30s:
|
|
mxa subscribe-batch tags.jsonl -s 30 --llm-json
|
|
```
|
|
|
|
The built executable is `bin\x86\Release\net48\mxa.exe`. Drop on `PATH` and use `mxa read ...`.
|
|
|
|
## Maintenance
|
|
|
|
This README follows the doctrine in [`../DOCS-GUIDE.md`](../DOCS-GUIDE.md). When you add a command, an option, or a new field on the JSON envelope, update [`docs/usage.md`](docs/usage.md) in the same change. Update [`docs/api-notes.md`](docs/api-notes.md) only if the underlying MxAccess assembly changes (different System Platform version, etc.). The root [`../CLAUDE.md`](../CLAUDE.md) carries one row pointing at this README — it should not need to change unless the tool's task surface changes.
|