using Opc.Ua; using Opc.Ua.Client; using Serilog; namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Adapters; /// /// Production subscription adapter wrapping a real OPC UA Subscription. /// internal sealed class DefaultSubscriptionAdapter : ISubscriptionAdapter { private static readonly ILogger Logger = Log.ForContext(); private readonly Dictionary _monitoredItems = new(); private readonly Subscription _subscription; /// /// Wraps a live OPC UA subscription so client code can manage monitored items through a testable abstraction. /// /// The underlying OPC UA subscription that owns monitored items for this client workflow. public DefaultSubscriptionAdapter(Subscription subscription) { _subscription = subscription; } /// public uint SubscriptionId => _subscription.Id; /// public async Task AddDataChangeMonitoredItemAsync( NodeId nodeId, int samplingIntervalMs, Action onDataChange, CancellationToken ct) { var item = new MonitoredItem(_subscription.DefaultItem) { StartNodeId = nodeId, DisplayName = nodeId.ToString(), SamplingInterval = samplingIntervalMs }; item.Notification += (_, e) => { if (e.NotificationValue is MonitoredItemNotification notification) onDataChange(nodeId.ToString(), notification.Value); }; _subscription.AddItem(item); await _subscription.ApplyChangesAsync(ct); var handle = item.ClientHandle; _monitoredItems[handle] = item; Logger.Debug("Added data change monitored item for {NodeId}, handle={Handle}", nodeId, handle); return handle; } /// public async Task RemoveMonitoredItemAsync(uint clientHandle, CancellationToken ct) { if (!_monitoredItems.TryGetValue(clientHandle, out var item)) return; _subscription.RemoveItem(item); await _subscription.ApplyChangesAsync(ct); _monitoredItems.Remove(clientHandle); Logger.Debug("Removed monitored item handle={Handle}", clientHandle); } /// public async Task AddEventMonitoredItemAsync( NodeId nodeId, int samplingIntervalMs, EventFilter filter, Action onEvent, CancellationToken ct) { var item = new MonitoredItem(_subscription.DefaultItem) { StartNodeId = nodeId, DisplayName = "AlarmMonitor", SamplingInterval = samplingIntervalMs, NodeClass = NodeClass.Object, AttributeId = Attributes.EventNotifier, Filter = filter }; item.Notification += (_, e) => { if (e.NotificationValue is EventFieldList eventFields) onEvent(eventFields); }; _subscription.AddItem(item); await _subscription.ApplyChangesAsync(ct); var handle = item.ClientHandle; _monitoredItems[handle] = item; Logger.Debug("Added event monitored item for {NodeId}, handle={Handle}", nodeId, handle); return handle; } /// public async Task ConditionRefreshAsync(CancellationToken ct) { await _subscription.ConditionRefreshAsync(ct); } /// public async Task DeleteAsync(CancellationToken ct) { try { await _subscription.DeleteAsync(true); } catch (Exception ex) { Logger.Warning(ex, "Error deleting subscription"); } _monitoredItems.Clear(); } /// /// Releases the wrapped OPC UA subscription and clears tracked monitored items held by the adapter. /// public void Dispose() { try { _subscription.Delete(true); } catch { } _monitoredItems.Clear(); } }