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