Files
mxaccess/docs/MXAccess-Public-API.md
T
Joseph Doherty fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Initial project state: .NET reference, design, Rust port (M0+M1), evidence
Layout:
- src/                    .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
                          MxAsbClient, probes, tests, harnesses. Executable spec.
- design/                 Architectural plan for the Rust port (M0–M6), error
                          model, protocol invariants, risks (R1–R16), adversarial
                          review log (review.md).
- rust/                   Rust workspace. M0 skeleton + M1 codec parity.
                          mxaccess-codec: 215 unit tests + 2 cross-implementation
                          parity tests (byte-identical against .NET reference).
                          Other crates are M0 stubs awaiting M2+.
- captures/               Frida + netsh + pcap evidence per CLAUDE.md
                          ("captures are evidence, not throwaway logs").
- analysis/               Decompiled C# (frida/proxy/decompiled-*),
                          Ghidra exports for native DLLs (`exports/` only —
                          working state at `projects/` and AVEVA's input
                          binaries at `input/` are gitignored).
- docs/                   Reverse-engineering reference docs.
- tools/                  Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
                          Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/      Rust CI: fmt + build + test + clippy on Windows.
- LICENSE                 MIT (Joseph Doherty, 2026).

Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly

Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 06:21:00 -04:00

524 lines
24 KiB
Markdown

# MXAccess public API surface
Source of truth: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll`
Assembly identity:
- Name: `ArchestrA.MxAccess`
- Version: `3.2.0.0`
- Public key token: `23106a86e706d0ae`
- File product string: `Assembly imported from type library 'LMXPROXYLib'.`
- Processor architecture in metadata: `None`; practical use is still x86 because the COM class is a 32-bit in-proc server.
## COM class
`ArchestrA.MxAccess.LMXProxyServerClass`
- CLSID: `{C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}`
- ProgID: `LMXProxy.LMXProxyServer.1`
- Version-independent ProgID: `LMXProxy.LMXProxyServer`
- Registered server: `C:\Program Files (x86)\ArchestrA\Framework\Bin\LmxProxy.dll`
- Registry location: `HKCR\Wow6432Node\CLSID\{C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}`
- Threading model: `Apartment`
Because the server is under `Wow6432Node` and registered as an in-process DLL,
64-bit callers cannot instantiate it directly.
## Interface lineage
`ILMXProxyServer5` extends prior versions without changing the older method
signatures:
- `ILMXProxyServer` - original register/add/advise/write/auth API.
- `ILMXProxyServer2` - adds `ArchestrAUserToId`.
- `ILMXProxyServer3` - adds `AddItem2`.
- `ILMXProxyServer4` - adds `Write2`, `WriteSecured2`, `Suspend`, `Activate`, `AdviseSupervisory`.
- `ILMXProxyServer5` - adds buffered item support.
Interface GUIDs:
- `ILMXProxyServer`: `{CCE67FB7-EAFD-4367-9212-617043BF126D}`
- `ILMXProxyServer2`: `{020A8A87-69C5-497F-A893-B629E669FBFF}`
- `ILMXProxyServer3`: `{57D006B6-F25E-4654-A81E-BCBAFD60FE59}`
- `ILMXProxyServer4`: `{9DC0D5C1-9371-4E84-86F8-7091D316A66C}`
- `ILMXProxyServer5`: `{ECEFF506-A752-46E3-9E31-0A8E257C9926}`
## Methods
| Method | Purpose inferred from API and current usage |
| --- | --- |
| `Register(string pClientName) -> int` | Opens a client session and returns an LMX server handle. |
| `Unregister(int hLMXServerHandle)` | Closes the client session. |
| `AddItem(int hLMXServerHandle, string strItemDef) -> int` | Resolves/registers an item reference and returns an item handle. |
| `RemoveItem(int hLMXServerHandle, int hItem)` | Releases an item handle. |
| `Advise(int hLMXServerHandle, int hItem)` | Subscribes to item updates. |
| `UnAdvise(int hLMXServerHandle, int hItem)` | Cancels item updates. |
| `Write(int hLMXServerHandle, int hItem, object pItemValue, int userId)` | Writes a value. In current code `userId` is used as security classification. |
| `WriteSecured(int hLMXServerHandle, int hItem, int currentUserId, int verifierUserId, object pItemValue)` | Secured/verified write path. |
| `AuthenticateUser(int hLMXServerHandle, string verifyUser, string verifyUserPsw) -> int` | Authenticates a user and returns a numeric id. |
| `ArchestrAUserToId(int hLMXServerHandle, string userIdGuid) -> int` | Maps ArchestrA user GUID to integer id. |
| `AddItem2(int hLMXServerHandle, string strItemDef, string strItemCtxt) -> int` | Adds an item with a separate context string. |
| `Write2(int hLMXServerHandle, int hItem, object pItemValue, object pItemTime, int userId)` | Timestamped write. |
| `WriteSecured2(...)` | Timestamped secured/verified write. |
| `Suspend(int hLMXServerHandle, int hItem, out MxStatus status)` | Suspends an item/reference. |
| `Activate(int hLMXServerHandle, int hItem, out MxStatus status)` | Activates a suspended item/reference. |
| `AdviseSupervisory(int hLMXServerHandle, int hItem)` | Supervisory subscription mode. This is what the existing OPC UA bridge uses. |
| `AddBufferedItem(int hLMXServerHandle, string strItemDef, string strItemCtxt) -> int` | Adds a buffered item. |
| `SetBufferedUpdateInterval(int hLMXServerHandle, int updateInterval)` | Sets buffered update cadence. |
Reflection over the installed interop assembly confirms
`LMXProxyServerClass` exposes these 18 public methods plus the four public COM
event families: `OnDataChange`, `OnWriteComplete`, `OperationComplete`, and
`OnBufferedDataChange`. `Write2` and `WriteSecured2` expose their timestamp
argument as `object`/VARIANT. The .NET 10 compatibility facade keeps typed
`DateTime` overloads and also exposes object timestamp overloads for
API-shape parity.
## Secured write capture status
The current test node still does not produce a successful dedicated
`WriteSecured` wire path:
- `captures\036-frida-write-secured-test-int`: `WriteSecured` returned
`0x80004021` before any NMX `0x37` write body was emitted.
- `captures\038-frida-write-secured-protectedvalue`: `WriteSecured` returned
`0x80004021` before any NMX `0x37` write body was emitted.
- `captures\039-frida-write-secured-verified-protectedvalue1`: `WriteSecured`
returned `0x80004021` before any NMX `0x37` write body was emitted.
- `captures\111-frida-write-secured-auth-protectedvalue` and
`captures\112-frida-write-secured-auth-verified-protectedvalue1`:
`AuthenticateUser` succeeded first, but `WriteSecured` still returned
`0x80004021` before any value-bearing body.
- `captures\037-frida-write-secured2-test-int`: `WriteSecured2` returned
`0x80070057` before any NMX write body was emitted.
Headless Ghidra output in
`analysis\ghidra\exports\LmxProxy.dll.write-secured-decompile.md` shows why
these paths split: `WriteSecured` has an extra item-record flag check that can
return `0x80004021`; `WriteSecured2` skips that check and proceeds through the
authenticated timestamped writer when the user handles are mapped.
Normal `Write` calls to the secured/verified public test attributes did emit
ordinary `0x37` write bodies and succeeded in captures `040` and `041`. That
means the managed implementation supports the successful observed path through
normal `Write`.
Authenticated `WriteSecured2` now has successful captures and a managed encoder.
Captures `113`-`116` show command `0x38` over transfer kind `Write` (`3`) for
the boolean secured/verified tags in this GR. The body carries the current-user
authenticator token before the client name and the verifier token after the
client name. Live .NET 10 x64 probes succeeded for
`TestMachine_001.ProtectedValue` with no verifier and
`TestMachine_001.ProtectedValue1` with verifier handle `1`, both through the
low-level session API and through the handle-based compatibility facade. The
facade does not synthesize `OnWriteComplete` for this path because the native
successful captures did not show a public write-complete event. Capture `117`
adds authenticated `WriteSecured2` for integer `TestChildObject.TestInt`; it
uses the same generic timestamped-prefix rule, so the managed encoder is no
longer bool-only. Live managed probes also succeeded for float, double, string,
datetime, and the scalar-array value kinds. Native captures beyond bool and int
would still be useful as additional golden fixtures, but the managed encoder is
now generic over the existing timestamped write-body support.
## User mapping capture status
The x86 `MxTraceHarness` `user-map` scenario directly calls
`CLMXProxyServer.ArchestrAUserToId`. On this test node, it does not return the
Galaxy Repository `user_profile_id`:
| Capture | Input GUID | MXAccess return |
| --- | --- | ---: |
| `captures\mxaccess-user-map-administrator.log` | `9222FBBA-53F4-457E-8B37-C93A9A250B4A` | `1` |
| `captures\mxaccess-user-map-systemengineer.log` | `626A355F-737E-45F8-9740-43372220DEAB` | `1` |
| `captures\mxaccess-user-map-defaultuser.log` | `F4A9B907-6E72-48AE-83B5-BBDE918C890F` | `1` |
| `captures\mxaccess-user-map-invalid.log` | `00000000-0000-0000-0000-000000000000` | `1` |
The managed compatibility layer mirrors this observed behavior for
`ArchestrAUserToId`. GR profile lookup remains available separately through
`GalaxyRepositoryUserResolver`.
## Authentication capture status
`captures\mxaccess-authenticate-user-administrator-empty.log` shows
`AuthenticateUser(session, "Administrator", "")` returning user ID `1`.
Frida captures `087-frida-authenticate-administrator-empty` and
`088-frida-authenticate-invalid-empty` show both `Administrator` and
`DefinitelyNotAUser` returning `S_OK` and user ID `1` when the password is
empty on this dev node. The Frida hook records only password length, not
password content.
Headless Ghidra decompile `analysis\ghidra\exports\LmxProxy.dll.auth-decompile.md`
shows `AuthenticateUser` calling the underlying user-authenticator object and,
when it receives a security token, incrementing a per-session user counter and
mapping that generated integer to the token GUID. This matches
`ArchestrAUserToId`: the public return value is a session-local MXAccess handle,
not the GR `dbo.user_profile.user_profile_id`.
`MxNativeCompatibilityServer.AuthenticateUser` now mirrors the observed dev-node
behavior by validating the server handle and returning a session-local user
handle. It deliberately ignores and does not retain password material. A
security-enabled Galaxy still needs dedicated captures before password/hash
verification can be implemented safely.
## AddItem2 context capture status
`captures\mxaccess-additem2-testint-context.log` shows
`CLMXProxyServer.AddItem2(session, "TestInt", "TestChildObject")` succeeding and
returning item handle `1`. That confirms the simple relative-reference form used
by `MxNativeCompatibilityServer.AddItem2`: combine context and item definition
as `TestChildObject.TestInt` before GR resolution. More complex context strings
still need captures.
## Advise capture status
`captures\mxaccess-plain-advise-testint.log` shows the public
`CLMXProxyServer.Advise(session, item)` method succeeding for
`TestChildObject.TestInt`. Frida capture `099-frida-plain-advise-testint`
captures the lower-level body: plain `Advise` sends the same item-control
`0x1f` body shape as the earlier `AdviseSupervisory` capture for the same
scalar tag, including the same trailing option value `3`. The managed
compatibility layer therefore routes both public methods through the same
decoded subscription body for the observed scalar path.
## Suspend/Activate capture status
`captures\mxaccess-suspend-testint.log` and
`captures\mxaccess-activate-testint.log` show the public MXAccess methods
throwing `0x80070057` for `TestChildObject.TestInt` before a usable `MxStatus`
is returned. These captures establish that the test integer attribute is not a
valid suspend/activate scenario. A successful capture against the correct item
class is still required before implementing native NMX bodies for these methods.
## Buffered item capture status
The x86 harness now covers the public buffered APIs:
- `captures\mxaccess-set-buffered-interval-1000.log` shows
`SetBufferedUpdateInterval(session, 1000)` succeeding.
- `captures\mxaccess-add-buffered-testint-context.log` shows
`AddBufferedItem(session, "TestInt", "TestChildObject")` succeeding and
returning item handle `1`.
- `captures\mxaccess-add-buffered-write-testint-context.log` shows that using
the buffered item handle with normal `Write` throws `0x80070057`.
- `captures\079-frida-add-buffered-advise-testint` and
`captures\080-frida-buffered-external-write-testint` show the advised
buffered registration body. MXAccess does not send the normal `0x1f`
item-control advise for the buffered handle. It sends an item-control `0x10`
reference registration for `TestInt.property(buffer)` in context
`TestChildObject`, wrapped in a `MessageKind=2` transfer envelope.
- `captures\121-frida-buffered-history-testhistoryvalue-context` and
`captures\122-frida-buffered-history-testhistoryvalue-plainadvise` repeat the
buffered registration against GR-confirmed historized integer attribute
`TestMachine_001.TestHistoryValue`. Both supervisory and plain advise forms
emit the same context-bearing `0x10`/`0x11` registration/result shape for
`TestHistoryValue.property(buffer)` in context `TestMachine_001`.
Separate writer-session writes succeed and normal writer data callbacks are
observed, but native MXAccess still does not enter `Fire_OnBufferedDataChange`
on this VM.
- `captures\085-frida-subscribe-property-buffer` and
`captures\086-frida-write-property-buffer` show that adding the literal item
`TestChildObject.TestInt.property(buffer)` does not enter the public
`AddBufferedItem` helper. It follows the normal add/advise path, sends the
same item-control `0x10` reference-registration body with an empty item
context, and receives an NMX registration-result frame carrying the runtime
internal-error text for `TestInt.property(buffer)`. Normal `Write` against
the literal handle returns from `CLMXProxyServer.Write` without producing an
observed buffered callback.
- Ghidra decompile of `LmxProxy.dll` function `1001121d` confirms the public
`AddBufferedItem` implementation allocates a BSTR copy of `strItemDef`,
appends `.property(buffer)`, calls the normal add-item implementation, and
marks the resulting item record as buffered. Function `1000fc80` confirms
`SetBufferedUpdateInterval` rejects intervals below `1` and stores the
interval as `(milliseconds + 99) / 100`, effectively rounding up to 100 ms
ticks.
- Ghidra decompile of `100163c0` confirms `OnBufferedDataChange` is fired
through the `_ILMXProxyServerEvents2` connection point with seven arguments:
server handle, item handle, data type, value variant, quality variant,
timestamp variant, and status array.
- Headless xref/decompile output shows `Fire_OnBufferedDataChange` is reached
from the same native `OnDataChange callback received` method used for normal
data changes. The callback looks up the item record and branches on the
buffered item flag: non-buffered items fire `OnDataChange`; buffered items
convert the callback value into value, quality, and timestamp SAFEARRAY
variants before firing `OnBufferedDataChange`.
- The buffered value conversion helper maps MX buffered element type `1` to a
`VT_BOOL` value array, `2` to `VT_I4`, `3` to `VT_R4`, `4` to `VT_R8`, `5`
to `VT_BSTR`, and `6`/`7` to `VT_UI8` FILETIME-style values. Quality is
emitted as a `VT_I2` SAFEARRAY, timestamp as a `VT_UI8` SAFEARRAY, and the
status argument uses the normal `MXSTATUS_PROXY[]` SAFEARRAY.
No live `OnBufferedDataChange` payload has been observed yet. A source/runtime
condition that actually delivers buffered sample batches is still needed to
validate the wire body and multi-sample parser. The managed library now
implements the decoded add/registration surface, including context-bearing
buffered registrations, and routes parsed buffered callbacks to a separate
managed buffered event instead of the normal data-change event.
## Events
Event source interfaces:
- `_ILMXProxyServerEvents`: `{848299B6-DD61-4A0D-A304-3947A564B89C}`
- `_ILMXProxyServerEvents2`: `{C70A6FC4-09EF-4F31-8874-A049FEE87A95}`
Events:
```csharp
void OnDataChange(
int hLMXServerHandle,
int phItemHandle,
object pvItemValue,
int pwItemQuality,
object pftItemTimeStamp,
ref MXSTATUS_PROXY[] pVars);
void OnWriteComplete(
int hLMXServerHandle,
int phItemHandle,
ref MXSTATUS_PROXY[] pVars);
void OperationComplete(
int hLMXServerHandle,
int phItemHandle,
ref MXSTATUS_PROXY[] pVars);
void OnBufferedDataChange(
int hLMXServerHandle,
int phItemHandle,
MxDataType dtDataType,
object pvItemValue,
object pwItemQuality,
object pftItemTimeStamp,
ref MXSTATUS_PROXY[] pVars);
```
Ghidra decompile of `LmxProxy.dll` event helpers confirms that
`Fire_OnWriteComplete` and `Fire_OperationComplete` both construct a three
element `VARIANT` argument array containing server handle, item handle, and one
`MXSTATUS_PROXY` SAFEARRAY. `Fire_OnWriteComplete` dispatches event ID `2`;
`Fire_OperationComplete` dispatches event ID `3`. In the capture set, successful
writes raise only `OnWriteComplete`; no `mx.event.operation-complete` line has
been observed yet.
Headless Ghidra xref/decompile output in
`analysis\ghidra\exports\LmxProxy.dll.event-xrefs.md` and
`analysis\ghidra\exports\LmxProxy.dll.event-callers-decompile.md` narrows the
source further: `Fire_OnWriteComplete` is reached from
`CUserConnectionCallback::OnSetAttributeResult`, while `Fire_OperationComplete`
is reached from `CUserConnectionCallback::OperationComplete`. They are distinct
callback paths even though their COM event payload shape is the same.
The installed interop assemblies expose the lower-level callback as
`IMxCallback2.OperationComplete(int lCallbackId, ref MxStatus, string)`. They
also expose `IDataConsumer.ActivateSuspend` and `ProcessActivateSuspend2`, whose
response type is `ItemActiveResponse`. That makes the DataConsumer
activate/suspend completion path the strongest remaining native trigger
candidate. The public `LMXProxyServerClass.Suspend` and `Activate` methods
tested in captures `118` and `119` instead decompile to direct
`IMxScanOnDemand` calls and did not enter
`CUserConnectionCallback.OperationComplete` on this node.
`aaMxDataConsumer.dll` is registered separately as `MxDataConsumer Class`
(`{85209FB2-0BA1-4594-BBC4-59D3DDAB823D}`) and exposes the same
`IDataConsumer` activate/suspend methods through its type library. A targeted
x86 probe can instantiate it and call those methods, but the standalone object
currently reports `IsConnected(namespaceId)=0` and
`ProcessActivateSuspend2` returns `0x8007139F`. That means the COM surface is
reachable, but a DataConsumer/DataClient bootstrap step is still missing before
it can prove the public `OperationComplete` trigger.
The mixed-mode `aaMxDataConsumer.dll` decompile shows that bootstrap should
ultimately route through managed ASB IData proxies: `CDataClientCLI` owns a
`DataClientProxy`, starts an auto-connect worker, and passes the namespace
string as an ASB access name to
`IDataProxySelector.SelectProxyForLatestEndpoint`. `ASBIDataV2Adapter.dll`
contains that selector; it looks for `IASBIDataV2` endpoints under
`domainname/<accessName>/global` before falling back to IData V1. A new x64
managed probe found and connected to the live `IASBIDataV2` endpoint with
access name `ZB`, then successfully called `PublishWriteComplete`. This does
not prove `OperationComplete` yet, but it proves the relevant data-service
route can be reached from managed x64 code without MXAccess or COM.
The same x64 ASB probe now proves the register/read/write/complete flow for
`TestChildObject.TestInt`. `RegisterItems` and `Read` succeed, ASB type `4`
decodes as `Int32`, `Write(401)` is accepted with per-item
`OperationWouldBlock`, the next read returns `401`, and
`PublishWriteComplete` returns the submitted write handle with final per-item
success. That gives a concrete managed completion queue to model for ASB-native
write-complete behavior; `OperationComplete` should still not be synthesized
until an operation other than a basic write is proven to use the native
MXAccess event ID `3` path.
The same core flow is now reproduced by the pure .NET 10 x64
`src\MxAsbClient` implementation with no AVEVA assembly references. Its live
probe reads the ASB solution secret through DPAPI, performs the system-auth
handshake, reads `TestChildObject.TestInt`, writes a new integer value, reads
that value back, and decodes `PublishWriteComplete` result `0x00000020` with
the submitted write handle and final per-item success. `RegisterItems` now
matches the observed ASB startup behavior: the first immediate call after
one-way `AuthenticateMe` can return `0x00000001`, so the client retries briefly
and receives the expected item status/id once the server-side implementation is
registered. The remaining ASB-native work is API breadth: multi-item calls,
subscriptions, scalar/array type matrix, and status/error mapping.
`UnregisterItems` is now also implemented and compared against the installed
AVEVA `ASBDataV2Proxy`. Both the pure .NET 10 client and installed proxy return
global success for the unregister call and the same per-item `0x0000000B`
(`OperationFailed`) for `TestChildObject.TestInt` on this provider. That is
documented as parity with the deployed ASB provider, not a successful item-level
cleanup status.
The pure .NET 10 client also handles multi-item register/read bodies. A two-tag
probe registered and read `TestChildObject.TestInt` plus
`TestMachine_001.TestHistoryValue` in single requests; both returned per-item
success and decoded as ASB `TypeInt32`.
For write status callbacks, the public event is tied to the non-length-prefixed
5-byte operation-status body `00 00 50 80 00`. Length-prefixed completion-only
bodies are lower-level NMX status frames and did not produce public
`OnWriteComplete` events in captures `089`, `091`, `092`, or `093`, even when
the completion byte was `0x00`.
## Data types
`MxDataType`:
```text
Unknown = -1
NoData = 0
Boolean = 1
Integer = 2
Float = 3
Double = 4
String = 5
Time = 6
ElapsedTime = 7
ReferenceType = 8
StatusType = 9
Enum = 10
SecurityClassificationEnum = 11
DataQualityType = 12
QualifiedEnum = 13
QualifiedStruct = 14
InternationalizedString = 15
BigString = 16
END = 17
```
Live GR inventory on this node found deployed/configured instances of all core
scalar types plus `ElapsedTime` and `InternationalizedString`. Target references
captured for the two non-core types:
- `TestMachine_001.TestAlarm001.Alarm.TimeDeadband`: `ElapsedTime`, observed
subscription callback wire kind `0x07` with four-byte zero payload in
`captures\063-frida-subscribe-elapsed-time-deadband`.
- `TestChildObject.ShortDesc`: `InternationalizedString`, observed callback
normalizes the empty value to string wire kind `0x05` with compact payload
`04 00 00 00` in `captures\062-frida-subscribe-intl-shortdesc`.
- Non-empty GR defaults exist at `DevPlatform._EngUnitsPercent` and
`DevAppEngine.Scheduler._EngUnitsMB`; captures `064` and `065` resolved and
advised those items but did not emit a value callback during the capture
window.
Write projection for these non-core types is caller-variant based in the
observed MXAccess path. Capture `095` wrote an `Int32` to `ElapsedTime` and
emitted integer wire kind `0x02`; capture `096` wrote a `string` to
`InternationalizedString` and emitted string wire kind `0x05`. The managed
library mirrors those projections for write attempts while keeping the data
types out of generic `TryGetValueKind` classification.
`MxStatus` and `MXSTATUS_PROXY` are identical sequential structs with 4-byte
packing:
```csharp
public short success;
public MxStatusCategory category;
public MxStatusSource detectedBy;
public short detail;
```
`MxStatusCategory`:
```text
Unknown = -1
Ok = 0
Pending = 1
Warning = 2
CommunicationError = 3
ConfigurationError = 4
OperationalError = 5
SecurityError = 6
SoftwareError = 7
OtherError = 8
```
`MxStatusSource`:
```text
Unknown = -1
RequestingLmx = 0
RespondingLmx = 1
RequestingNmx = 2
RespondingNmx = 3
RequestingAutomationObject = 4
RespondingAutomationObject = 5
```
## Status detail text
`C:\Program Files (x86)\Common Files\ArchestrA\Framework\Bin\Lmx.aaDCT`
contains localized text for common status detail codes. The .NET 10 managed
model now includes the installed English entries:
| Detail | Text |
| ---: | --- |
| 16 | Request timed out |
| 17 | Platform communication error |
| 18 | Invalid platform ID |
| 19 | Invalid engine ID |
| 20 | Engine communication error |
| 21 | Invalid reference |
| 22 | No Galaxy Repository |
| 23 | Invalid object ID |
| 24 | Object signature mismatch |
| 25 | Invalid primitive ID |
| 26 | Invalid attribute ID |
| 27 | Invalid property ID |
| 28 | Index out of range |
| 29 | Data out of range |
| 30 | Incorrect data type |
| 31 | Attribute not readable |
| 32 | Attribute not writeable |
| 33 | Write access denied |
| 34 | Unknown error |
| 35 | detected by |
| 36 | Wrong data type |
| 37 | Wrong number of dimensions |
| 38 | Invalid index |
| 39 | Index out of order |
| 40 | Dimension does not exist |
| 41 | Conversion not supported |
| 42 | Unable to convert string |
| 43 | Overflow |
| 44 | Attribute signature mismatch |
| 45 | Resolving local portion of reference |
| 46 | Resolving global portion of reference |
| 47 | Nmx version mismatch |
| 48 | Nmx command not valid |
| 49 | Lmx version mismatch |
| 50 | Lmx command not valid |
| 51 | However, the object could not be put On Scan - Permission to modify "Operate" attributes is required |
| 52 | Unable to resolve reference for 'set' request because Galaxy Repository is busy performing a 'Deploy/Undeploy' operation |
| 53 | Too many outstanding pending requests to engine |
| 54 | Object Initializing |
| 55 | Engine Initializing |
| 56 | Secured Write |
| 57 | Verified Write |
| 58 | No Alarm Ack Privilege |
| 59 | Alarm Acked Already |
| 60 | User did not have the necessary permissions to write |
| 61 | Verifier did not have the necessary permissions to verify |
| 541 | Conversion to intended data type is not supported |
| 542 | Unable to convert the input string to intended data type |
| 8017 | Object must be offscan to modify attributes that have an MxSecurityConfigure security classification |