diff --git a/Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs b/Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs index 2b0717c9f..32b5d09e9 100644 --- a/Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs +++ b/Applications/Quickstarts.Servers/ReferenceServer/ReferenceNodeManager.cs @@ -5068,26 +5068,23 @@ protected override NodeHandle GetManagerHandle( NodeId nodeId, IDictionary cache) { - lock (Lock) + // quickly exclude nodes that are not in the namespace. + if (!IsNodeIdInNamespace(nodeId)) { - // quickly exclude nodes that are not in the namespace. - if (!IsNodeIdInNamespace(nodeId)) - { - return null; - } - - if (!PredefinedNodes.TryGetValue(nodeId, out NodeState node)) - { - return null; - } + return null; + } - return new NodeHandle - { - NodeId = nodeId, - Node = node, - Validated = true - }; + if (!PredefinedNodes.TryGetValue(nodeId, out NodeState node)) + { + return null; } + + return new NodeHandle + { + NodeId = nodeId, + Node = node, + Validated = true + }; } /// diff --git a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs index fde4bcd1a..731f9c17f 100644 --- a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs @@ -43,21 +43,6 @@ namespace Opc.Ua.Server { - /// - /// Privileged identity which can access the system configuration. - /// - public class SystemConfigurationIdentity : RoleBasedIdentity - { - /// - /// Create a user identity with the privilege - /// to modify the system configuration. - /// - /// The user identity. - public SystemConfigurationIdentity(IUserIdentity identity) - : base(identity, [Role.SecurityAdmin, Role.ConfigureAdmin]) - { - } - } /// /// The Server Configuration Node Manager. diff --git a/Libraries/Opc.Ua.Server/Configuration/SystemConfigurationIdentity.cs b/Libraries/Opc.Ua.Server/Configuration/SystemConfigurationIdentity.cs new file mode 100644 index 000000000..b43f8d893 --- /dev/null +++ b/Libraries/Opc.Ua.Server/Configuration/SystemConfigurationIdentity.cs @@ -0,0 +1,50 @@ +/* ======================================================================== + * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. + * + * OPC Foundation MIT License 1.00 + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * The complete license agreement can be found here: + * http://opcfoundation.org/License/MIT/1.00/ + * ======================================================================*/ + +#if !NET9_0_OR_GREATER +#endif + +namespace Opc.Ua.Server +{ + /// + /// Privileged identity which can access the system configuration. + /// + public class SystemConfigurationIdentity : RoleBasedIdentity + { + /// + /// Create a user identity with the privilege + /// to modify the system configuration. + /// + /// The user identity. + public SystemConfigurationIdentity(IUserIdentity identity) + : base(identity, [Role.SecurityAdmin, Role.ConfigureAdmin]) + { + } + } +} diff --git a/Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs similarity index 100% rename from Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManager.cs rename to Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs diff --git a/Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManagerAsync.cs b/Libraries/Opc.Ua.Server/NodeManager/CustomNodeManagerAsync.cs similarity index 100% rename from Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManagerAsync.cs rename to Libraries/Opc.Ua.Server/NodeManager/CustomNodeManagerAsync.cs diff --git a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs index 38ebb8fee..0a672abc9 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs @@ -648,6 +648,7 @@ public virtual object GetManagerHandle(NodeId nodeId, out INodeManager nodeManag /// /// Returns node handle and its node manager. /// + [Obsolete("Use GetManagerHandleAsync instead.")] public virtual object GetManagerHandle(NodeId nodeId, out IAsyncNodeManager nodeManager) { (object handle, IAsyncNodeManager nodeManager) result = @@ -706,6 +707,7 @@ public virtual object GetManagerHandle(NodeId nodeId, out IAsyncNodeManager node /// /// Adds the references to the target. /// + [Obsolete("Use AddReferencesAsync instead.")] public virtual void AddReferences(NodeId sourceId, IList references) { AddReferencesAsync(sourceId, references).AsTask().GetAwaiter().GetResult(); @@ -890,6 +892,7 @@ public virtual void UnregisterNodes( /// /// is null. /// + [Obsolete("Use TranslateBrowsePathsToNodeIdsAsync instead.")] public virtual void TranslateBrowsePathsToNodeIds( OperationContext context, BrowsePathCollection browsePaths, @@ -2545,35 +2548,6 @@ await nodeManager.ConditionRefreshAsync(context, monitoredItems, cancellationTok } } - /// - /// Creates a set of monitored items. - /// - /// is null. - /// - /// - public virtual void CreateMonitoredItems( - OperationContext context, - uint subscriptionId, - double publishingInterval, - TimestampsToReturn timestampsToReturn, - IList itemsToCreate, - IList errors, - IList filterResults, - IList monitoredItems, - bool createDurable) - { - CreateMonitoredItemsAsync( - context, - subscriptionId, - publishingInterval, - timestampsToReturn, - itemsToCreate, - errors, - filterResults, - monitoredItems, - createDurable).AsTask().GetAwaiter().GetResult(); - } - /// /// Creates a set of monitored items. /// @@ -2983,28 +2957,6 @@ await manager.SubscribeToAllEventsAsync( } } - /// - /// Modifies a set of monitored items. - /// - /// is null. - /// - public virtual void ModifyMonitoredItems( - OperationContext context, - TimestampsToReturn timestampsToReturn, - IList monitoredItems, - IList itemsToModify, - IList errors, - IList filterResults) - { - ModifyMonitoredItemsAsync( - context, - timestampsToReturn, - monitoredItems, - itemsToModify, - errors, - filterResults).AsTask().GetAwaiter().GetResult(); - } - /// /// Modifies a set of monitored items. /// @@ -3203,23 +3155,6 @@ await nodeManager.SubscribeToAllEventsAsync( } } - /// - /// Transfers a set of monitored items. - /// - /// is null. - public virtual void TransferMonitoredItems( - OperationContext context, - bool sendInitialValues, - IList monitoredItems, - IList errors) - { - TransferMonitoredItemsAsync( - context, - sendInitialValues, - monitoredItems, - errors).AsTask().GetAwaiter().GetResult(); - } - /// /// Transfers a set of monitored items. /// @@ -3269,23 +3204,6 @@ await nodeManager.TransferMonitoredItemsAsync( } } - /// - /// Deletes a set of monitored items. - /// - /// is null. - public virtual void DeleteMonitoredItems( - OperationContext context, - uint subscriptionId, - IList itemsToDelete, - IList errors) - { - DeleteMonitoredItemsAsync( - context, - subscriptionId, - itemsToDelete, - errors).AsTask().GetAwaiter().GetResult(); - } - /// /// Deletes a set of monitored items. /// diff --git a/Libraries/Opc.Ua.Server/Server/IServerInternal.cs b/Libraries/Opc.Ua.Server/Server/IServerInternal.cs index dee2635d4..18f6d626f 100644 --- a/Libraries/Opc.Ua.Server/Server/IServerInternal.cs +++ b/Libraries/Opc.Ua.Server/Server/IServerInternal.cs @@ -240,7 +240,8 @@ ValueTask CloseSessionAsync( /// Deletes the specified subscription. /// /// The subscription identifier. - void DeleteSubscription(uint subscriptionId); + /// The cancellation token. + ValueTask DeleteSubscriptionAsync(uint subscriptionId, CancellationToken cancellationToken = default); /// /// Called by any component to report a global event. diff --git a/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs b/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs index 6032cb8b0..d6096626c 100644 --- a/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs +++ b/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs @@ -494,7 +494,8 @@ public async ValueTask CloseSessionAsync( { await NodeManager.SessionClosingAsync(context, sessionId, deleteSubscriptions, cancellationToken) .ConfigureAwait(false); - SubscriptionManager.SessionClosing(context, sessionId, deleteSubscriptions); + await SubscriptionManager.SessionClosingAsync(context, sessionId, deleteSubscriptions, cancellationToken) + .ConfigureAwait(false); SessionManager.CloseSession(sessionId); } @@ -502,9 +503,10 @@ await NodeManager.SessionClosingAsync(context, sessionId, deleteSubscriptions, c /// Deletes the specified subscription. /// /// The subscription identifier. - public void DeleteSubscription(uint subscriptionId) + /// The cancellation token + public async ValueTask DeleteSubscriptionAsync(uint subscriptionId, CancellationToken cancellationToken = default) { - SubscriptionManager.DeleteSubscription(null, subscriptionId); + await SubscriptionManager.DeleteSubscriptionAsync(null, subscriptionId, cancellationToken).ConfigureAwait(false); } /// diff --git a/Libraries/Opc.Ua.Server/Server/StandardServer.cs b/Libraries/Opc.Ua.Server/Server/StandardServer.cs index 834e47ea4..0ebccd7f5 100644 --- a/Libraries/Opc.Ua.Server/Server/StandardServer.cs +++ b/Libraries/Opc.Ua.Server/Server/StandardServer.cs @@ -1542,7 +1542,7 @@ public override async Task HistoryUpdateAsync( /// /// Returns a object /// - public override Task CreateSubscriptionAsync( + public override async Task CreateSubscriptionAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, double requestedPublishingInterval, @@ -1560,7 +1560,7 @@ public override Task CreateSubscriptionAsync( try { - ServerInternal.SubscriptionManager.CreateSubscription( + CreateSubscriptionResponse response = await ServerInternal.SubscriptionManager.CreateSubscriptionAsync( context, requestedPublishingInterval, requestedLifetimeCount, @@ -1568,19 +1568,11 @@ public override Task CreateSubscriptionAsync( maxNotificationsPerPublish, publishingEnabled, priority, - out uint subscriptionId, - out double revisedPublishingInterval, - out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount); + ct).ConfigureAwait(false); - return Task.FromResult(new CreateSubscriptionResponse - { - ResponseHeader = CreateResponse(requestHeader, context.StringTable), - SubscriptionId = subscriptionId, - RevisedPublishingInterval = revisedPublishingInterval, - RevisedLifetimeCount = revisedLifetimeCount, - RevisedMaxKeepAliveCount = revisedMaxKeepAliveCount - }); + response.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return response; } catch (ServiceResultException e) { @@ -1610,7 +1602,7 @@ public override Task CreateSubscriptionAsync( /// The list of Subscriptions to transfer. /// If the initial values should be sent. /// The cancellation token. - public override Task TransferSubscriptionsAsync( + public override async Task TransferSubscriptionsAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, UInt32Collection subscriptionIds, @@ -1626,19 +1618,15 @@ public override Task TransferSubscriptionsAsync( { ValidateOperationLimits(subscriptionIds); - ServerInternal.SubscriptionManager.TransferSubscriptions( + TransferSubscriptionsResponse response = await ServerInternal.SubscriptionManager.TransferSubscriptionsAsync( context, subscriptionIds, sendInitialValues, - out TransferResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + ct).ConfigureAwait(false); - return Task.FromResult(new TransferSubscriptionsResponse - { - ResponseHeader = CreateResponse(requestHeader, context.StringTable), - Results = results, - DiagnosticInfos = diagnosticInfos - }); + response.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return response; } catch (ServiceResultException e) { @@ -1670,7 +1658,7 @@ public override Task TransferSubscriptionsAsync( /// /// Returns a object /// - public override Task DeleteSubscriptionsAsync( + public override async Task DeleteSubscriptionsAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, UInt32Collection subscriptionIds, @@ -1685,18 +1673,14 @@ public override Task DeleteSubscriptionsAsync( { ValidateOperationLimits(subscriptionIds); - ServerInternal.SubscriptionManager.DeleteSubscriptions( + DeleteSubscriptionsResponse response = await ServerInternal.SubscriptionManager.DeleteSubscriptionsAsync( context, subscriptionIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); + ct).ConfigureAwait(false); - return Task.FromResult(new DeleteSubscriptionsResponse - { - ResponseHeader = CreateResponse(requestHeader, context.StringTable), - Results = results, - DiagnosticInfos = diagnosticInfos - }); + response.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return response; } catch (ServiceResultException e) { @@ -2073,7 +2057,7 @@ public override Task SetTriggeringAsync( /// /// Returns a object /// - public override Task CreateMonitoredItemsAsync( + public override async Task CreateMonitoredItemsAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, uint subscriptionId, @@ -2090,20 +2074,16 @@ public override Task CreateMonitoredItemsAsync( { ValidateOperationLimits(itemsToCreate, OperationLimits.MaxMonitoredItemsPerCall); - ServerInternal.SubscriptionManager.CreateMonitoredItems( + CreateMonitoredItemsResponse result = await ServerInternal.SubscriptionManager.CreateMonitoredItemsAsync( context, subscriptionId, timestampsToReturn, itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + ct).ConfigureAwait(false); - return Task.FromResult(new CreateMonitoredItemsResponse - { - Results = results, - DiagnosticInfos = diagnosticInfos, - ResponseHeader = CreateResponse(requestHeader, context.StringTable) - }); + result.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return result; } catch (ServiceResultException e) { @@ -2137,7 +2117,7 @@ public override Task CreateMonitoredItemsAsync( /// /// Returns a object /// - public override Task ModifyMonitoredItemsAsync( + public override async Task ModifyMonitoredItemsAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, uint subscriptionId, @@ -2154,20 +2134,16 @@ public override Task ModifyMonitoredItemsAsync( { ValidateOperationLimits(itemsToModify, OperationLimits.MaxMonitoredItemsPerCall); - ServerInternal.SubscriptionManager.ModifyMonitoredItems( + ModifyMonitoredItemsResponse response = await ServerInternal.SubscriptionManager.ModifyMonitoredItemsAsync( context, subscriptionId, timestampsToReturn, itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + ct).ConfigureAwait(false); - return Task.FromResult(new ModifyMonitoredItemsResponse - { - Results = results, - DiagnosticInfos = diagnosticInfos, - ResponseHeader = CreateResponse(requestHeader, context.StringTable) - }); + response.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return response; } catch (ServiceResultException e) { @@ -2200,7 +2176,7 @@ public override Task ModifyMonitoredItemsAsync( /// /// Returns a object /// - public override Task DeleteMonitoredItemsAsync( + public override async Task DeleteMonitoredItemsAsync( SecureChannelContext secureChannelContext, RequestHeader requestHeader, uint subscriptionId, @@ -2216,19 +2192,15 @@ public override Task DeleteMonitoredItemsAsync( { ValidateOperationLimits(monitoredItemIds, OperationLimits.MaxMonitoredItemsPerCall); - ServerInternal.SubscriptionManager.DeleteMonitoredItems( + DeleteMonitoredItemsResponse response = await ServerInternal.SubscriptionManager.DeleteMonitoredItemsAsync( context, subscriptionId, monitoredItemIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); + ct).ConfigureAwait(false); - return Task.FromResult(new DeleteMonitoredItemsResponse - { - Results = results, - DiagnosticInfos = diagnosticInfos, - ResponseHeader = CreateResponse(requestHeader, context.StringTable) - }); + response.ResponseHeader = CreateResponse(requestHeader, context.StringTable); + + return response; } catch (ServiceResultException e) { @@ -2448,109 +2420,109 @@ await configuration if (m_registrationEndpoints != null) { foreach (ConfiguredEndpoint endpoint in m_registrationEndpoints.Endpoints) - { - RegistrationClient client = null; - int i = 0; + { + RegistrationClient client = null; + int i = 0; - while (i++ < 2) + while (i++ < 2) + { + try { - try - { - // update from the server. - bool updateRequired = true; + // update from the server. + bool updateRequired = true; - lock (m_registrationLock) - { - updateRequired = endpoint.UpdateBeforeConnect; - } + lock (m_registrationLock) + { + updateRequired = endpoint.UpdateBeforeConnect; + } - if (updateRequired) - { - await endpoint.UpdateFromServerAsync(MessageContext.Telemetry, ct).ConfigureAwait(false); - } + if (updateRequired) + { + await endpoint.UpdateFromServerAsync(MessageContext.Telemetry, ct).ConfigureAwait(false); + } - lock (m_registrationLock) - { - endpoint.UpdateBeforeConnect = false; - } + lock (m_registrationLock) + { + endpoint.UpdateBeforeConnect = false; + } - var requestHeader = new RequestHeader + var requestHeader = new RequestHeader + { + Timestamp = DateTime.UtcNow + }; + + // create the client. + X509Certificate2 instanceCertificate = + InstanceCertificateTypesProvider.GetInstanceCertificate( + endpoint.Description?.SecurityPolicyUri ?? + SecurityPolicies.None); + client = await RegistrationClient.CreateAsync( + configuration, + endpoint.Description, + endpoint.Configuration, + instanceCertificate, + ct: ct).ConfigureAwait(false); + + client.OperationTimeout = 10000; + + // register the server. + if (m_useRegisterServer2) + { + var discoveryConfiguration = new ExtensionObjectCollection(); + var mdnsDiscoveryConfig = new MdnsDiscoveryConfiguration { - Timestamp = DateTime.UtcNow + ServerCapabilities = configuration.ServerConfiguration + .ServerCapabilities, + MdnsServerName = Utils.GetHostName() }; - - // create the client. - X509Certificate2 instanceCertificate = - InstanceCertificateTypesProvider.GetInstanceCertificate( - endpoint.Description?.SecurityPolicyUri ?? - SecurityPolicies.None); - client = await RegistrationClient.CreateAsync( - configuration, - endpoint.Description, - endpoint.Configuration, - instanceCertificate, - ct: ct).ConfigureAwait(false); - - client.OperationTimeout = 10000; - - // register the server. - if (m_useRegisterServer2) - { - var discoveryConfiguration = new ExtensionObjectCollection(); - var mdnsDiscoveryConfig = new MdnsDiscoveryConfiguration - { - ServerCapabilities = configuration.ServerConfiguration - .ServerCapabilities, - MdnsServerName = Utils.GetHostName() - }; - var extensionObject = new ExtensionObject(mdnsDiscoveryConfig); - discoveryConfiguration.Add(extensionObject); - await client.RegisterServer2Async( - requestHeader, - m_registrationInfo, - discoveryConfiguration, - ct).ConfigureAwait(false); - } - else - { - await client.RegisterServerAsync( - requestHeader, - m_registrationInfo, - ct) - .ConfigureAwait(false); - } - - m_registeredWithDiscoveryServer = m_registrationInfo.IsOnline; - return true; + var extensionObject = new ExtensionObject(mdnsDiscoveryConfig); + discoveryConfiguration.Add(extensionObject); + await client.RegisterServer2Async( + requestHeader, + m_registrationInfo, + discoveryConfiguration, + ct).ConfigureAwait(false); } - catch (Exception e) + else { - m_logger.LogWarning( - "RegisterServer{Api} failed for {EndpointUrl}. Exception={ErrorMessage}", - m_useRegisterServer2 ? "2" : string.Empty, - endpoint.EndpointUrl, - e.Message); - m_useRegisterServer2 = !m_useRegisterServer2; + await client.RegisterServerAsync( + requestHeader, + m_registrationInfo, + ct) + .ConfigureAwait(false); } - finally + + m_registeredWithDiscoveryServer = m_registrationInfo.IsOnline; + return true; + } + catch (Exception e) + { + m_logger.LogWarning( + "RegisterServer{Api} failed for {EndpointUrl}. Exception={ErrorMessage}", + m_useRegisterServer2 ? "2" : string.Empty, + endpoint.EndpointUrl, + e.Message); + m_useRegisterServer2 = !m_useRegisterServer2; + } + finally + { + if (client != null) { - if (client != null) + try { - try - { - await client.CloseAsync(ct).ConfigureAwait(false); - client = null; - } - catch (Exception e) - { - m_logger.LogWarning( - "Could not cleanly close connection with LDS. Exception={ErrorMessage}", - e.Message); - } + await client.CloseAsync(ct).ConfigureAwait(false); + client = null; + } + catch (Exception e) + { + m_logger.LogWarning( + "Could not cleanly close connection with LDS. Exception={ErrorMessage}", + e.Message); } } } } + } // retry to start with RegisterServer2 if both failed m_useRegisterServer2 = true; } diff --git a/Libraries/Opc.Ua.Server/Subscription/ISubscription.cs b/Libraries/Opc.Ua.Server/Subscription/ISubscription.cs index aaecfb3ea..eaa8297a7 100644 --- a/Libraries/Opc.Ua.Server/Subscription/ISubscription.cs +++ b/Libraries/Opc.Ua.Server/Subscription/ISubscription.cs @@ -158,31 +158,28 @@ void Modify( /// /// Deletes the monitored items in a subscription. /// - void DeleteMonitoredItems( + ValueTask DeleteMonitoredItemsAsync( OperationContext context, UInt32Collection monitoredItemIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Modifies monitored items in a subscription. /// - void ModifyMonitoredItems( + ValueTask ModifyMonitoredItemsAsync( OperationContext context, TimestampsToReturn timestampsToReturn, MonitoredItemModifyRequestCollection itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Adds monitored items to a subscription. /// - void CreateMonitoredItems( + ValueTask CreateMonitoredItemsAsync( OperationContext context, TimestampsToReturn timestampsToReturn, MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Gets the monitored items for the subscription. @@ -212,7 +209,7 @@ void CreateMonitoredItems( /// /// Deletes the subscription. /// - void Delete(OperationContext context); + ValueTask DeleteAsync(OperationContext context, CancellationToken cancellationToken = default); /// /// Verifies that a condition refresh operation is permitted. @@ -252,7 +249,8 @@ NotificationMessage Publish( /// /// The session to which the subscription is transferred. /// Whether the first Publish response shall contain current values. - void TransferSession(OperationContext context, bool sendInitialValues); + /// The cancellation token. + ValueTask TransferSessionAsync(OperationContext context, bool sendInitialValues, CancellationToken cancellationToken = default); /// /// Updates the triggers for the monitored item. diff --git a/Libraries/Opc.Ua.Server/Subscription/ISubscriptionManager.cs b/Libraries/Opc.Ua.Server/Subscription/ISubscriptionManager.cs index a42d402b2..009ea13e9 100644 --- a/Libraries/Opc.Ua.Server/Subscription/ISubscriptionManager.cs +++ b/Libraries/Opc.Ua.Server/Subscription/ISubscriptionManager.cs @@ -70,7 +70,7 @@ ServiceResult SetSubscriptionDurable( /// /// Creates a new subscription. /// - void CreateSubscription( + ValueTask CreateSubscriptionAsync( OperationContext context, double requestedPublishingInterval, uint requestedLifetimeCount, @@ -78,10 +78,7 @@ void CreateSubscription( uint maxNotificationsPerPublish, bool publishingEnabled, byte priority, - out uint subscriptionId, - out double revisedPublishingInterval, - out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount); + CancellationToken cancellationToken = default); /// /// Starts up the manager makes it ready to create subscriptions. @@ -106,11 +103,10 @@ void CreateSubscription( /// /// Deletes group of subscriptions. /// - void DeleteSubscriptions( + ValueTask DeleteSubscriptionsAsync( OperationContext context, UInt32Collection subscriptionIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Publishes a subscription. @@ -148,12 +144,11 @@ void SetPublishingMode( /// /// Attaches a groups of subscriptions to a different session. /// - void TransferSubscriptions( + ValueTask TransferSubscriptionsAsync( OperationContext context, UInt32Collection subscriptionIds, bool sendInitialValues, - out TransferResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Republishes a previously published notification message. @@ -180,34 +175,31 @@ void SetTriggering( /// /// Adds monitored items to a subscription. /// - void CreateMonitoredItems( + ValueTask CreateMonitoredItemsAsync( OperationContext context, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Modifies monitored items in a subscription. /// - void ModifyMonitoredItems( + ValueTask ModifyMonitoredItemsAsync( OperationContext context, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemModifyRequestCollection itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Deletes the monitored items in a subscription. /// - void DeleteMonitoredItems( + ValueTask DeleteMonitoredItemsAsync( OperationContext context, uint subscriptionId, UInt32Collection monitoredItemIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); + CancellationToken cancellationToken = default); /// /// Changes the monitoring mode for a set of items. @@ -222,12 +214,16 @@ void DeleteMonitoredItems( /// /// Signals that a session is closing. /// - void SessionClosing(OperationContext context, NodeId sessionId, bool deleteSubscriptions); + ValueTask SessionClosingAsync( + OperationContext context, + NodeId sessionId, + bool deleteSubscriptions, + CancellationToken cancellationToken); /// /// Deletes the specified subscription. /// - StatusCode DeleteSubscription(OperationContext context, uint subscriptionId); + ValueTask DeleteSubscriptionAsync(OperationContext context, uint subscriptionId, CancellationToken cancellationToken = default); /// /// Refreshes the conditions for the specified subscription. diff --git a/Libraries/Opc.Ua.Server/Subscription/MonitoredItem/MonitoredItem.cs b/Libraries/Opc.Ua.Server/Subscription/MonitoredItem/MonitoredItem.cs index 8bc072b74..b53b5ad67 100644 --- a/Libraries/Opc.Ua.Server/Subscription/MonitoredItem/MonitoredItem.cs +++ b/Libraries/Opc.Ua.Server/Subscription/MonitoredItem/MonitoredItem.cs @@ -866,11 +866,14 @@ public virtual void QueueValue(DataValue value, ServiceResult error, bool ignore // make a shallow copy of the value. if (value != null) { - m_logger.LogTrace( - Utils.TraceMasks.OperationDetail, - "RECEIVED VALUE[{MonitoredItemId}] Value={Value}", - Id, - value.WrappedValue); + if (m_logger.IsEnabled(LogLevel.Trace)) + { + m_logger.LogTrace( + Utils.TraceMasks.OperationDetail, + "RECEIVED VALUE[{MonitoredItemId}] Value={Value}", + Id, + value.WrappedValue); + } value = new DataValue { diff --git a/Libraries/Opc.Ua.Server/Subscription/Subscription.cs b/Libraries/Opc.Ua.Server/Subscription/Subscription.cs index 05965a2fa..7c497ee54 100644 --- a/Libraries/Opc.Ua.Server/Subscription/Subscription.cs +++ b/Libraries/Opc.Ua.Server/Subscription/Subscription.cs @@ -390,7 +390,7 @@ public int MonitoredItemCount /// /// Deletes the subscription. /// - public void Delete(OperationContext context) + public async ValueTask DeleteAsync(OperationContext context, CancellationToken cancellationToken = default) { // delete the diagnostics. if (m_diagnosticsId != null && !m_diagnosticsId.IsNullNodeId) @@ -400,34 +400,30 @@ public void Delete(OperationContext context) .DeleteSubscriptionDiagnostics(systemContext, m_diagnosticsId); } - lock (m_lock) + try { - try - { - TraceState(LogLevel.Information, TraceStateId.Deleted, "DELETED"); - - // the context may be null if the server is cleaning up expired subscriptions. - // in this case we create a context with a dummy request and use the current session. - if (context == null) - { - var requestHeader = new RequestHeader - { - ReturnDiagnostics = (int)DiagnosticsMasks.OperationSymbolicIdAndText - }; - context = new OperationContext(requestHeader, null, RequestType.Unknown); - } + TraceState(LogLevel.Information, TraceStateId.Deleted, "DELETED"); - DeleteMonitoredItems( - context, - [.. m_monitoredItems.Keys], - true, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos); - } - catch (Exception e) + // the context may be null if the server is cleaning up expired subscriptions. + // in this case we create a context with a dummy request and use the current session. + if (context == null) { - m_logger.LogError(e, "Delete items for subscription failed."); + var requestHeader = new RequestHeader + { + ReturnDiagnostics = (int)DiagnosticsMasks.OperationSymbolicIdAndText + }; + context = new OperationContext(requestHeader, null, RequestType.Unknown); } + + await DeleteMonitoredItemsAsync( + context, + [.. m_monitoredItems.Keys], + true, + cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + m_logger.LogError(e, "Delete items for subscription failed."); } } @@ -579,7 +575,8 @@ public PublishingState PublishTimerExpired() /// /// The session to which the subscription is transferred. /// Whether the first Publish response shall contain current values. - public void TransferSession(OperationContext context, bool sendInitialValues) + /// The cancellation token. + public async ValueTask TransferSessionAsync(OperationContext context, bool sendInitialValues, CancellationToken cancellationToken = default) { // locked by caller Session = context.Session; @@ -591,8 +588,9 @@ public void TransferSession(OperationContext context, bool sendInitialValues) errors.Add(null); } - m_server.NodeManager - .TransferMonitoredItems(context, sendInitialValues, monitoredItems, errors); + await m_server.NodeManager + .TransferMonitoredItemsAsync(context, sendInitialValues, monitoredItems, errors, cancellationToken) + .ConfigureAwait(false); int badTransfers = 0; for (int ii = 0; ii < errors.Count; ii++) @@ -1540,12 +1538,11 @@ public void SetTriggering( /// Adds monitored items to a subscription. /// /// is null. - public void CreateMonitoredItems( + public async ValueTask CreateMonitoredItemsAsync( OperationContext context, TimestampsToReturn timestampsToReturn, MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { if (context == null) { @@ -1559,6 +1556,9 @@ public void CreateMonitoredItems( int count = itemsToCreate.Count; + MonitoredItemCreateResultCollection results; + DiagnosticInfoCollection diagnosticInfos; + lock (m_lock) { // check session. @@ -1580,7 +1580,7 @@ public void CreateMonitoredItems( filterResults.Add(null); } - m_server.NodeManager.CreateMonitoredItems( + await m_server.NodeManager.CreateMonitoredItemsAsync( context, Id, m_publishingInterval, @@ -1589,7 +1589,8 @@ public void CreateMonitoredItems( errors, filterResults, monitoredItems, - IsDurable); + IsDurable, + cancellationToken).ConfigureAwait(false); // allocate results. bool diagnosticsExist = false; @@ -1670,6 +1671,12 @@ public void CreateMonitoredItems( TraceState(LogLevel.Information, TraceStateId.Items, "ITEMS CREATED"); } + + return new CreateMonitoredItemsResponse + { + Results = results, + DiagnosticInfos = diagnosticInfos + }; } /// @@ -1748,12 +1755,11 @@ private void ModifyItemMonitoringMode( /// Modifies monitored items in a subscription. /// /// is null. - public void ModifyMonitoredItems( + public async ValueTask ModifyMonitoredItemsAsync( OperationContext context, TimestampsToReturn timestampsToReturn, MonitoredItemModifyRequestCollection itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { if (context == null) { @@ -1769,8 +1775,8 @@ public void ModifyMonitoredItems( // allocate results. bool diagnosticsExist = false; - results = new MonitoredItemModifyResultCollection(count); - diagnosticInfos = null; + var results = new MonitoredItemModifyResultCollection(count); + DiagnosticInfoCollection diagnosticInfos = null; if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { @@ -1837,13 +1843,15 @@ public void ModifyMonitoredItems( // update items. if (validItems) { - m_server.NodeManager.ModifyMonitoredItems( + await m_server.NodeManager.ModifyMonitoredItemsAsync( context, timestampsToReturn, monitoredItems, itemsToModify, errors, - filterResults); + filterResults, + cancellationToken) + .ConfigureAwait(false); } lock (m_lock) @@ -1908,35 +1916,38 @@ public void ModifyMonitoredItems( TraceState(LogLevel.Information, TraceStateId.Items, "ITEMS MODIFIED"); } + + return new ModifyMonitoredItemsResponse + { + Results = results, + DiagnosticInfos = diagnosticInfos + }; } /// /// Deletes the monitored items in a subscription. /// - public void DeleteMonitoredItems( + public ValueTask DeleteMonitoredItemsAsync( OperationContext context, UInt32Collection monitoredItemIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { - DeleteMonitoredItems( + return DeleteMonitoredItemsAsync( context, monitoredItemIds, false, - out results, - out diagnosticInfos); + cancellationToken); } /// /// Deletes the monitored items in a subscription. /// /// is null. - private void DeleteMonitoredItems( + private async ValueTask DeleteMonitoredItemsAsync( OperationContext context, UInt32Collection monitoredItemIds, bool doNotCheckSession, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { if (context == null) { @@ -1951,8 +1962,8 @@ private void DeleteMonitoredItems( int count = monitoredItemIds.Count; bool diagnosticsExist = false; - results = new StatusCodeCollection(count); - diagnosticInfos = null; + var results = new StatusCodeCollection(count); + DiagnosticInfoCollection diagnosticInfos = null; if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { @@ -2043,7 +2054,8 @@ private void DeleteMonitoredItems( // update items. if (validItems) { - m_server.NodeManager.DeleteMonitoredItems(context, Id, monitoredItems, errors); + await m_server.NodeManager.DeleteMonitoredItemsAsync(context, Id, monitoredItems, errors, cancellationToken) + .ConfigureAwait(false); } //dispose monitored Items @@ -2097,6 +2109,12 @@ private void DeleteMonitoredItems( TraceState(LogLevel.Information, TraceStateId.Items, "ITEMS DELETED"); } + + return new DeleteMonitoredItemsResponse + { + Results = results, + DiagnosticInfos = diagnosticInfos + }; } /// diff --git a/Libraries/Opc.Ua.Server/Subscription/SubscriptionManager.cs b/Libraries/Opc.Ua.Server/Subscription/SubscriptionManager.cs index 07e622919..3a55c687d 100644 --- a/Libraries/Opc.Ua.Server/Subscription/SubscriptionManager.cs +++ b/Libraries/Opc.Ua.Server/Subscription/SubscriptionManager.cs @@ -32,6 +32,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -236,7 +237,7 @@ await RestoreSubscriptionsAsync(cancellationToken) // TODO: Ensure shutdown awaits completion and a cancellation token is passed _ = Task.Factory.StartNew( - () => PublishSubscriptions(m_publishingResolution), + () => PublishSubscriptionsAsync(m_publishingResolution), default, TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); @@ -437,7 +438,7 @@ protected virtual async ValueTask RestoreSubscriptionAsync( storedSubscription.MaxNotificationsPerPublish); // create the subscription. - var subscription = await Subscription.RestoreAsync(m_server, storedSubscription, cancellationToken) + Subscription subscription = await Subscription.RestoreAsync(m_server, storedSubscription, cancellationToken) .ConfigureAwait(false); uint publishingIntervalCount; @@ -468,10 +469,11 @@ protected virtual async ValueTask RestoreSubscriptionAsync( /// /// Signals that a session is closing. /// - public virtual void SessionClosing( + public virtual async ValueTask SessionClosingAsync( OperationContext context, NodeId sessionId, - bool deleteSubscriptions) + bool deleteSubscriptions, + CancellationToken cancellationToken) { IList subscriptionsToDelete = null; @@ -513,7 +515,7 @@ public virtual void SessionClosing( RaiseSubscriptionEvent(subscription, true); // delete subscription. - subscription.Delete(context); + await subscription.DeleteAsync(context, cancellationToken).ConfigureAwait(false); // get the count for the diagnostics. uint publishingIntervalCount = GetPublishingIntervalCount(); @@ -528,7 +530,7 @@ public virtual void SessionClosing( // mark the subscriptions as abandoned. else { - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { (m_abandonedSubscriptions ??= []).Add(subscription); @@ -670,11 +672,11 @@ await subscription.ConditionRefresh2Async(monitoredItemId, cancellationToken) /// Deletes the specified subscription. /// /// - public StatusCode DeleteSubscription(OperationContext context, uint subscriptionId) + public async ValueTask DeleteSubscriptionAsync(OperationContext context, uint subscriptionId, CancellationToken cancellationToken = default) { ISubscription subscription = null; - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { // remove from publish queue. @@ -730,7 +732,7 @@ public StatusCode DeleteSubscription(OperationContext context, uint subscription RaiseSubscriptionEvent(subscription, true); // delete subscription. - subscription.Delete(context); + await subscription.DeleteAsync(context, cancellationToken).ConfigureAwait(false); // get the count for the diagnostics. uint publishingIntervalCount = GetPublishingIntervalCount(); @@ -804,7 +806,7 @@ private uint GetPublishingIntervalCount() /// Creates a new subscription. /// /// - public virtual void CreateSubscription( + public virtual async ValueTask CreateSubscriptionAsync( OperationContext context, double requestedPublishingInterval, uint requestedLifetimeCount, @@ -812,16 +814,18 @@ public virtual void CreateSubscription( uint maxNotificationsPerPublish, bool publishingEnabled, byte priority, - out uint subscriptionId, - out double revisedPublishingInterval, - out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount) + CancellationToken cancellationToken = default) { if (m_subscriptions.Count >= m_maxSubscriptionCount) { throw new ServiceResultException(StatusCodes.BadTooManySubscriptions); } + uint subscriptionId; + double revisedPublishingInterval; + uint revisedLifetimeCount; + uint revisedMaxKeepAliveCount; + uint publishingIntervalCount = 0; // get session from context. @@ -859,7 +863,7 @@ public virtual void CreateSubscription( priority, publishingEnabled); - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { // save subscription. @@ -925,26 +929,33 @@ public virtual void CreateSubscription( // raise subscription event. RaiseSubscriptionEvent(subscription, false); + + return new CreateSubscriptionResponse + { + SubscriptionId = subscriptionId, + RevisedPublishingInterval = revisedPublishingInterval, + RevisedLifetimeCount = revisedLifetimeCount, + RevisedMaxKeepAliveCount = revisedMaxKeepAliveCount + }; } /// /// Deletes group of subscriptions. /// - public void DeleteSubscriptions( + public async ValueTask DeleteSubscriptionsAsync( OperationContext context, UInt32Collection subscriptionIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { bool diagnosticsExist = false; - results = new StatusCodeCollection(subscriptionIds.Count); - diagnosticInfos = new DiagnosticInfoCollection(subscriptionIds.Count); + var results = new StatusCodeCollection(subscriptionIds.Count); + var diagnosticInfos = new DiagnosticInfoCollection(subscriptionIds.Count); foreach (uint subscriptionId in subscriptionIds) { try { - StatusCode result = DeleteSubscription(context, subscriptionId); + StatusCode result = await DeleteSubscriptionAsync(context, subscriptionId, cancellationToken).ConfigureAwait(false); results.Add(result); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) @@ -979,6 +990,12 @@ public void DeleteSubscriptions( { diagnosticInfos.Clear(); } + + return new DeleteSubscriptionsResponse + { + Results = results, + DiagnosticInfos = diagnosticInfos + }; } /// @@ -1342,15 +1359,14 @@ public void SetPublishingMode( /// /// Attaches a groups of subscriptions to a different session. /// - public void TransferSubscriptions( + public async ValueTask TransferSubscriptionsAsync( OperationContext context, UInt32Collection subscriptionIds, bool sendInitialValues, - out TransferResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { - results = []; - diagnosticInfos = []; + var results = new TransferResultCollection(); + var diagnosticInfos = new DiagnosticInfoCollection(); m_logger.LogInformation( "TransferSubscriptions to SessionId={SessionId}, Count={Count}, sendInitialValues={SendInitialValues}", @@ -1432,10 +1448,10 @@ public void TransferSubscriptions( } // transfer session, add subscription to publish queue - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { - subscription.TransferSession(context, sendInitialValues); + await subscription.TransferSessionAsync(context, sendInitialValues, cancellationToken).ConfigureAwait(false); // remove from queue in old session if (ownerSession != null && @@ -1532,7 +1548,7 @@ public void TransferSubscriptions( } } - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { // trigger publish response to return status immediately @@ -1606,6 +1622,11 @@ public void TransferSubscriptions( m_logger); } } + return new TransferSubscriptionsResponse + { + Results = results, + DiagnosticInfos = diagnosticInfos + }; } /// @@ -1665,13 +1686,12 @@ public void SetTriggering( /// Adds monitored items to a subscription. /// /// - public void CreateMonitoredItems( + public async ValueTask CreateMonitoredItemsAsync( OperationContext context, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { // find subscription. if (!m_subscriptions.TryGetValue(subscriptionId, out ISubscription subscription)) @@ -1682,12 +1702,11 @@ public void CreateMonitoredItems( int currentMonitoredItemCount = subscription.MonitoredItemCount; // create the items. - subscription.CreateMonitoredItems( + CreateMonitoredItemsResponse response = await subscription.CreateMonitoredItemsAsync( context, timestampsToReturn, itemsToCreate, - out results, - out diagnosticInfos); + cancellationToken).ConfigureAwait(false); int monitoredItemCountIncrement = subscription.MonitoredItemCount - currentMonitoredItemCount; @@ -1701,19 +1720,20 @@ public void CreateMonitoredItems( UpdateCurrentMonitoredItemsCount(diagnostics, monitoredItemCountIncrement); } } + + return response; } /// /// Modifies monitored items in a subscription. /// /// - public void ModifyMonitoredItems( + public ValueTask ModifyMonitoredItemsAsync( OperationContext context, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemModifyRequestCollection itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { // find subscription. if (!m_subscriptions.TryGetValue(subscriptionId, out ISubscription subscription)) @@ -1722,24 +1742,22 @@ public void ModifyMonitoredItems( } // modify the items. - subscription.ModifyMonitoredItems( + return subscription.ModifyMonitoredItemsAsync( context, timestampsToReturn, itemsToModify, - out results, - out diagnosticInfos); + cancellationToken); } /// /// Deletes the monitored items in a subscription. /// /// - public void DeleteMonitoredItems( + public async ValueTask DeleteMonitoredItemsAsync( OperationContext context, uint subscriptionId, UInt32Collection monitoredItemIds, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) + CancellationToken cancellationToken = default) { // find subscription. if (!m_subscriptions.TryGetValue(subscriptionId, out ISubscription subscription)) @@ -1750,11 +1768,10 @@ public void DeleteMonitoredItems( int currentMonitoredItemCount = subscription.MonitoredItemCount; // create the items. - subscription.DeleteMonitoredItems( + DeleteMonitoredItemsResponse response = await subscription.DeleteMonitoredItemsAsync( context, monitoredItemIds, - out results, - out diagnosticInfos); + cancellationToken).ConfigureAwait(false); int monitoredItemCountIncrement = subscription.MonitoredItemCount - currentMonitoredItemCount; @@ -1768,6 +1785,8 @@ public void DeleteMonitoredItems( UpdateCurrentMonitoredItemsCount(diagnostics, monitoredItemCountIncrement); } } + + return response; } /// @@ -2037,7 +2056,7 @@ private bool ReturnPendingStatusMessage( /// /// Periodically checks if the sessions have timed out. /// - private void PublishSubscriptions(object data) + private async ValueTask PublishSubscriptionsAsync(int sleepCycle, CancellationToken cancellationToken = default) { try { @@ -2045,7 +2064,6 @@ private void PublishSubscriptions(object data) "Subscription - Publish Task {TaskId:X8} Started.", Task.CurrentId); - int sleepCycle = Convert.ToInt32(data, CultureInfo.InvariantCulture); int timeToWait = sleepCycle; while (true) @@ -2055,7 +2073,7 @@ private void PublishSubscriptions(object data) SessionPublishQueue[] queues = null; ISubscription[] abandonedSubscriptions = null; - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { // collect active session queues. @@ -2109,7 +2127,7 @@ private void PublishSubscriptions(object data) // schedule cleanup on a background thread. if (subscriptionsToDelete.Count > 0) { - m_semaphoreSlim.Wait(); + await m_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { for (int ii = 0; ii < subscriptionsToDelete.Count; ii++) @@ -2126,7 +2144,7 @@ private void PublishSubscriptions(object data) } } - if (m_shutdownEvent.WaitOne(timeToWait)) + if (m_shutdownEvent.WaitOne(0)) { m_logger.LogInformation( "Subscription - Publish Task {TaskId:X8} Exited Normally.", @@ -2134,8 +2152,7 @@ private void PublishSubscriptions(object data) break; } - int delay = (int)(DateTime.UtcNow - start).TotalMilliseconds; - timeToWait = sleepCycle; + await Task.Delay(timeToWait, cancellationToken).ConfigureAwait(false); } } catch (Exception e) @@ -2228,17 +2245,18 @@ internal static void CleanupSubscriptions( subscriptionsToDelete.Count); Task.Run( - () => CleanupSubscriptionsCore(server, subscriptionsToDelete, logger)); + () => CleanupSubscriptionsCoreAsync(server, subscriptionsToDelete, logger)); } } /// /// Deletes any expired subscriptions. /// - private static void CleanupSubscriptionsCore( + private static async ValueTask CleanupSubscriptionsCoreAsync( IServerInternal server, IList subscriptionsToDelete, - ILogger logger) + ILogger logger, + CancellationToken cancellationToken = default) { try { @@ -2246,7 +2264,7 @@ private static void CleanupSubscriptionsCore( foreach (ISubscription subscription in subscriptionsToDelete) { - server.DeleteSubscription(subscription.Id); + await server.DeleteSubscriptionAsync(subscription.Id, cancellationToken).ConfigureAwait(false); } logger.LogInformation("Server - CleanupSubscriptions Task Completed"); diff --git a/Tests/Opc.Ua.Client.Tests/ClientTestFramework.cs b/Tests/Opc.Ua.Client.Tests/ClientTestFramework.cs index 88b8de5d6..abc753633 100644 --- a/Tests/Opc.Ua.Client.Tests/ClientTestFramework.cs +++ b/Tests/Opc.Ua.Client.Tests/ClientTestFramework.cs @@ -63,6 +63,7 @@ public class ClientTestFramework public bool SingleSession { get; set; } = true; public int MaxChannelCount { get; set; } = 100; public bool SupportsExternalServerUrl { get; set; } + public bool UseSamplingGroupsInReferenceNodeManager { get; set; } public ServerFixture ServerFixture { get; set; } public ClientFixture ClientFixture { get; set; } public ReferenceServer ReferenceServer { get; set; } @@ -217,7 +218,8 @@ public virtual async Task CreateReferenceServerFixtureAsync( SecurityNone = securityNone, AutoAccept = true, AllNodeManagers = true, - OperationLimits = true + OperationLimits = true, + UseSamplingGroupsInReferenceNodeManager = UseSamplingGroupsInReferenceNodeManager }; await ServerFixture.LoadConfigurationAsync(PkiRoot).ConfigureAwait(false); diff --git a/Tests/Opc.Ua.Client.Tests/LoadTest.cs b/Tests/Opc.Ua.Client.Tests/LoadTest.cs index 622a136c3..c5ab37e2d 100644 --- a/Tests/Opc.Ua.Client.Tests/LoadTest.cs +++ b/Tests/Opc.Ua.Client.Tests/LoadTest.cs @@ -62,6 +62,7 @@ public LoadTest(string uriScheme) public override Task OneTimeSetUpAsync() { SupportsExternalServerUrl = true; + UseSamplingGroupsInReferenceNodeManager = false; return base.OneTimeSetUpAsync(); }