diff --git a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs index 8f7ae7270..55d18f049 100644 --- a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs @@ -184,7 +184,7 @@ protected override NodeState AddBehaviourToPredefinedNode( } else { - NodeState serverNode = FindNodeInAddressSpace(ObjectIds.Server); + NodeState serverNode = Server.NodeManager.FindNodeInAddressSpaceAsync(ObjectIds.Server).AsTask().GetAwaiter().GetResult(); serverNode?.ReplaceChild(context, activeNode); } // remove the reference to server node because it is set as parent @@ -1192,8 +1192,8 @@ private NamespaceMetadataState FindNamespaceMetadataState(string namespaceUri) var nameSpaceNodeId = ExpandedNodeId.ToNodeId( serverNamespacesReference.TargetId, Server.NamespaceUris); - if (FindNodeInAddressSpace( - nameSpaceNodeId) is not NamespaceMetadataState namespaceMetadata) + if (Server.NodeManager.FindNodeInAddressSpaceAsync( + nameSpaceNodeId).AsTask().GetAwaiter().GetResult() is not NamespaceMetadataState namespaceMetadata) { continue; } diff --git a/Libraries/Opc.Ua.Server/Configuration/IConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/IConfigurationNodeManager.cs index b7b73916c..c65b31abf 100644 --- a/Libraries/Opc.Ua.Server/Configuration/IConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/IConfigurationNodeManager.cs @@ -32,7 +32,7 @@ namespace Opc.Ua.Server /// /// The Server Configuration Node Manager. /// - public interface IConfigurationNodeManager : INodeManager2 + public interface IConfigurationNodeManager : INodeManager3 { /// /// Gets or creates the node for the specified NamespaceUri. diff --git a/Libraries/Opc.Ua.Server/Diagnostics/IDiagnosticsNodeManager.cs b/Libraries/Opc.Ua.Server/Diagnostics/IDiagnosticsNodeManager.cs index 3c1d370ea..bac24d3ac 100644 --- a/Libraries/Opc.Ua.Server/Diagnostics/IDiagnosticsNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Diagnostics/IDiagnosticsNodeManager.cs @@ -32,7 +32,7 @@ namespace Opc.Ua.Server /// /// A node manager the diagnostic information exposed by the server. /// - public interface IDiagnosticsNodeManager : INodeManager2, INodeIdFactory + public interface IDiagnosticsNodeManager : INodeManager3, INodeIdFactory { /// /// True if diagnostics are currently enabled. diff --git a/Libraries/Opc.Ua.Server/NodeManager/Adapters/AsyncNodeManagerAdapter.cs b/Libraries/Opc.Ua.Server/NodeManager/Adapters/AsyncNodeManagerAdapter.cs index 28c0b35e2..d457c82fe 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/Adapters/AsyncNodeManagerAdapter.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/Adapters/AsyncNodeManagerAdapter.cs @@ -606,6 +606,38 @@ public ValueTask WriteAsync( return default; } + /// + public ValueTask ValidateEventRolePermissionsAsync(IEventMonitoredItem monitoredItem, IFilterTarget filterTarget) + { + if (SyncNodeManager is IAsyncNodeManager asyncNodeManager) + { + return asyncNodeManager.ValidateEventRolePermissionsAsync(monitoredItem, filterTarget); + } + + if (SyncNodeManager is INodeManager3 nodeManager2) + { + return new ValueTask(nodeManager2.ValidateEventRolePermissions(monitoredItem, filterTarget)); + } + + return new ValueTask(ServiceResult.Good); + } + + /// + public ValueTask ValidateRolePermissionsAsync(OperationContext operationContext, NodeId nodeId, PermissionType requestedPermission) + { + if (SyncNodeManager is IAsyncNodeManager asyncNodeManager) + { + return asyncNodeManager.ValidateRolePermissionsAsync(operationContext, nodeId, requestedPermission); + } + + if (SyncNodeManager is INodeManager3 nodeManager2) + { + return new ValueTask(nodeManager2.ValidateRolePermissions(operationContext, nodeId, requestedPermission)); + } + + return new ValueTask(ServiceResult.Good); + } + /// /// Frees any unmanaged resources. /// diff --git a/Libraries/Opc.Ua.Server/NodeManager/Adapters/SyncNodeManagerAdapter.cs b/Libraries/Opc.Ua.Server/NodeManager/Adapters/SyncNodeManagerAdapter.cs index 4d3559af7..5b70fe8ae 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/Adapters/SyncNodeManagerAdapter.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/Adapters/SyncNodeManagerAdapter.cs @@ -42,9 +42,9 @@ public static class SyncNodeManagerAdapterFactory /// if the NodeManager does not implement the interface uses the /// to create an ISyncNodeManager compatible object /// - public static INodeManager2 ToSyncNodeManager(this IAsyncNodeManager nodeManager) + public static INodeManager3 ToSyncNodeManager(this IAsyncNodeManager nodeManager) { - if (nodeManager is INodeManager2 syncNodeManager) + if (nodeManager is INodeManager3 syncNodeManager) { return syncNodeManager; } @@ -59,7 +59,7 @@ public static INodeManager2 ToSyncNodeManager(this IAsyncNodeManager nodeManager /// This allows asynchronous nodeManagers to be treated as synchronous, which can help /// compatibility with existing code. /// - public class SyncNodeManagerAdapter : INodeManager2 + public class SyncNodeManagerAdapter : INodeManager3 { /// /// Initializes a new instance of the class. @@ -288,6 +288,18 @@ public NodeMetadata GetPermissionMetadata( .AsTask().GetAwaiter().GetResult(); } + /// + public ServiceResult ValidateEventRolePermissions(IEventMonitoredItem monitoredItem, IFilterTarget filterTarget) + { + return m_nodeManager.ValidateEventRolePermissionsAsync(monitoredItem, filterTarget).AsTask().GetAwaiter().GetResult(); + } + + /// + public ServiceResult ValidateRolePermissions(OperationContext operationContext, NodeId nodeId, PermissionType requestedPermission) + { + return m_nodeManager.ValidateRolePermissionsAsync(operationContext, nodeId, requestedPermission).AsTask().GetAwaiter().GetResult(); + } + private readonly IAsyncNodeManager m_nodeManager; } } diff --git a/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs index 114d56f8c..6f2f66f75 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs @@ -48,7 +48,7 @@ namespace Opc.Ua.Server /// is not part of the SDK because most real implementations of a INodeManager will need to /// modify the behavior of the base class. /// - public partial class CustomNodeManager2 : INodeManager2, INodeIdFactory, IDisposable + public partial class CustomNodeManager2 : INodeManager3, INodeIdFactory, IDisposable { /// /// Initializes the node manager. @@ -169,7 +169,7 @@ protected CustomNodeManager2( } else { - m_monitoredItemManager = new MonitoredNodeMonitoredItemManager(this); + m_monitoredItemManager = new MonitoredNodeMonitoredItemManager(this, server); } PredefinedNodes = []; @@ -445,22 +445,10 @@ public bool DeleteNode(ServerSystemContext context, NodeId nodeId) /// /// Searches the node id in all node managers /// + [Obsolete("Use IServerInteral.IMasterNodeManager.FindNodeInAddressSpaceAsync instead.")] public NodeState FindNodeInAddressSpace(NodeId nodeId) { - if (nodeId.IsNull) - { - return null; - } - // search node id in all node managers - foreach (INodeManager nodeManager in Server.NodeManager.NodeManagers) - { - if (nodeManager.GetManagerHandle(nodeId) is not NodeHandle handle) - { - continue; - } - return handle.Node; - } - return null; + return Server.NodeManager.FindNodeInAddressSpaceAsync(nodeId).AsTask().GetAwaiter().GetResult(); } /// diff --git a/Libraries/Opc.Ua.Server/NodeManager/IMasterNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/IMasterNodeManager.cs index 76a364663..1b211f4fd 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/IMasterNodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/IMasterNodeManager.cs @@ -141,6 +141,12 @@ ValueTask DeleteMonitoredItemsAsync( /// ValueTask DeleteReferencesAsync(NodeId targetId, IList references, CancellationToken cancellationToken = default); + /// + /// Searches the node id in all node managers, + /// returns the node state if found (and node Manager supports it), otherwise returns null. + /// + ValueTask FindNodeInAddressSpaceAsync(NodeId nodeId); + /// /// Returns node handle and its node manager. /// diff --git a/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs index 5f2a9f2a1..898b2cc05 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs @@ -376,7 +376,28 @@ NodeMetadata GetPermissionMetadata( } /// - /// An asynchronous version of the "Call" method defined on the interface. + /// An interface to an object that manages a set of nodes in the address space. + /// + public interface INodeManager3 : INodeManager2 + { + /// + /// Validates if the specified event monitored item has enough permissions to receive the specified event + /// + ServiceResult ValidateEventRolePermissions( + IEventMonitoredItem monitoredItem, + IFilterTarget filterTarget); + + /// + /// Validates Role permissions for the specified NodeId + /// + ServiceResult ValidateRolePermissions( + OperationContext operationContext, + NodeId nodeId, + PermissionType requestedPermission); + } + + /// + /// An asynchronous version of the "Call" method defined on the interface. /// public interface ICallAsyncNodeManager { @@ -392,7 +413,7 @@ ValueTask CallAsync( } /// - /// An asynchronous version of the "Read" method defined on the interface. + /// An asynchronous version of the "Read" method defined on the interface. /// public interface IReadAsyncNodeManager { @@ -427,7 +448,7 @@ ValueTask ReadAsync( } /// - /// An asynchronous version of the "Write" method defined on the interface. + /// An asynchronous version of the "Write" method defined on the interface. /// public interface IWriteAsyncNodeManager { @@ -446,7 +467,7 @@ ValueTask WriteAsync( } /// - /// An asynchronous version of the "HistoryRead" method defined on the interface. + /// An asynchronous version of the "HistoryRead" method defined on the interface. /// public interface IHistoryReadAsyncNodeManager { @@ -465,7 +486,7 @@ ValueTask HistoryReadAsync( } /// - /// An asynchronous version of the "HistoryUpdate" method defined on the interface. + /// An asynchronous version of the "HistoryUpdate" method defined on the interface. /// public interface IHistoryUpdateAsyncNodeManager { @@ -482,7 +503,7 @@ ValueTask HistoryUpdateAsync( } /// - /// An asynchronous version of the "ConditionRefresh" method defined on the interface. + /// An asynchronous version of the "ConditionRefresh" method defined on the interface. /// public interface IConditionRefreshAsyncNodeManager { @@ -496,7 +517,7 @@ ValueTask ConditionRefreshAsync( } /// - /// An asynchronous version of the "TranslateBrowsePath" method defined on the interface. + /// An asynchronous version of the "TranslateBrowsePath" method defined on the interface. /// public interface ITranslateBrowsePathAsyncNodeManager { @@ -527,7 +548,7 @@ ValueTask TranslateBrowsePathAsync( } /// - /// An asynchronous version of the "Browse" method defined on the interface. + /// An asynchronous version of the "Browse" method defined on the interface. /// public interface IBrowseAsyncNodeManager { @@ -557,7 +578,7 @@ ValueTask BrowseAsync( } /// - /// An asynchronous version of the "SetMonitoringMode" method defined on the interface. + /// An asynchronous version of the "SetMonitoringMode" method defined on the interface. /// public interface ISetMonitoringModeAsyncNodeManager { @@ -574,7 +595,7 @@ ValueTask SetMonitoringModeAsync( } /// - /// An asynchronous version of the "TransferMonitoredItems" method defined on the interface. + /// An asynchronous version of the "TransferMonitoredItems" method defined on the interface. /// public interface ITransferMonitoredItemsAsyncNodeManager { @@ -594,7 +615,7 @@ ValueTask TransferMonitoredItemsAsync( } /// - /// An asynchronous version of the "DeleteMonitoredItems" method defined on the interface. + /// An asynchronous version of the "DeleteMonitoredItems" method defined on the interface. /// public interface IDeleteMonitoredItemsAsyncNodeManager { @@ -610,7 +631,7 @@ ValueTask DeleteMonitoredItemsAsync( } /// - /// An asynchronous version of the "ModifyMonitoredItems" method defined on the interface. + /// An asynchronous version of the "ModifyMonitoredItems" method defined on the interface. /// public interface IModifyMonitoredItemsAsyncNodeManager { @@ -628,7 +649,7 @@ ValueTask ModifyMonitoredItemsAsync( } /// - /// An asynchronous version of the "CreateMonitoredItems" method defined on the interface. + /// An asynchronous version of the "CreateMonitoredItems" method defined on the interface. /// public interface ICreateMonitoredItemsAsyncNodeManager { @@ -672,11 +693,10 @@ ValueTask CreateAsync( } /// - /// An asynchronous verison of the interface. + /// An asynchronous verison of the interface. /// This interface is in active development and will be extended in future releases. /// Please use the sub interfaces to implement async support for specific service calls. /// - [Experimental("UA_NETStandard_1")] public interface IAsyncNodeManager : ICallAsyncNodeManager, IReadAsyncNodeManager, @@ -847,6 +867,21 @@ ValueTask RestoreMonitoredItemsAsync( IList monitoredItems, IUserIdentity savedOwnerIdentity, CancellationToken cancellationToken = default); + + /// + /// Validates if the specified event monitored item has enough permissions to receive the specified event + /// + ValueTask ValidateEventRolePermissionsAsync( + IEventMonitoredItem monitoredItem, + IFilterTarget filterTarget); + + /// + /// Validates Role permissions for the specified NodeId + /// + ValueTask ValidateRolePermissionsAsync( + OperationContext operationContext, + NodeId nodeId, + PermissionType requestedPermission); } /// diff --git a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs index cd01a0eec..4af1063ab 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs @@ -1794,6 +1794,26 @@ private async ValueTask UpdateReferenceDescriptionAsync( return true; } + /// + public async ValueTask FindNodeInAddressSpaceAsync(NodeId nodeId) + { + if (nodeId.IsNull) + { + return null; + } + // search node id in all node managers + foreach (IAsyncNodeManager nodeManager in AsyncNodeManagers) + { + if ((await nodeManager.GetManagerHandleAsync(nodeId).ConfigureAwait(false)) + is not NodeHandle handle) + { + continue; + } + return handle.Node; + } + return null; + } + /// public virtual async ValueTask<(DataValueCollection values, DiagnosticInfoCollection diagnosticInfos)> ReadAsync( OperationContext context, diff --git a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNode.cs b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNode.cs index 7a33e9d16..f87fda512 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNode.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNode.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Threading; namespace Opc.Ua.Server { @@ -47,17 +48,19 @@ public class MonitoredNode2 /// Initializes a new instance of the class. /// /// The node manager. + /// The server. /// The node. - public MonitoredNode2(CustomNodeManager2 nodeManager, NodeState node) + public MonitoredNode2(INodeManager3 nodeManager, IServerInternal server, NodeState node) { NodeManager = nodeManager; + m_server = server; Node = node; } /// /// Gets or sets the NodeManager which the MonitoredNode belongs to. /// - public CustomNodeManager2 NodeManager { get; set; } + public INodeManager3 NodeManager { get; set; } /// /// Gets or sets the Node being monitored. @@ -67,12 +70,12 @@ public MonitoredNode2(CustomNodeManager2 nodeManager, NodeState node) /// /// Gets the current list of data change MonitoredItems. /// - public List DataChangeMonitoredItems { get; private set; } + public ConcurrentDictionary DataChangeMonitoredItems { get; } = new(); /// /// Gets the current list of event MonitoredItems. /// - public List EventMonitoredItems { get; private set; } + public ConcurrentDictionary EventMonitoredItems { get; } = new(); /// /// Gets a value indicating whether this instance has monitored items. @@ -84,12 +87,12 @@ public bool HasMonitoredItems { get { - if (DataChangeMonitoredItems != null && DataChangeMonitoredItems.Count > 0) + if (DataChangeMonitoredItems != null && !DataChangeMonitoredItems.IsEmpty) { return true; } - if (EventMonitoredItems != null && EventMonitoredItems.Count > 0) + if (EventMonitoredItems != null && !EventMonitoredItems.IsEmpty) { return true; } @@ -104,13 +107,9 @@ public bool HasMonitoredItems /// The monitored item. public void Add(IDataChangeMonitoredItem2 datachangeItem) { - if (DataChangeMonitoredItems == null) - { - DataChangeMonitoredItems = []; - Node.OnStateChanged = OnMonitoredNodeChanged; - } + DataChangeMonitoredItems.TryAdd(datachangeItem.Id, datachangeItem); - DataChangeMonitoredItems.Add(datachangeItem); + Node.OnStateChanged = OnMonitoredNodeChanged; } /// @@ -119,22 +118,14 @@ public void Add(IDataChangeMonitoredItem2 datachangeItem) /// The monitored item. public void Remove(IDataChangeMonitoredItem2 datachangeItem) { - for (int ii = 0; ii < DataChangeMonitoredItems.Count; ii++) + if (DataChangeMonitoredItems.TryRemove(datachangeItem.Id, out _)) { - if (ReferenceEquals(DataChangeMonitoredItems[ii], datachangeItem)) - { - DataChangeMonitoredItems.RemoveAt(ii); - - // Remove the cached context for the monitored item - m_contextCache.TryRemove(datachangeItem.Id, out _); - - break; - } + // Remove the cached context for the monitored item + m_contextCache.TryRemove(datachangeItem.Id, out _); } - if (DataChangeMonitoredItems.Count == 0) + if (DataChangeMonitoredItems.IsEmpty) { - DataChangeMonitoredItems = null; Node.OnStateChanged = null; } } @@ -145,13 +136,9 @@ public void Remove(IDataChangeMonitoredItem2 datachangeItem) /// The monitored item. public void Add(IEventMonitoredItem eventItem) { - if (EventMonitoredItems == null) - { - EventMonitoredItems = []; - Node.OnReportEvent = OnReportEvent; - } + EventMonitoredItems.TryAdd(eventItem.Id, eventItem); - EventMonitoredItems.Add(eventItem); + Node.OnReportEvent = OnReportEvent; } /// @@ -160,18 +147,10 @@ public void Add(IEventMonitoredItem eventItem) /// The monitored item. public void Remove(IEventMonitoredItem eventItem) { - for (int ii = 0; ii < EventMonitoredItems.Count; ii++) - { - if (ReferenceEquals(EventMonitoredItems[ii], eventItem)) - { - EventMonitoredItems.RemoveAt(ii); - break; - } - } + EventMonitoredItems.TryRemove(eventItem.Id, out _); - if (EventMonitoredItems.Count == 0) + if (EventMonitoredItems.IsEmpty) { - EventMonitoredItems = null; Node.OnReportEvent = null; } } @@ -184,76 +163,53 @@ public void Remove(IEventMonitoredItem eventItem) /// The event. public void OnReportEvent(ISystemContext context, NodeState node, IFilterTarget e) { - var eventMonitoredItems = new List(); - - lock (NodeManager.Lock) + // make sure to process events in the order they are received and avoid concurrent processing of events for the same node + lock (m_eventLock) { - if (EventMonitoredItems == null) - { - return; - } - - for (int ii = 0; ii < EventMonitoredItems.Count; ii++) + foreach (KeyValuePair kvp in EventMonitoredItems) { - IEventMonitoredItem monitoredItem = EventMonitoredItems[ii]; - // enqueue event for role permission validation - eventMonitoredItems.Add(monitoredItem); - } - } - - for (int ii = 0; ii < eventMonitoredItems.Count; ii++) - { - IEventMonitoredItem monitoredItem = eventMonitoredItems[ii]; + IEventMonitoredItem monitoredItem = kvp.Value; - if (e is AuditEventState) - { - // check Server.Auditing flag and skip if false - if (!NodeManager.Server.Auditing) + if (e is AuditEventState) { - continue; + // check Server.Auditing flag and skip if false + if (!m_server.Auditing) + { + continue; + } + // check if channel is not encrypted and skip if so + if (monitoredItem?.Session?.EndpointDescription?.SecurityMode != + MessageSecurityMode.SignAndEncrypt && + monitoredItem?.Session?.EndpointDescription?.TransportProfileUri != + Profiles.HttpsBinaryTransport) + { + continue; + } } - // check if channel is not encrypted and skip if so - if (monitoredItem?.Session?.EndpointDescription?.SecurityMode != - MessageSecurityMode.SignAndEncrypt && - monitoredItem?.Session?.EndpointDescription?.TransportProfileUri != - Profiles.HttpsBinaryTransport) + + // validate if the monitored item has the required role permissions to receive the event + ServiceResult validationResult = NodeManager.ValidateEventRolePermissions( + monitoredItem, + e); + + if (ServiceResult.IsBad(validationResult)) { + // skip event reporting for EventType without permissions continue; } - } - - // validate if the monitored item has the required role permissions to receive the event - ServiceResult validationResult = NodeManager.ValidateEventRolePermissions( - monitoredItem, - e); - - if (ServiceResult.IsBad(validationResult)) - { - // skip event reporting for EventType without permissions - continue; - } - lock (NodeManager.Lock) - { // enqueue event if (context is ISessionSystemContext session && !session.SessionId.IsNull && monitoredItem?.Session != null && - !monitoredItem.Session.Id.IsNull) + !monitoredItem.Session.Id.IsNull && + !monitoredItem.Session.Id.Equals(session.SessionId)) { - if (monitoredItem.Session.Id.Equals(session.SessionId)) - { - monitoredItem?.QueueEvent(e); - } - else - { - continue; - } - } - else - { - monitoredItem?.QueueEvent(e); + // skip if the event does not belong to the same session as the monitored item + continue; } + + monitoredItem?.QueueEvent(e); } } } @@ -269,16 +225,17 @@ public void OnMonitoredNodeChanged( NodeState node, NodeStateChangeMasks changes) { - lock (NodeManager.Lock) + //make sure to process data change notifications in the order they are received and avoid concurrent processing of value changes for the same node + lock (m_dataChangelock) { if (DataChangeMonitoredItems == null) { return; } - for (int ii = 0; ii < DataChangeMonitoredItems.Count; ii++) + foreach (KeyValuePair kvp in DataChangeMonitoredItems) { - IDataChangeMonitoredItem2 monitoredItem = DataChangeMonitoredItems[ii]; + IDataChangeMonitoredItem2 monitoredItem = kvp.Value; if (monitoredItem.AttributeId == Attributes.Value && (changes & NodeStateChangeMasks.Value) != 0) @@ -391,5 +348,9 @@ private ServerSystemContext GetOrCreateContext( new(); private readonly int m_cacheLifetimeTicks = (int)TimeSpan.FromMinutes(5).TotalMilliseconds; + + private readonly Lock m_dataChangelock = new(); + private readonly Lock m_eventLock = new(); + private readonly IServerInternal m_server; } } diff --git a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNodeMonitoredItemManager.cs b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNodeMonitoredItemManager.cs index 811d7e4a6..91f34812d 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNodeMonitoredItemManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/MonitoredNodeMonitoredItemManager.cs @@ -39,9 +39,10 @@ namespace Opc.Ua.Server public class MonitoredNodeMonitoredItemManager : IMonitoredItemManager { /// - public MonitoredNodeMonitoredItemManager(CustomNodeManager2 nodeManager) + public MonitoredNodeMonitoredItemManager(INodeManager3 nodeManager, IServerInternal server) { m_nodeManager = nodeManager; + m_server = server; MonitoredNodes = []; MonitoredItems = new ConcurrentDictionary(); } @@ -77,7 +78,7 @@ public ISampledDataChangeMonitoredItem CreateMonitoredItem( { NodeState cachedNode = addNodeToComponentCache(context, handle, handle.Node); MonitoredNodes[handle.Node.NodeId] - = monitoredNode = new MonitoredNode2(m_nodeManager, cachedNode); + = monitoredNode = new MonitoredNode2(m_nodeManager, m_server, cachedNode); } handle.Node = monitoredNode.Node; @@ -201,7 +202,7 @@ public bool RestoreMonitoredItem( { NodeState cachedNode = addNodeToComponentCache(context, handle, handle.Node); MonitoredNodes[handle.Node.NodeId] - = monitoredNode = new MonitoredNode2(m_nodeManager, cachedNode); + = monitoredNode = new MonitoredNode2(m_nodeManager, m_server, cachedNode); } handle.Node = monitoredNode.Node; @@ -255,7 +256,7 @@ public ServiceResult ModifyMonitoredItem( IEventMonitoredItem monitoredItem, bool unsubscribe) { - MonitoredNode2 monitoredNode = null; + MonitoredNode2 monitoredNode; // handle unsubscribe. if (unsubscribe) { @@ -292,12 +293,12 @@ public ServiceResult ModifyMonitoredItem( if (!MonitoredNodes.TryGetValue(source.NodeId, out monitoredNode)) { MonitoredNodes[source.NodeId] - = monitoredNode = new MonitoredNode2(m_nodeManager, source); + = monitoredNode = new MonitoredNode2(m_nodeManager, m_server, source); } // remove existing monitored items with the same Id prior to insertion in order to avoid duplicates // this is necessary since the SubscribeToEvents method is called also from ModifyMonitoredItemsForEvents - monitoredNode.EventMonitoredItems?.RemoveAll(e => e.Id == monitoredItem.Id); + monitoredNode.EventMonitoredItems.TryRemove(monitoredItem.Id, out _); // this links the node to specified monitored item and ensures all events // reported by the node are added to the monitored item's queue. @@ -310,6 +311,7 @@ public ServiceResult ModifyMonitoredItem( return (monitoredNode, ServiceResult.Good); } - private readonly CustomNodeManager2 m_nodeManager; + private readonly INodeManager3 m_nodeManager; + private readonly IServerInternal m_server; } } diff --git a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/SamplingGroupMonitoredItemManager.cs b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/SamplingGroupMonitoredItemManager.cs index dab4a0316..edafa9f77 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/SamplingGroupMonitoredItemManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/MonitoredItem/SamplingGroupMonitoredItemManager.cs @@ -39,7 +39,7 @@ public class SamplingGroupMonitoredItemManager : IMonitoredItemManager { /// public SamplingGroupMonitoredItemManager( - CustomNodeManager2 nodeManager, + INodeManager3 nodeManager, IServerInternal server, ApplicationConfiguration configuration) { @@ -51,6 +51,7 @@ public SamplingGroupMonitoredItemManager( configuration.ServerConfiguration.AvailableSamplingRates); m_nodeManager = nodeManager; + m_server = server; MonitoredNodes = []; MonitoredItems = new ConcurrentDictionary(); } @@ -319,12 +320,12 @@ public bool RestoreMonitoredItem( if (!MonitoredNodes.TryGetValue(source.NodeId, out monitoredNode)) { MonitoredNodes[source.NodeId] - = monitoredNode = new MonitoredNode2(m_nodeManager, source); + = monitoredNode = new MonitoredNode2(m_nodeManager, m_server, source); } // remove existing monitored items with the same Id prior to insertion in order to avoid duplicates // this is necessary since the SubscribeToEvents method is called also from ModifyMonitoredItemsForEvents - monitoredNode.EventMonitoredItems?.RemoveAll(e => e.Id == monitoredItem.Id); + monitoredNode.EventMonitoredItems.TryRemove(monitoredItem.Id, out _); // this links the node to specified monitored item and ensures all events // reported by the node are added to the monitored item's queue. @@ -334,7 +335,8 @@ public bool RestoreMonitoredItem( return (monitoredNode, ServiceResult.Good); } - private readonly CustomNodeManager2 m_nodeManager; + private readonly INodeManager3 m_nodeManager; + private readonly IServerInternal m_server; private readonly SamplingGroupManager m_samplingGroupManager; } }