f711a55be4
Switch the worker's alarm-consumer surface from `aaAlarmManagedClient.AlarmClient` to `WNWRAPCONSUMERLib.wwAlarmConsumerClass` (CLSID 7AB52E5F-…) hosted by `wnwrapConsumer.dll`. The new path returns alarm records as a BSTR XML payload via `GetXmlCurrentAlarms2`, bypassing the FILETIME→DateTime auto-marshaling that crashed `GetHighPriAlarm` with ArgumentOutOfRangeException on every poll. Live captured 60/60 polls clean against `\DESKTOP-6JL3KKO\Galaxy!DEV` while a System Platform script flipped TestMachine_001.TestAlarm001 every 10s; the GUID, priority, state (UNACK_ALM ↔ UNACK_RTN), and ASCII-formatted timestamps arrived end-to-end. Implementation: - `Interop.WNWRAPCONSUMERLib.dll` generated via tlbimp, checked in under `lib/` so dev boxes don't need the SDK to build. - New `WnWrapAlarmConsumer` (replaces `AlarmClientConsumer`): owns a 500ms polling timer, parses `GetXmlCurrentAlarms2` output, diffs the snapshot keyed by alarm GUID, and raises one `MxAlarmTransitionEvent` per state change. Includes the Initialize→Register-before-Subscribe ordering fix found during Discovery probe runs. - New library-agnostic types `MxAlarmSnapshotRecord` / `MxAlarmStateKind` / `MxAlarmTransitionEvent` so the proto-build path is testable without an AVEVA install. - `AlarmRecordTransitionMapper` retired the COM-coupled `MapTransitionKind(eAlmTransitions)`; new pure helpers `ParseStateKind`, `MapTransition(prev, curr)`, and `ParseTransitionTimestampUtc` cover XML decode + state-delta logic. - `IMxAccessAlarmConsumer` event surface changed from `EventHandler<AlarmRecord>` to `EventHandler<MxAlarmTransitionEvent>` and `SnapshotActiveAlarms()` returns `MxAlarmSnapshotRecord` — decoupling the interface from any specific COM library. - Worker csproj drops `aaAlarmManagedClient` / `IAlarmMgrDataProvider` refs; adds `Interop.WNWRAPCONSUMERLib`. Tests: - 36 new unit tests (state-string mapping, prev/current → proto kind decision table, timestamp UTC reassembly, XML payload parser, 32-char hex GUID round-trip) covering everything that doesn't touch the live COM surface — all passing. - Skip-gated `WnWrapConsumerProbeTests.ProbeWnWrapConsumer` archives the live capture flow for regression / future probes. Docs: - `docs/AlarmClientDiscovery.md` "Option A — captured" section records sample XML payloads, the mangled `SetXmlAlarmQuery` round-trip (prefer `Subscribe` for filtering), the `GetStatistics` AccessViolationException quirk, and the worker-integration outline. Pre-existing failure noted (separate): `MxAccessInteropReference_ExistsOnlyInWorkerProject` was already failing on HEAD — the test project still references `ArchestrA.MxAccess` for the Skip-gated discovery probes. Not regressed by this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 lines
2.2 KiB
C#
60 lines
2.2 KiB
C#
using System;
|
|
|
|
namespace MxGateway.Worker.MxAccess;
|
|
|
|
/// <summary>
|
|
/// Library-agnostic alarm-state enum. Mirrors the four <c>STATE</c>
|
|
/// values returned by AVEVA's <c>WNWRAPCONSUMERLib</c> XML payload —
|
|
/// <c>UNACK_ALM</c>, <c>ACK_ALM</c>, <c>UNACK_RTN</c>, <c>ACK_RTN</c>.
|
|
/// Decoupling the consumer from any specific COM library keeps the
|
|
/// proto-build path testable without an AVEVA install.
|
|
/// </summary>
|
|
public enum MxAlarmStateKind
|
|
{
|
|
Unspecified = 0,
|
|
UnackAlm = 1,
|
|
AckAlm = 2,
|
|
UnackRtn = 3,
|
|
AckRtn = 4,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Single alarm record as emitted by the wnwrapConsumer XML stream.
|
|
/// Field names match the captured XML schema (see
|
|
/// <c>docs/AlarmClientDiscovery.md</c> "Option A — captured" section).
|
|
/// </summary>
|
|
public sealed class MxAlarmSnapshotRecord
|
|
{
|
|
public Guid AlarmGuid { get; set; }
|
|
public DateTime TransitionTimestampUtc { get; set; }
|
|
public string ProviderNode { get; set; } = string.Empty;
|
|
public string ProviderName { get; set; } = string.Empty;
|
|
public string Group { get; set; } = string.Empty;
|
|
public string TagName { get; set; } = string.Empty;
|
|
public string Type { get; set; } = string.Empty;
|
|
public string Value { get; set; } = string.Empty;
|
|
public string Limit { get; set; } = string.Empty;
|
|
public int Priority { get; set; }
|
|
public MxAlarmStateKind State { get; set; }
|
|
public string OperatorNode { get; set; } = string.Empty;
|
|
public string OperatorName { get; set; } = string.Empty;
|
|
public string AlarmComment { get; set; } = string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// One transition emitted by the consumer's snapshot diff. Pairs the
|
|
/// latest record with its previous state so the proto layer can decide
|
|
/// whether the transition is a Raise / Acknowledge / Clear.
|
|
/// </summary>
|
|
public sealed class MxAlarmTransitionEvent : EventArgs
|
|
{
|
|
public MxAlarmSnapshotRecord Record { get; set; } = new MxAlarmSnapshotRecord();
|
|
|
|
/// <summary>
|
|
/// The state on the consumer's previous polled snapshot, or
|
|
/// <see cref="MxAlarmStateKind.Unspecified"/> when this is the
|
|
/// first time the GUID has been observed.
|
|
/// </summary>
|
|
public MxAlarmStateKind PreviousState { get; set; }
|
|
}
|