using System; using ArchestrA.MxAccess; using Serilog; using ZB.MOM.WW.LmxProxy.Host.Domain; namespace ZB.MOM.WW.LmxProxy.Host.MxAccess { public sealed partial class MxAccessClient { /// /// Callback invoked by the SubscriptionManager when it needs to deliver /// data change events. Set by the SubscriptionManager during initialization. /// public Action? OnTagValueChanged { get; set; } /// /// COM event handler for MxAccess OnDataChange events. /// Signature matches the ArchestrA.MxAccess ILMXProxyServerEvents interface. /// private void OnDataChange( int hLMXServerHandle, int phItemHandle, object pvItemValue, int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus) { try { var quality = MapQuality(pwItemQuality); var timestamp = ConvertTimestamp(pftItemTimeStamp); // Check MXSTATUS_PROXY — if success is false, override quality // with a more specific code derived from the MxAccess status fields if (ItemStatus != null && ItemStatus.Length > 0 && ItemStatus[0].success == 0) { var status = ItemStatus[0]; quality = MxStatusMapper.CategoryToQuality((int)status.category, status.detail); Log.Debug("OnDataChange status failure for handle {Handle}: {Status}", phItemHandle, MxStatusMapper.FormatStatus(status.detail, (int)status.category, (int)status.detectedBy)); } var vtq = new Vtq(pvItemValue, timestamp, quality); // Resolve address from handle map string address; lock (_lock) { if (!_handleToAddress.TryGetValue(phItemHandle, out address)) { Log.Debug("OnDataChange for unknown handle {Handle}, ignoring", phItemHandle); return; } } // Invoke the stored subscription callback Action callback; lock (_lock) { if (!_storedSubscriptions.TryGetValue(address, out callback)) { Log.Debug("OnDataChange for {Address} but no callback registered", address); return; } } callback.Invoke(address, vtq); // Also route to the SubscriptionManager's global handler OnTagValueChanged?.Invoke(address, vtq); } catch (Exception ex) { Log.Error(ex, "Error processing OnDataChange event for handle {Handle}", phItemHandle); } } /// /// COM event handler for MxAccess OnWriteComplete events. /// Signature matches the ArchestrA.MxAccess ILMXProxyServerEvents interface. /// Kept wired for diagnostic logging only — writes are resolved synchronously /// when the Write() COM call returns without throwing. /// private void OnWriteComplete( int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus) { try { if (ItemStatus != null && ItemStatus.Length > 0) { var status = ItemStatus[0]; if (status.success == 0) { Log.Warning("OnWriteComplete callback: write failed for handle {Handle}: {Status}", phItemHandle, MxStatusMapper.FormatStatus(status.detail, (int)status.category, (int)status.detectedBy)); } else { Log.Debug("OnWriteComplete callback: write succeeded for handle {Handle}", phItemHandle); } } else { Log.Debug("OnWriteComplete callback: no status for handle {Handle}", phItemHandle); } } catch (Exception ex) { Log.Error(ex, "Error processing OnWriteComplete event for handle {Handle}", phItemHandle); } } /// /// Converts a timestamp object to DateTime in UTC. /// private static DateTime ConvertTimestamp(object timestamp) { if (timestamp is DateTime dt) { return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime(); } return DateTime.UtcNow; } } }