|
|
|
@@ -4,80 +4,49 @@ using MxGateway.Contracts.Proto;
|
|
|
|
|
namespace MxGateway.Worker.MxAccess;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PR A.2 sink intended to register against an MXAccess alarm event source
|
|
|
|
|
/// and forward each alarm transition into the worker's event queue as an
|
|
|
|
|
/// <see cref="OnAlarmTransitionEvent"/>. The mapper bridge is fully
|
|
|
|
|
/// implemented + unit-tested via
|
|
|
|
|
/// <see cref="MxAccessEventMapper.CreateOnAlarmTransition"/>; the
|
|
|
|
|
/// <see cref="Attach"/> path is intentionally a no-op pending the
|
|
|
|
|
/// architectural decision documented below.
|
|
|
|
|
/// PR A.2 sink for native MxAccess alarm transitions. Bridges the
|
|
|
|
|
/// <c>aaAlarmManagedClient.AlarmClient</c> consumer to the worker's
|
|
|
|
|
/// event queue, producing <see cref="OnAlarmTransitionEvent"/> messages
|
|
|
|
|
/// via <see cref="MxAccessEventMapper.CreateOnAlarmTransition"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// <strong>2026-04-30 dev-rig finding:</strong> the MXAccess COM
|
|
|
|
|
/// Toolkit installed at <c>C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll</c>
|
|
|
|
|
/// does <strong>not</strong> expose any alarm event family. Reflection
|
|
|
|
|
/// enumeration of the assembly (which exports a single COM interop
|
|
|
|
|
/// module containing <c>ILMXProxyServerEvents</c> and
|
|
|
|
|
/// <c>ILMXProxyServerEvents2</c>) confirms the only available events
|
|
|
|
|
/// are <c>OnDataChange</c>, <c>OnWriteComplete</c>,
|
|
|
|
|
/// <c>OperationComplete</c>, and <c>OnBufferedDataChange</c>. There is
|
|
|
|
|
/// no <c>OnAlarmTransition</c>, no <c>IAlarmEventSink</c>, and no
|
|
|
|
|
/// <c>Alarms</c> collection on the COM server.
|
|
|
|
|
/// <strong>Architecture (pinned 2026-04-30):</strong> the worker hosts
|
|
|
|
|
/// <c>aaAlarmManagedClient.AlarmClient</c> alongside the existing
|
|
|
|
|
/// <c>ArchestrA.MxAccess</c> COM consumer. Both are x86 .NET Framework
|
|
|
|
|
/// 4.8 — the worker's existing runtime — and both use the same Windows
|
|
|
|
|
/// STA + WM_APP message pump. The MxAccess COM Toolkit at
|
|
|
|
|
/// <c>C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll</c>
|
|
|
|
|
/// exposes no alarm events; the alarm provider lives in a separate
|
|
|
|
|
/// AVEVA service that <c>aaAlarmManagedClient</c> subscribes to.
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// AVEVA's separate alarm-subscription managed assemblies
|
|
|
|
|
/// (<c>aaAlarmManagedClient.dll</c> under
|
|
|
|
|
/// <c>InTouch\ViewAppFramework\Content\MA\</c>,
|
|
|
|
|
/// <c>ArchestrAAlarmsAndEvents.SDK.Common.dll</c> under
|
|
|
|
|
/// <c>Wonderware\Historian\x64\</c>) are present on this box but are
|
|
|
|
|
/// <strong>x64-only</strong>; they cannot load into the worker process,
|
|
|
|
|
/// which is x86 because of the MXAccess COM bitness constraint that
|
|
|
|
|
/// the <c>mxaccessgw</c> architecture exists to isolate. Loading them
|
|
|
|
|
/// in a separate x64 helper process would add meaningful operational
|
|
|
|
|
/// complexity (a third process tier alongside worker + gateway) and is
|
|
|
|
|
/// not in the current architecture.
|
|
|
|
|
/// <strong>Discovered API surface</strong> (see
|
|
|
|
|
/// <c>AlarmClientDiscoveryTests.DumpAlarmClientPublicSurface</c> in
|
|
|
|
|
/// <c>MxGateway.Worker.Tests</c> — Skip-gated reflection probe):
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// <strong>Two paths forward — operator decision needed before the
|
|
|
|
|
/// sink can be wired:</strong>
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <list type="number">
|
|
|
|
|
/// <item>
|
|
|
|
|
/// <description>
|
|
|
|
|
/// <strong>Stay on the value-driven sub-attribute path</strong>
|
|
|
|
|
/// (current production behaviour). The lmxopcua server's
|
|
|
|
|
/// <c>AlarmConditionService</c> already synthesizes Part 9
|
|
|
|
|
/// transitions from the four MXAccess sub-attributes
|
|
|
|
|
/// (<c>InAlarm</c>, <c>Acked</c>, <c>Priority</c>,
|
|
|
|
|
/// <c>Description</c>) via the data-change subscription.
|
|
|
|
|
/// Operator-comment fidelity is the only regression vs. v1; if
|
|
|
|
|
/// acceptable, this row stays the production path and the
|
|
|
|
|
/// <see cref="MxEventFamily.OnAlarmTransition"/> family stays
|
|
|
|
|
/// reserved-but-dormant on the wire.
|
|
|
|
|
/// </description>
|
|
|
|
|
/// </item>
|
|
|
|
|
/// <item>
|
|
|
|
|
/// <description>
|
|
|
|
|
/// <strong>Add an x64 alarm-helper sub-process</strong> alongside
|
|
|
|
|
/// the worker that loads <c>aaAlarmManagedClient</c>,
|
|
|
|
|
/// subscribes to alarms, and forwards transitions to the worker
|
|
|
|
|
/// over a small named-pipe IPC. Then this sink's
|
|
|
|
|
/// <see cref="Attach"/> connects to that helper instead of to
|
|
|
|
|
/// the COM server, and routes each transition through
|
|
|
|
|
/// <see cref="EnqueueTransition"/>. Adds operational complexity
|
|
|
|
|
/// but recovers full v1 fidelity (operator user, comment,
|
|
|
|
|
/// original raise time, category).
|
|
|
|
|
/// </description>
|
|
|
|
|
/// </item>
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
/// <item><description><c>RegisterConsumer(hWnd, productName, applicationName, version, retainHidden)</c> — registers a Windows-message-pump consumer; the AVEVA alarm service WM_APP-pokes the hWnd when alarms change.</description></item>
|
|
|
|
|
/// <item><description><c>Subscribe(provider, fromPri, toPri, queryType, sortFlags, filterMask, filterSpec)</c> — subscribes to a Galaxy alarm provider with priority + filter scoping.</description></item>
|
|
|
|
|
/// <item><description><c>GetStatistics(out percentQuery, totalAlarms, activeAlarms, …, out int[] changeCodes, out int[] changePos, out int[] hAlarm)</c> — called on each WM_APP poke; enumerates which alarms changed.</description></item>
|
|
|
|
|
/// <item><description><c>GetAlarmExtendedRec(index, out AlarmRecord)</c> — pulls the full alarm record (operator, comment, original raise, category, severity).</description></item>
|
|
|
|
|
/// <item><description><c>AlarmAckByGUID(alarmGuid, ackComment, oprName, oprNode, oprDomain, oprFullName)</c> — full-fidelity native Acknowledge: comment + four operator-identity fields are atomic with the ack transition.</description></item>
|
|
|
|
|
/// </list>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Until that decision is made, this sink's <see cref="Attach"/> is a
|
|
|
|
|
/// no-op. The worker continues to function for data subscriptions, and
|
|
|
|
|
/// the gateway's <see cref="MxEventFamily.OnAlarmTransition"/> family
|
|
|
|
|
/// is reserved on the wire but never emitted. lmxopcua-side
|
|
|
|
|
/// <c>AlarmConditionService</c> keeps the sub-attribute synthesis
|
|
|
|
|
/// active and continues to surface alarms to OPC UA Part 9 clients.
|
|
|
|
|
/// <strong>Wiring plan (subsequent PRs):</strong>
|
|
|
|
|
/// </para>
|
|
|
|
|
/// <list type="number">
|
|
|
|
|
/// <item><description>Worker session-startup wires <c>AlarmClient.RegisterConsumer</c> against the worker's existing STA hWnd; <c>Subscribe</c> with the Galaxy provider name + a permissive priority/filter range.</description></item>
|
|
|
|
|
/// <item><description>The STA's WM_APP handler routes alarm-changed messages into <see cref="EnqueueTransition"/>; the message ID is established at runtime via the consumer's reported handler (verify on dev rig).</description></item>
|
|
|
|
|
/// <item><description>Gateway-side <c>AcknowledgeAlarm</c> RPC translates to a worker command that calls <c>AlarmClient.AlarmAckByGUID</c> with the OPC UA operator's resolved identity — replaces the worker-pending diagnostic from PR A.3.</description></item>
|
|
|
|
|
/// </list>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Until those PRs land, <see cref="Attach"/> is a no-op. The worker
|
|
|
|
|
/// continues to function for data subscriptions, and the gateway's
|
|
|
|
|
/// <see cref="MxEventFamily.OnAlarmTransition"/> family is reserved on
|
|
|
|
|
/// the wire but never emitted. lmxopcua-side <c>AlarmConditionService</c>
|
|
|
|
|
/// keeps the sub-attribute synthesis active and continues to surface
|
|
|
|
|
/// alarms to OPC UA Part 9 clients in the meantime.
|
|
|
|
|
/// </para>
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public sealed class MxAccessAlarmEventSink : IMxAccessEventSink
|
|
|
|
|