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.",
},
});
}
}
}