using System; using ZB.MOM.WW.MxGateway.Contracts.Proto; namespace ZB.MOM.WW.MxGateway.Worker.MxAccess; /// /// Sink for native MxAccess alarm transitions. Bridges /// to the worker's event queue, /// producing messages via /// . /// /// /// /// owns the wire-up: it constructs the /// consumer/sink pair, calls to propagate the /// session id, and subscribes the consumer's /// event /// so each decoded transition reaches . /// The method here carries only the session id — /// the alarm path needs no COM-event subscription of its own because /// the consumer already polls and raises transition events. The /// captured payload schema is described in /// docs/AlarmClientDiscovery.md "Option A — captured". /// /// public sealed class MxAccessAlarmEventSink : IMxAccessEventSink { private readonly MxAccessEventMapper eventMapper; private readonly MxAccessEventQueue eventQueue; private string sessionId = string.Empty; private bool attached; /// Initializes a new instance of the MxAccessAlarmEventSink class with default dependencies. public MxAccessAlarmEventSink() : this(new MxAccessEventQueue(), new MxAccessEventMapper()) { } /// Initializes a new instance of the MxAccessAlarmEventSink class. /// Queue for buffering converted alarm events. /// Converter for alarm transitions to protobuf format. public MxAccessAlarmEventSink( MxAccessEventQueue eventQueue, MxAccessEventMapper eventMapper) { this.eventQueue = eventQueue ?? throw new ArgumentNullException(nameof(eventQueue)); this.eventMapper = eventMapper ?? throw new ArgumentNullException(nameof(eventMapper)); } /// public void Attach(object mxAccessComObject, string sessionId) { if (mxAccessComObject is null) throw new ArgumentNullException(nameof(mxAccessComObject)); this.sessionId = sessionId ?? string.Empty; // The alarm path needs no COM-event subscription here: the wnwrap // consumer is polled by the worker's STA and raises transition events // that AlarmDispatcher routes into EnqueueTransition. Attach only // records the session id stamped onto every emitted MxEvent. attached = true; } /// public void Detach() { if (!attached) return; attached = false; sessionId = string.Empty; } /// /// Enqueues a decoded alarm transition. The COM-side delegate registered /// in calls this method once it pulls the alarm /// fields out of the MxAccess event payload. Exposed internal so unit /// tests can drive the proto build path without a real COM event /// source. /// /// The fully qualified alarm object reference. /// The object that sourced the alarm. /// The alarm type name. /// The kind of alarm transition. /// The alarm severity level. /// The original alarm raise timestamp in UTC, if available. /// The timestamp of this transition in UTC. /// The user who performed the operation, if applicable. /// The operator's comment, if any. /// The alarm category. /// The alarm description. /// /// when the transition was synthesized by the /// subtag-provider fallback rather than the native alarmmgr path. /// Defaults to so existing alarmmgr callers /// compile unchanged and stay on the parity (alarmmgr) path. /// internal void EnqueueTransition( string alarmFullReference, string sourceObjectReference, string alarmTypeName, AlarmTransitionKind transitionKind, int severity, DateTime? originalRaiseTimestampUtc, DateTime transitionTimestampUtc, string operatorUser, string operatorComment, string category, string description, bool degraded = false) { try { // Degraded transitions come from the subtag fallback; the native // alarmmgr (wnwrap) path stays degraded=false / ALARMMGR for parity. AlarmProviderMode sourceProvider = degraded ? AlarmProviderMode.Subtag : AlarmProviderMode.Alarmmgr; MxEvent mxEvent = eventMapper.CreateOnAlarmTransition( sessionId, alarmFullReference, sourceObjectReference, alarmTypeName, transitionKind, severity, originalRaiseTimestampUtc, transitionTimestampUtc, operatorUser, operatorComment, category, description, statuses: null, degraded: degraded, sourceProvider: sourceProvider); eventQueue.Enqueue(mxEvent); } catch (Exception exception) { eventQueue.RecordFault(new WorkerFault { Category = WorkerFaultCategory.MxaccessEventConversionFailed, ExceptionType = exception.GetType().FullName ?? string.Empty, DiagnosticMessage = $"{exception.GetType().FullName}: HRESULT 0x{unchecked((uint)exception.HResult):X8}", ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.MxaccessFailure, Message = "MXAccess alarm event conversion failed.", }, }); } } }