using System; using ArchestrA.MxAccess; using Proto = MxGateway.Contracts.Proto; namespace MxGateway.Worker.MxAccess; /// Sink for MXAccess COM events that converts them to protobuf format. public sealed class MxAccessBaseEventSink : IMxAccessEventSink { private readonly MxAccessEventMapper eventMapper; private readonly MxAccessEventQueue eventQueue; private LMXProxyServerClass? server; private string sessionId = string.Empty; /// Initializes a new instance of the MxAccessBaseEventSink class with a default queue. public MxAccessBaseEventSink() : this(new MxAccessEventQueue()) { } /// Initializes a new instance of the MxAccessBaseEventSink class with a provided queue. /// Queue for buffering converted MXAccess events. public MxAccessBaseEventSink(MxAccessEventQueue eventQueue) : this(eventQueue, new MxAccessEventMapper()) { } /// Initializes a new instance of the MxAccessBaseEventSink class with provided queue and mapper. /// Queue for buffering converted MXAccess events. /// Converter for MXAccess events to protobuf format. public MxAccessBaseEventSink( 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) { this.sessionId = sessionId ?? string.Empty; server = (LMXProxyServerClass)mxAccessComObject; server.OnDataChange += OnDataChange; server.OnWriteComplete += OnWriteComplete; server.OperationComplete += OperationComplete; server.OnBufferedDataChange += OnBufferedDataChange; } /// public void Detach() { if (server is null) { return; } server.OnDataChange -= OnDataChange; server.OnWriteComplete -= OnWriteComplete; server.OperationComplete -= OperationComplete; server.OnBufferedDataChange -= OnBufferedDataChange; server = null; sessionId = string.Empty; } private void OnDataChange( int hLMXServerHandle, int phItemHandle, object pvItemValue, int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] pVars) { MXSTATUS_PROXY[] statuses = pVars; EnqueueEvent(() => eventMapper.CreateOnDataChange( sessionId, hLMXServerHandle, phItemHandle, pvItemValue, pwItemQuality, pftItemTimeStamp, statuses)); } private void OnWriteComplete( int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] pVars) { MXSTATUS_PROXY[] statuses = pVars; EnqueueEvent(() => eventMapper.CreateOnWriteComplete( sessionId, hLMXServerHandle, phItemHandle, statuses)); } private void OperationComplete( int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] pVars) { MXSTATUS_PROXY[] statuses = pVars; EnqueueEvent(() => eventMapper.CreateOperationComplete( sessionId, hLMXServerHandle, phItemHandle, statuses)); } private void OnBufferedDataChange( int hLMXServerHandle, int phItemHandle, MxDataType dtDataType, object pvItemValue, object pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] pVars) { MXSTATUS_PROXY[] statuses = pVars; EnqueueEvent(() => eventMapper.CreateOnBufferedDataChange( sessionId, hLMXServerHandle, phItemHandle, (int)dtDataType, pvItemValue, pwItemQuality, pftItemTimeStamp, statuses)); } private void EnqueueEvent(Func createEvent) { try { eventQueue.Enqueue(createEvent()); } catch (Exception exception) { eventQueue.RecordFault(CreateEventConversionFault(exception)); } } private Proto.WorkerFault CreateEventConversionFault(Exception exception) { return new Proto.WorkerFault { Category = Proto.WorkerFaultCategory.MxaccessEventConversionFailed, ExceptionType = exception.GetType().FullName ?? string.Empty, DiagnosticMessage = $"{exception.GetType().FullName}: HRESULT 0x{unchecked((uint)exception.HResult):X8}", ProtocolStatus = new Proto.ProtocolStatus { Code = Proto.ProtocolStatusCode.MxaccessFailure, Message = "MXAccess event conversion failed.", }, }; } }