using System; using ArchestrA.MxAccess; using ZB.MOM.WW.OtOpcUa.Host.Domain; namespace ZB.MOM.WW.OtOpcUa.Host.MxAccess { public sealed partial class MxAccessClient { /// /// COM event handler for MxAccess OnDataChange events. /// Signature matches the ArchestrA.MxAccess ILMXProxyServerEvents interface. /// private void HandleOnDataChange( int hLMXServerHandle, int phItemHandle, object pvItemValue, int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus) { try { if (!_handleToAddress.TryGetValue(phItemHandle, out var address)) { Log.Debug("OnDataChange for unknown handle {Handle}", phItemHandle); return; } var quality = QualityMapper.MapFromMxAccessQuality(pwItemQuality); // Check MXSTATUS_PROXY — if success is false, use more specific quality if (ItemStatus != null && ItemStatus.Length > 0 && ItemStatus[0].success == 0) quality = MxErrorCodes.MapToQuality(ItemStatus[0].detail); var timestamp = ConvertTimestamp(pftItemTimeStamp); var vtq = new Vtq(pvItemValue, timestamp, quality); // Update probe timestamp if (string.Equals(address, _probeTag, StringComparison.OrdinalIgnoreCase)) _lastProbeValueTime = DateTime.UtcNow; // Invoke stored subscription callback if (_storedSubscriptions.TryGetValue(address, out var callback)) callback(address, vtq); if (_pendingReadsByAddress.TryGetValue(address, out var pendingReads)) foreach (var pendingRead in pendingReads.Values) pendingRead.TrySetResult(vtq); // Global handler OnTagValueChanged?.Invoke(address, vtq); } catch (Exception ex) { Log.Error(ex, "Error processing OnDataChange for handle {Handle}", phItemHandle); } } /// /// COM event handler for MxAccess OnWriteComplete events. /// private void HandleOnWriteComplete( int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus) { try { if (_pendingWrites.TryRemove(phItemHandle, out var tcs)) { var success = ItemStatus == null || ItemStatus.Length == 0 || ItemStatus[0].success != 0; if (success) { tcs.TrySetResult(true); } else { var detail = ItemStatus![0].detail; var message = MxErrorCodes.GetMessage(detail); Log.Warning("Write failed for handle {Handle}: {Message}", phItemHandle, message); tcs.TrySetResult(false); } } } catch (Exception ex) { Log.Error(ex, "Error processing OnWriteComplete for handle {Handle}", phItemHandle); } } private static DateTime ConvertTimestamp(object pftItemTimeStamp) { if (pftItemTimeStamp is DateTime dt) return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime(); return DateTime.UtcNow; } } }