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;
}
}