using System; using ArchestrA.MxAccess; using ZB.MOM.WW.LmxProxy.Host.Domain; namespace ZB.MOM.WW.LmxProxy.Host.Implementation { /// /// Event handlers for MxAccessClient to process data changes, write completions, and operation completions. /// public sealed partial class MxAccessClient { /// /// Handles data change events from the MxAccess server. /// /// Server handle. /// Item handle. /// Item value. /// Item quality code. /// Item timestamp. /// Status array. private void OnDataChange(int hLMXServerHandle, int phItemHandle, object pvItemValue, int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus) { try { if (!_subscriptionsByHandle.TryGetValue(phItemHandle, out SubscriptionInfo? subscription)) { return; } // Convert quality from integer Quality quality = ConvertQuality(pwItemQuality); DateTime timestamp = ConvertTimestamp(pftItemTimeStamp); var vtq = new Vtq(pvItemValue, timestamp, quality); // Invoke callback subscription.Callback?.Invoke(subscription.Address, vtq); } catch (Exception ex) { Logger.Error(ex, "Error processing data change for handle {Handle}", phItemHandle); } } /// /// Handles write completion events from the MxAccess server. /// /// Server handle. /// Item handle. /// Status array. private void OnWriteComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus) { try { WriteOperation? writeOp; lock (_lock) { if (_pendingWrites.TryGetValue(phItemHandle, out writeOp)) { _pendingWrites.Remove(phItemHandle); } } if (writeOp != null) { try { if (ItemStatus is { Length: > 0 }) { var status = ItemStatus[0]; if (status.success == 0) { string errorMsg = GetWriteErrorMessage(status.detail); Logger.Warning( "Write failed for {Address} (handle {Handle}): {Error} (Category={Category}, Detail={Detail})", writeOp.Address, phItemHandle, errorMsg, status.category, status.detail); writeOp.CompletionSource.TrySetException(new InvalidOperationException( $"Write failed: {errorMsg}")); } else { Logger.Debug("Write completed successfully for {Address} (handle {Handle})", writeOp.Address, phItemHandle); writeOp.CompletionSource.TrySetResult(true); } } else { Logger.Debug("Write completed for {Address} (handle {Handle}) with no status", writeOp.Address, phItemHandle); writeOp.CompletionSource.TrySetResult(true); } } finally { // Clean up the item after write completes lock (_lock) { if (_lmxProxy != null) { try { _lmxProxy.UnAdvise(_connectionHandle, phItemHandle); _lmxProxy.RemoveItem(_connectionHandle, phItemHandle); } catch (Exception ex) { Logger.Debug(ex, "Error cleaning up after write for handle {Handle}", phItemHandle); } } } } } else if (ItemStatus is { Length: > 0 }) { var status = ItemStatus[0]; if (status.success == 0) { Logger.Warning("Write failed for unknown handle {Handle}: Category={Category}, Detail={Detail}", phItemHandle, status.category, status.detail); } } } catch (Exception ex) { Logger.Error(ex, "Error processing write complete for handle {Handle}", phItemHandle); } } /// /// Handles operation completion events from the MxAccess server. /// /// Server handle. /// Item handle. /// Status array. private void OnOperationComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus) { // Log operation completion Logger.Debug("Operation complete for handle {Handle}", phItemHandle); } /// /// Converts an integer MxAccess quality code to . /// /// The MxAccess quality code. /// The corresponding value. private Quality ConvertQuality(int mxQuality) => (Quality)mxQuality; /// /// Converts a timestamp object to in UTC. /// /// The timestamp object. /// The UTC value. private DateTime ConvertTimestamp(object timestamp) { if (timestamp is DateTime dt) { return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime(); } return DateTime.UtcNow; } } }