using System; using System.Collections.Generic; namespace ZB.MOM.WW.MxGateway.Worker.MxAccess; /// /// Abstraction over an AVEVA alarm-consumer COM library. The production /// implementation () wraps /// WNWRAPCONSUMERLib.wwAlarmConsumerClass from /// C:\Program Files (x86)\Common Files\ArchestrA\wnwrapConsumer.dll; /// tests substitute a fake to drive transition events without a live /// Galaxy. /// /// /// /// The receive surface is poll-based: the production consumer /// periodically calls GetXmlCurrentAlarms2, parses the /// returned XML payload, diffs against the previous snapshot keyed /// by alarm GUID, and raises /// once per state change. This bypasses the FILETIME marshaling /// crash in aaAlarmManagedClient.AlarmClient.GetHighPriAlarm /// (see docs/AlarmClientDiscovery.md) — XML strings carry /// timestamps as ASCII fields, no DateTime auto-conversion happens /// on the .NET interop boundary. /// /// public interface IMxAccessAlarmConsumer : IDisposable { /// /// Fires once per detected alarm-state transition (raise, acknowledge, /// clear, or new-alarm-already-acked-on-arrival). Subscribers are /// expected to translate the record into the proto family /// OnAlarmTransition and enqueue it. Fired on the consumer's /// polling thread (the worker's STA in production); subscribers that /// need a different thread must marshal back themselves. /// event EventHandler? AlarmTransitionEmitted; /// /// Initializes the AVEVA alarm-client connection, registers as a /// consumer, and subscribes to the supplied alarm-provider expression. /// Subscription string follows AVEVA's canonical format: /// \\<node>\Galaxy!<area>. The literal "Galaxy" is /// the provider name (regardless of the configured Galaxy database /// name). Subscribe does not start any polling of its own; the caller /// drives polls explicitly via . /// /// The subscription expression (e.g., \\HOST\Galaxy!Area). void Subscribe(string subscription); /// /// Resolves the MXAccess item whose write acknowledges the alarm with the /// given GUID (e.g. Obj.Alarm.AckMsg). Does not perform the ack. /// /// The alarm GUID as reported by this consumer. /// The resolved target or the reason none exists. AlarmAckTarget ResolveAckTarget(Guid alarmGuid); /// Resolves the ack item for an alarm identified by name, provider and group. /// The tag/alarm name, e.g. TestMachine_001.TestAlarm001. /// The provider (literal Galaxy for ArchestrA). /// The area/group. /// The resolved target or the reason none exists. AlarmAckTarget ResolveAckTargetByName(string alarmName, string providerName, string groupName); /// /// Returns the consumer's most recently parsed snapshot of currently /// active alarms, together with whether the fetch that produced it hit /// the per-fetch cap. Used by the gateway's QueryActiveAlarms (PR A.7) /// ConditionRefresh path — operator clients call this after reconnect /// to seed local Part 9 state. /// /// /// /// The verdict is an out parameter rather than a separate /// property on purpose, and the reason is a correctness one. /// Implementations must produce both values from a single acquisition /// of whatever lock guards the retained snapshot, mirroring the write /// side (WnWrapAlarmConsumer.FoldFetch updates snapshot and /// verdict together). Two separate reads could straddle a poll that /// flips not-truncated → truncated and pair a stale /// with a capped snapshot — a snapshot that /// reads as complete while missing actives, which is exactly the /// false all-clear this signal exists to prevent. Making the pair /// inseparable in the signature removes the possibility rather than /// relying on callers, or on the STA serializing them. /// /// /// While is the /// returned snapshot is authoritative about presence only: the /// provider may hold actives it had no room to report, and the /// consumer has suspended the absence-implies-Clear inference. Not /// latched — the first sub-cap fetch clears it, because that fetch is /// complete and its snapshot is again authoritative about absence. /// Consumers with no per-fetch cap (the subtag fallback, which is /// advise-driven) always report . /// /// /// /// Receives whether the fetch behind the returned snapshot hit the /// per-fetch cap. /// /// The most recently parsed snapshot of currently active alarms. IReadOnlyList SnapshotActiveAlarms(out bool truncated); /// /// Drives a single synchronous poll of the underlying alarm source. /// The production consumer owns no internal timer; the worker's STA /// drives polls via StaRuntime.InvokeAsync, satisfying the /// ThreadingModel=Apartment requirement of /// wwAlarmConsumerClass. Fake implementations should no-op. /// This method must be invoked on the thread that created the consumer /// (the worker's STA in production). /// void PollOnce(); }