Discovers the surface of aaAlarmManagedClient.dll and stages the worker
csproj reference so subsequent PRs can wire native MxAccess alarm
subscription. Replaces the speculative "operator decision needed
between path 1 and path 2" framing in MxAccessAlarmEventSink with the
validated architecture.
Key findings from the discovery probe:
1. aaAlarmManagedClient.dll is x86 + .NET Framework (mixed-mode
C++/CLI; PE Machine = i386, NativeEntryPoint flag set). The
"x64-only" framing in the prior follow-up was wrong — confused
by the file path under Wonderware\Historian\x64\.
The assembly is bitness- and runtime-compatible with the
worker (net48 x86), so it loads in the existing process. No
sub-process needed.
2. AlarmClient is the public class. Its model mirrors MxAccess:
RegisterConsumer takes a Windows hWnd and the AVEVA alarm
service WM_APP-pokes that hwnd when alarms change. The worker's
existing STA + WM_APP pump can drive both the data-change COM
subscriber and the alarm-client consumer.
3. AlarmAckByGUID(alarmGuid, ackComment, oprName, oprNode,
oprDomain, oprFullName) — the native ack carries the operator's
full identity atomically with the comment. Closes the v1
operator-comment fidelity gap completely.
This PR:
- Adds the aaAlarmManagedClient.dll reference to MxGateway.Worker.
csproj. Worker still builds clean.
- Adds AlarmClientDiscoveryTests as a Skip-gated reflection probe;
flip the Skip parameter to dump the public type surface for
reference. Captured the dump into MxAccessAlarmEventSink
documentation so it doesn't have to be re-run.
- Replaces MxAccessAlarmEventSink's "two paths forward" doc with
the actual wiring plan against AlarmClient's RegisterConsumer +
Subscribe + AlarmAckByGUID surface.
Subsequent PRs (gated on STA + WM_APP integration testing on the
dev rig):
- Wire RegisterConsumer + Subscribe at session-startup; route
WM_APP messages through GetStatistics + GetAlarmExtendedRec into
EnqueueTransition.
- Translate gateway-side AcknowledgeAlarm RPC to a worker command
that calls AlarmAckByGUID with the OPC UA operator's identity;
replaces the worker-pending diagnostic from PR A.3.
- Translate gateway-side QueryActiveAlarms to a worker command
that walks GetStatistics's reported handles via GetAlarmExtendedRec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR A.2 ship-pin discovery: the MXAccess COM Toolkit installed at
C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll
does not expose any alarm-event family. Reflection enumeration of
the assembly confirms ILMXProxyServerEvents and
ILMXProxyServerEvents2 only carry OnDataChange, OnWriteComplete,
OperationComplete, and OnBufferedDataChange — no IAlarmEventSink,
no Alarms collection, no OnAlarmTransition.
AVEVA's separate alarm-subscription managed assemblies
(aaAlarmManagedClient.dll under InTouch\ViewAppFramework\Content\MA\
and ArchestrAAlarmsAndEvents.SDK.Common.dll under
Wonderware\Historian\x64\) exist on this box but are x64-only —
incompatible with the worker's x86 bitness, which is the bitness
constraint the mxaccessgw architecture exists to isolate in the
first place.
This commit replaces the speculative "TBD pin during dev-rig
validation" comment in MxAccessAlarmEventSink with the actual
finding plus the two operator-facing paths forward:
1. Stay on the value-driven sub-attribute path (current production
behaviour). lmxopcua's AlarmConditionService already synthesizes
Part 9 transitions from the four MXAccess sub-attributes.
Operator-comment fidelity is the only v1 regression.
2. Add an x64 alarm-helper sub-process alongside the worker that
loads aaAlarmManagedClient and forwards transitions to the
worker over a small named-pipe IPC. Recovers full v1 fidelity
but adds operational complexity.
Until that decision resolves, the sink's Attach is a no-op, the
worker continues to function for data subscriptions, and
lmxopcua-side AlarmConditionService keeps the sub-attribute
synthesis active.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eighteenth PR of the alarms-over-gateway epic
(docs/plans/alarms-over-gateway.md). Lands the proto-build path that
the worker uses to create OnAlarmTransition events. The COM-side
subscription that registers an alarm event sink against the MXAccess
Toolkit is pinned during dev-rig validation — the exact API differs
across AVEVA versions and needs hardware to verify.
Lands today (unit-testable, no hardware needed):
- MxAccessEventMapper.CreateOnAlarmTransition — mechanical proto
builder. Takes decoded alarm fields (full reference, source
object, alarm type, transition kind, severity, timestamps,
operator user/comment, category, description) and produces an
MxEvent with the OnAlarmTransition body populated. Mirrors the
pattern of CreateOnDataChange / CreateOnWriteComplete / etc.
- MxAccessAlarmEventSink — scaffolded class with documented
Attach / Detach + an internal EnqueueTransition entry point.
When dev-rig validation pins the MXAccess Toolkit alarm
subscription API, the only edit needed is to wire the COM
delegate inside Attach to call EnqueueTransition. The mapper
bridge is already done.
Pending dev-rig validation:
- Pin the MXAccess Toolkit alarm event source COM API (likely one
of IAlarmEventSink, IAlarmEventSubscription, or a method on
LMXProxyServerClass — verify against the worker host's installed
version).
- Add cancellation/cleanup tests once the COM hook is wired.
- Integration test against the parity rig that fires a real Galaxy
alarm and asserts the gateway emits OnAlarmTransition.
Tests:
- 2 new mapper tests pin the full-payload Acknowledge case and
the bare-bones Raise case.
- Full Worker.Tests suite green: 123 passed (was 121; 2 new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>