From 8c095b9a563e0805b012d526f8955594f890902d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 18 Oct 2026 15:11:11 -0400 Subject: [PATCH] Record the managed GUID alarm-ack live result The 2026-10-18 live test on the dev rig showed the opt-in managed ack path cannot work as built: AlarmClient.RegisterConsumer returns -65 alongside the worker's wnwrap reader (the worker then silently falls back to wnwrap), the managed client does not recognise the wnwrap GUID even standalone, and the gateway's GUID arm sends empty identity fields. Replace the docs' claim that the path works with the measured results and tell operators to leave MXGATEWAY_USE_MANAGED_ALARM_ACK unset. Co-Authored-By: Claude Opus 5.5 --- docs/AlarmClientDiscovery.md | 65 +++++++++++++++++++++++++----------- docs/GatewayConfiguration.md | 11 +++--- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/docs/AlarmClientDiscovery.md b/docs/AlarmClientDiscovery.md index 7878f81..1c040ce 100644 --- a/docs/AlarmClientDiscovery.md +++ b/docs/AlarmClientDiscovery.md @@ -793,27 +793,54 @@ in the codebase for the forward-compat shape, but the gateway-side `AcknowledgeAlarmByName` when the public RPC supplies a recognizable `Provider!Group.Tag` reference. -**Opt-in managed GUID ack.** Setting `MXGATEWAY_USE_MANAGED_ALARM_ACK=1` -in the gateway's environment (workers inherit it) routes GUID-keyed acks -through `aaAlarmManagedClient.AlarmClient.AlarmAckByGUID` instead of the -wnwrap method. That managed method is implemented on this build and -carries operator domain and full name natively. The worker reaches it by -reflection (`AaManagedAlarmAckConsumer`) because the members are -non-public, and runs its own Initialize → Register → Subscribe lifecycle -against the alarm subscription expression. The path is deliberately -conservative: +**Opt-in managed GUID ack — does not work on this rig (live-tested +2026-10-18).** Setting `MXGATEWAY_USE_MANAGED_ALARM_ACK=1` in the +gateway's environment (workers inherit it) makes the worker build an +`AaManagedAlarmAckConsumer`, which drives +`aaAlarmManagedClient.AlarmClient` by reflection (its members are +non-public) and is meant to take GUID-keyed acks in place of the +wnwrap method. The live test shows it cannot do that as built, so leave +the flag off. -- If the DLL cannot load or the lifecycle returns a non-zero status, the - worker drops back to the wnwrap ack path; alarm reads never fail - because of it. -- Subtag-mode acks (forced subtag, or failover currently on the standby) - stay on the subtag consumer: their GUIDs are synthetic and only it can - resolve them. -- By-name acks stay on wnwrap — `AlarmClient` has no by-name ack. +The test raised `TestMachine_001.TestAlarm001` with a secured write, +read its GUID from the wnwrap XML snapshot (the public API exposes no +alarm GUIDs), and acknowledged it through the `AcknowledgeAlarm` RPC +with the flag on: -A zero return still means *accepted*, not *applied*; a secured -(`MxSecurityOperate`) alarm needs an authenticated operator. The skip-gated -`AlarmManagedGuidAckLiveSmokeTests` asserts the ack actually lands. +| Step | Result | +|---|---| +| RPC ack by GUID, flag off (control) | `NotImplementedException`; `.Acked` stays `False` | +| RPC ack by GUID, flag on | Same `NotImplementedException`; `.Acked` `False`, `.AckMsg` empty | +| `AlarmClient.RegisterConsumer` in a process with a registered wnwrap consumer | `-65` — and wnwrap gets `-65` if the managed client registered first; a real `hWnd` does not help | +| Managed client alone in its own process, records loaded | `AlarmAckByGUID` → `-1`, `SelectByGUID` → `-1` for the wnwrap GUID in both byte orders, `AlarmAckSelected` → `-57` | + +Three separate problems follow: + +1. **The two libraries cannot share a process.** The worker always runs + a wnwrap reader, so the managed `RegisterConsumer` fails with `-65`. + The dispatcher is built to drop back to the wnwrap ack path on a + lifecycle failure (so alarm reads keep working), and it does so + silently — which is why the flag-on RPC is indistinguishable from the + control. +2. **The managed client does not recognise the wnwrap GUID.** `-1` from + `SelectByGUID` is "not found", not a security refusal, even with the + client holding records. Getting the client's own identifier for the + alarm is not reliable either: `GetHighPriAlarm` mostly throws the + FILETIME `ArgumentOutOfRangeException` described above. The + `MxSecurityOperate` question was therefore never reached. +3. **The gateway does not send the identity fields.** Its GUID arm fills + `OperatorNode`, `OperatorDomain` and `OperatorFullName` with empty + strings, so the domain and full name the managed method carries + would not arrive through the public RPC even if 1 and 2 were solved. + +A working managed ack would need the client hosted out of process, a way +to resolve the alarm to the identifier the managed client uses, and the +identity fields plumbed through the gateway. Routing rules the worker +already applies stay correct and would carry over: subtag-mode acks +(synthetic GUIDs) stay on the subtag consumer, and by-name acks stay on +wnwrap because `AlarmClient` has no by-name ack. The skip-gated +`AlarmManagedGuidAckLiveSmokeTests` exercises the in-process +arrangement and hits problem 1. ### 5. STA / threading — resolved diff --git a/docs/GatewayConfiguration.md b/docs/GatewayConfiguration.md index 4b0c0af..98b8535 100644 --- a/docs/GatewayConfiguration.md +++ b/docs/GatewayConfiguration.md @@ -446,11 +446,12 @@ behavior. The alarm monitor is independent of client sessions: `AcknowledgeAlarm` and `StreamAlarms` are session-less RPCs served by the monitor. -GUID-keyed acknowledges can opt into AVEVA's managed alarm client by setting -the environment variable `MXGATEWAY_USE_MANAGED_ALARM_ACK=1` on the gateway -process (workers inherit it; under NSSM, add it to the service environment). -It is off by default and has no `MxGateway:*` option because it is still -awaiting live confirmation on the rig. See +The worker also reads `MXGATEWAY_USE_MANAGED_ALARM_ACK` (inherited from the +gateway process). Setting it to `1` is meant to route GUID-keyed acknowledges +through AVEVA's managed alarm client, but a live test (2026-10-18) showed the +path cannot work as built: the managed client fails to register alongside the +worker's wnwrap reader and the worker silently falls back to wnwrap. Leave it +unset. It has no `MxGateway:*` option. See [AlarmClientDiscovery](./AlarmClientDiscovery.md) ("Opt-in managed GUID ack"). ### Alarm fallback options