/* ======================================================================== * 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/ * ======================================================================*/ using System; using System.Collections.Generic; using System.Globalization; using System.Threading; using System.Threading.Tasks; using Opc.Ua.Security.Certificates; namespace Opc.Ua.Server { /// /// A class that stores the globally accessible state of a server instance. /// /// /// /// This is a readonly class that is initialized when the server starts up. It provides /// access to global objects and data that different parts of the server may require. /// It also defines some global methods. /// /// /// This object is constructed is three steps: /// - the configuration is provided. /// - the node managers et. al. are provided. /// - the session/subscription managers are provided. /// /// The server is not running until all three steps are complete. /// /// The references returned from this object do not change after all three states are complete. /// This ensures the object is thread safe even though it does not use a lock. /// Objects returned from this object can be assumed to be threadsafe unless otherwise stated. /// /// public class ServerInternalData : IServerInternal { /// /// Initializes the datastore with the server configuration. /// /// The server description. /// The configuration. /// The message context. /// The certificate validator. /// The certificate type provider. public ServerInternalData( ServerProperties serverDescription, ApplicationConfiguration configuration, IServiceMessageContext messageContext, CertificateValidator certificateValidator, CertificateTypesProvider instanceCertificateProvider) { m_serverDescription = serverDescription; m_configuration = configuration; MessageContext = messageContext; m_endpointAddresses = []; foreach (string baseAddresses in m_configuration.ServerConfiguration.BaseAddresses) { Uri url = Utils.ParseUri(baseAddresses); if (url != null) { m_endpointAddresses.Add(url); } } NamespaceUris = MessageContext.NamespaceUris; Factory = MessageContext.Factory; ServerUris = new StringTable(); TypeTree = new TypeTable(NamespaceUris); // add the server uri to the server table. ServerUris.Append(m_configuration.ApplicationUri); // create the default system context. DefaultSystemContext = new ServerSystemContext(this); } /// /// Frees any unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// An overrideable version of the Dispose. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) { Utils.SilentDispose(ResourceManager); Utils.SilentDispose(RequestManager); Utils.SilentDispose(AggregateManager); Utils.SilentDispose(ModellingRulesManager); Utils.SilentDispose(NodeManager); Utils.SilentDispose(SessionManager); Utils.SilentDispose(SubscriptionManager); Utils.SilentDispose(MonitoredItemQueueFactory); } } /// /// The session manager to use with the server. /// /// The session manager. public ISessionManager SessionManager { get; private set; } /// /// The subscription manager to use with the server. /// /// The subscription manager. public ISubscriptionManager SubscriptionManager { get; private set; } /// /// Stores the MasterNodeManager, the DiagnosticsNodeManager and the CoreNodeManager /// /// The node manager. public void SetNodeManager(MasterNodeManager nodeManager) { NodeManager = nodeManager; DiagnosticsNodeManager = nodeManager.DiagnosticsNodeManager; ConfigurationNodeManager = nodeManager.ConfigurationNodeManager; CoreNodeManager = nodeManager.CoreNodeManager; } /// /// Stores the MainNodeManagerFactory /// /// The main node manager factory. public void SetMainNodeManagerFactory(IMainNodeManagerFactory mainNodeManagerFactory) { MainNodeManagerFactory = mainNodeManagerFactory; } /// /// Sets the EventManager, the ResourceManager, the RequestManager and the AggregateManager. /// /// The event manager. /// The resource manager. /// The request manager. public void CreateServerObject( EventManager eventManager, ResourceManager resourceManager, RequestManager requestManager) { EventManager = eventManager; ResourceManager = resourceManager; RequestManager = requestManager; // create the server object. CreateServerObject(); } /// /// Stores the SessionManager, the SubscriptionManager in the datastore. /// /// The session manager. /// The subscription manager. public void SetSessionManager( ISessionManager sessionManager, ISubscriptionManager subscriptionManager) { SessionManager = sessionManager; SubscriptionManager = subscriptionManager; } /// /// Stores the MonitoredItemQueueFactory in the datastore. /// /// The MonitoredItemQueueFactory. public void SetMonitoredItemQueueFactory( IMonitoredItemQueueFactory monitoredItemQueueFactory) { MonitoredItemQueueFactory = monitoredItemQueueFactory; } /// /// Stores the Subscriptionstore in the datastore. /// /// The subscriptionstore. public void SetSubscriptionStore(ISubscriptionStore subscriptionStore) { SubscriptionStore = subscriptionStore; } /// /// Stores the AggregateManager in the datastore. /// /// The AggregateManager. public void SetAggregateManager(AggregateManager aggregateManager) { AggregateManager = aggregateManager; } /// /// Stores the ModellingRulesManager in the datastore. /// /// The ModellingRulesManager. public void SetModellingRulesManager(ModellingRulesManager modellingRulesManager) { ModellingRulesManager = modellingRulesManager; } /// /// The endpoint addresses used by the server. /// /// The endpoint addresses. public IEnumerable EndpointAddresses => m_endpointAddresses; /// /// The context to use when serializing/deserializing extension objects. /// /// The message context. public IServiceMessageContext MessageContext { get; } /// /// The default system context for the server. /// /// The default system context. public ServerSystemContext DefaultSystemContext { get; } /// /// The table of namespace uris known to the server. /// /// The namespace URIs. public NamespaceTable NamespaceUris { get; } /// /// The table of remote server uris known to the server. /// /// The server URIs. public StringTable ServerUris { get; } /// /// The factory used to create encodeable objects that the server understands. /// /// The factory. public IEncodeableFactory Factory { get; } /// /// The datatypes, object types and variable types known to the server. /// /// The type tree. /// /// The type tree table is a global object that all components of a server have access to. /// Node managers must populate this table with all types that they define. /// This object is thread safe. /// public TypeTable TypeTree { get; } /// /// The master node manager for the server. /// /// The node manager. public MasterNodeManager NodeManager { get; private set; } /// public IMainNodeManagerFactory MainNodeManagerFactory { get; private set; } /// /// The internal node manager for the servers. /// /// The core node manager. public CoreNodeManager CoreNodeManager { get; private set; } /// /// Returns the node manager that managers the server diagnostics. /// /// The diagnostics node manager. public DiagnosticsNodeManager DiagnosticsNodeManager { get; private set; } /// public ConfigurationNodeManager ConfigurationNodeManager { get; private set; } /// /// The manager for events that all components use to queue events that occur. /// /// The event manager. public EventManager EventManager { get; private set; } /// /// A manager for localized resources that components can use to localize text. /// /// The resource manager. public ResourceManager ResourceManager { get; private set; } /// /// A manager for outstanding requests that allows components to receive notifications if the timeout or are cancelled. /// /// The request manager. public RequestManager RequestManager { get; private set; } /// /// A manager for aggregate calculators supported by the server. /// /// The aggregate manager. public AggregateManager AggregateManager { get; private set; } /// /// A manager for modelling rules supported by the server. /// /// The modelling rules manager. public ModellingRulesManager ModellingRulesManager { get; private set; } /// /// The manager for active sessions. /// /// The session manager. ISessionManager IServerInternal.SessionManager => SessionManager; /// /// The manager for active subscriptions. /// ISubscriptionManager IServerInternal.SubscriptionManager => SubscriptionManager; /// /// The factory for durable monitored item queues /// public IMonitoredItemQueueFactory MonitoredItemQueueFactory { get; private set; } /// /// The store to persist and retrieve subscriptions /// public ISubscriptionStore SubscriptionStore { get; private set; } /// public ITelemetryContext Telemetry => MessageContext.Telemetry; /// /// Returns the status object for the server. /// /// The status. [Obsolete("No longer thread safe. To read the value use CurrentState, to write use UpdateServerStatus.")] public ServerStatusValue Status => NonThreadSafeStatus; /// /// Gets or sets the current state of the server. /// /// The state of the current. public ServerState CurrentState { get { lock (NonThreadSafeStatus.Lock) { return NonThreadSafeStatus.Value.State; } } set { lock (NonThreadSafeStatus.Lock) { NonThreadSafeStatus.Value.State = value; } } } /// /// Returns the Server object node /// /// The Server object node. public ServerObjectState ServerObject { get; private set; } /// /// Used to synchronize access to the server diagnostics. /// /// The diagnostics lock. public object DiagnosticsLock { get; } = new object(); /// /// Used to synchronize write access to /// the server diagnostics. /// /// The diagnostics lock. public object DiagnosticsWriteLock { get { // implicitly force diagnostics update DiagnosticsNodeManager?.ForceDiagnosticsScan(); return DiagnosticsLock; } } /// /// Returns the diagnostics structure for the server. /// /// The server diagnostics. public ServerDiagnosticsSummaryDataType ServerDiagnostics { get; private set; } /// /// Whether the server is currently running. /// /// /// true if this instance is running; otherwise, false. /// /// /// This flag is set to false when the server shuts down. Threads running should check this flag whenever /// they return from a blocking operation. If it is false the thread should clean up and terminate. /// public bool IsRunning { get { if (NonThreadSafeStatus == null) { return false; } lock (NonThreadSafeStatus.Lock) { if (NonThreadSafeStatus.Value.State == ServerState.Running) { return true; } if (NonThreadSafeStatus.Value.State == ServerState.Shutdown && NonThreadSafeStatus.Value.SecondsTillShutdown > 0) { return true; } return false; } } } /// /// Whether the server is collecting diagnostics. /// /// true if diagnostics are enabled; otherwise, false. public bool DiagnosticsEnabled { get { if (DiagnosticsNodeManager == null) { return false; } return DiagnosticsNodeManager.DiagnosticsEnabled; } } /// /// Status but non thread safe - internal so not part of public api /// internal ServerStatusValue NonThreadSafeStatus { get; private set; } /// /// Closes the specified session. /// /// The context. /// The session identifier. /// if set to true subscriptions are to be deleted. public void CloseSession( OperationContext context, NodeId sessionId, bool deleteSubscriptions) { CloseSessionAsync(context, sessionId, deleteSubscriptions) .AsTask().GetAwaiter().GetResult(); } /// /// Closes the specified session. /// /// The context. /// The session identifier. /// if set to true subscriptions are to be deleted. /// The cancellationToken public async ValueTask CloseSessionAsync( OperationContext context, NodeId sessionId, bool deleteSubscriptions, CancellationToken cancellationToken = default) { await NodeManager.SessionClosingAsync(context, sessionId, deleteSubscriptions, cancellationToken) .ConfigureAwait(false); await SubscriptionManager.SessionClosingAsync(context, sessionId, deleteSubscriptions, cancellationToken) .ConfigureAwait(false); SessionManager.CloseSession(sessionId); } /// /// Deletes the specified subscription. /// /// The subscription identifier. /// The cancellation token public async ValueTask DeleteSubscriptionAsync(uint subscriptionId, CancellationToken cancellationToken = default) { await SubscriptionManager.DeleteSubscriptionAsync(null, subscriptionId, cancellationToken).ConfigureAwait(false); } /// /// Called by any component to report a global event. /// /// The event. public void ReportEvent(IFilterTarget e) { ReportEvent(DefaultSystemContext, e); } /// /// Called by any component to report a global event. /// /// The context. /// The event. public void ReportEvent(ISystemContext context, IFilterTarget e) { if ((!Auditing) && (e is AuditEventState)) { // do not report auditing events if server Auditing flag is false return; } ServerObject?.ReportEvent(context, e); } /// /// Refreshes the conditions for the specified subscription. /// /// The context. /// The subscription identifier. public void ConditionRefresh(OperationContext context, uint subscriptionId) { SubscriptionManager.ConditionRefresh(context, subscriptionId); } /// /// Refreshes the conditions for the specified subscription. /// /// The context. /// The subscription identifier. /// The monitored item identifier. public void ConditionRefresh2( OperationContext context, uint subscriptionId, uint monitoredItemId) { SubscriptionManager.ConditionRefresh2(context, subscriptionId, monitoredItemId); } /// /// Updates the server status safely. /// /// Action to perform on the server status object. /// is null. public void UpdateServerStatus(Action action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } lock (DiagnosticsLock) // TODO: Should this not take the status lock? { action.Invoke(NonThreadSafeStatus); } } /// public bool Auditing { get; private set; } /// public ISystemContext DefaultAuditContext => DefaultSystemContext.Copy(); /// public void ReportAuditEvent(ISystemContext context, AuditEventState e) { if (!Auditing) { // do not report auditing events if server Auditing flag is false return; } ReportEvent(context, e); } /// /// Creates the ServerObject and attaches it to the NodeManager. /// private void CreateServerObject() { lock (DiagnosticsLock) { // get the server object. ServerObjectState serverObject = ServerObject = DiagnosticsNodeManager.FindPredefinedNode(ObjectIds.Server); // update server capabilities. serverObject.ServiceLevel.Value = 255; serverObject.ServerCapabilities.LocaleIdArray.Value = ResourceManager .GetAvailableLocales(); serverObject.ServerCapabilities.ServerProfileArray.Value = [ .. m_configuration.ServerConfiguration.ServerProfileArray ]; serverObject.ServerCapabilities.MinSupportedSampleRate.Value = 0; serverObject.ServerCapabilities.MaxBrowseContinuationPoints.Value = (ushort) m_configuration.ServerConfiguration.MaxBrowseContinuationPoints; serverObject.ServerCapabilities.MaxQueryContinuationPoints.Value = (ushort) m_configuration.ServerConfiguration.MaxQueryContinuationPoints; serverObject.ServerCapabilities.MaxHistoryContinuationPoints.Value = (ushort) m_configuration.ServerConfiguration.MaxHistoryContinuationPoints; serverObject.ServerCapabilities.MaxArrayLength.Value = (uint) m_configuration.TransportQuotas.MaxArrayLength; serverObject.ServerCapabilities.MaxStringLength.Value = (uint) m_configuration.TransportQuotas.MaxStringLength; serverObject.ServerCapabilities.MaxByteStringLength.Value = (uint) m_configuration.TransportQuotas.MaxByteStringLength; // Any operational limits Property that is provided shall have a non zero value. OperationLimitsState operationLimits = serverObject.ServerCapabilities .OperationLimits; OperationLimits configOperationLimits = m_configuration.ServerConfiguration .OperationLimits; if (configOperationLimits != null) { operationLimits.MaxNodesPerRead = SetPropertyValue( operationLimits.MaxNodesPerRead, configOperationLimits.MaxNodesPerRead); operationLimits.MaxNodesPerHistoryReadData = SetPropertyValue( operationLimits.MaxNodesPerHistoryReadData, configOperationLimits.MaxNodesPerHistoryReadData); operationLimits.MaxNodesPerHistoryReadEvents = SetPropertyValue( operationLimits.MaxNodesPerHistoryReadEvents, configOperationLimits.MaxNodesPerHistoryReadEvents); operationLimits.MaxNodesPerWrite = SetPropertyValue( operationLimits.MaxNodesPerWrite, configOperationLimits.MaxNodesPerWrite); operationLimits.MaxNodesPerHistoryUpdateData = SetPropertyValue( operationLimits.MaxNodesPerHistoryUpdateData, configOperationLimits.MaxNodesPerHistoryUpdateData); operationLimits.MaxNodesPerHistoryUpdateEvents = SetPropertyValue( operationLimits.MaxNodesPerHistoryUpdateEvents, configOperationLimits.MaxNodesPerHistoryUpdateEvents); operationLimits.MaxNodesPerMethodCall = SetPropertyValue( operationLimits.MaxNodesPerMethodCall, configOperationLimits.MaxNodesPerMethodCall); operationLimits.MaxNodesPerBrowse = SetPropertyValue( operationLimits.MaxNodesPerBrowse, configOperationLimits.MaxNodesPerBrowse); operationLimits.MaxNodesPerRegisterNodes = SetPropertyValue( operationLimits.MaxNodesPerRegisterNodes, configOperationLimits.MaxNodesPerRegisterNodes); operationLimits.MaxNodesPerTranslateBrowsePathsToNodeIds = SetPropertyValue( operationLimits.MaxNodesPerTranslateBrowsePathsToNodeIds, configOperationLimits.MaxNodesPerTranslateBrowsePathsToNodeIds); operationLimits.MaxNodesPerNodeManagement = SetPropertyValue( operationLimits.MaxNodesPerNodeManagement, configOperationLimits.MaxNodesPerNodeManagement); operationLimits.MaxMonitoredItemsPerCall = SetPropertyValue( operationLimits.MaxMonitoredItemsPerCall, configOperationLimits.MaxMonitoredItemsPerCall); } else { operationLimits.MaxNodesPerRead = operationLimits.MaxNodesPerHistoryReadData = operationLimits.MaxNodesPerHistoryReadEvents = operationLimits.MaxNodesPerWrite = operationLimits.MaxNodesPerHistoryUpdateData = operationLimits.MaxNodesPerHistoryUpdateEvents = operationLimits.MaxNodesPerMethodCall = operationLimits.MaxNodesPerBrowse = operationLimits.MaxNodesPerRegisterNodes = operationLimits.MaxNodesPerTranslateBrowsePathsToNodeIds = operationLimits.MaxNodesPerNodeManagement = operationLimits.MaxMonitoredItemsPerCall = null; } // setup PublishSubscribe Status State value const PubSubState pubSubState = PubSubState.Disabled; BaseVariableState default_PubSubState = DiagnosticsNodeManager.FindPredefinedNode( VariableIds.PublishSubscribe_Status_State); default_PubSubState.Value = pubSubState; // setup value for SupportedTransportProfiles BaseVariableState default_SupportedTransportProfiles = DiagnosticsNodeManager.FindPredefinedNode( VariableIds.PublishSubscribe_SupportedTransportProfiles); default_SupportedTransportProfiles.Value = "uadp"; // setup callbacks for dynamic values. serverObject.NamespaceArray.OnSimpleReadValue = OnReadNamespaceArray; serverObject.NamespaceArray.MinimumSamplingInterval = 1000; serverObject.ServerArray.OnSimpleReadValue = OnReadServerArray; serverObject.ServerArray.MinimumSamplingInterval = 1000; // dynamic change of enabledFlag is disabled to pass CTT serverObject.ServerDiagnostics.EnabledFlag.AccessLevel = AccessLevels.CurrentRead; serverObject.ServerDiagnostics.EnabledFlag.UserAccessLevel = AccessLevels .CurrentRead; serverObject.ServerDiagnostics.EnabledFlag.OnSimpleReadValue = OnReadDiagnosticsEnabledFlag; serverObject.ServerDiagnostics.EnabledFlag.OnSimpleWriteValue = OnWriteDiagnosticsEnabledFlag; serverObject.ServerDiagnostics.EnabledFlag.MinimumSamplingInterval = 1000; // initialize status. var serverStatus = new ServerStatusDataType { StartTime = DateTime.UtcNow, CurrentTime = DateTime.UtcNow, State = ServerState.Shutdown }; var buildInfo = new BuildInfo { ProductName = m_serverDescription.ProductName, ProductUri = m_serverDescription.ProductUri, ManufacturerName = m_serverDescription.ManufacturerName, SoftwareVersion = m_serverDescription.SoftwareVersion, BuildNumber = m_serverDescription.BuildNumber, BuildDate = m_serverDescription.BuildDate }; BuildInfoVariableState buildInfoVariableState = DiagnosticsNodeManager.FindPredefinedNode( VariableIds.Server_ServerStatus_BuildInfo); var buildInfoVariable = new BuildInfoVariableValue( buildInfoVariableState, buildInfo, null); serverStatus.BuildInfo = buildInfoVariable.Value; serverObject.ServerStatus.MinimumSamplingInterval = 1000; serverObject.ServerStatus.CurrentTime.MinimumSamplingInterval = 1000; NonThreadSafeStatus = new ServerStatusValue( serverObject.ServerStatus, serverStatus, DiagnosticsLock) { Timestamp = DateTime.UtcNow, OnBeforeRead = OnReadServerStatus }; // initialize diagnostics. ServerDiagnostics = new ServerDiagnosticsSummaryDataType { ServerViewCount = 0, CurrentSessionCount = 0, CumulatedSessionCount = 0, SecurityRejectedSessionCount = 0, RejectedSessionCount = 0, SessionTimeoutCount = 0, SessionAbortCount = 0, PublishingIntervalCount = 0, CurrentSubscriptionCount = 0, CumulatedSubscriptionCount = 0, SecurityRejectedRequestsCount = 0, RejectedRequestsCount = 0 }; DiagnosticsNodeManager.CreateServerDiagnostics( DefaultSystemContext, ServerDiagnostics, OnUpdateDiagnostics); // set the diagnostics enabled state. DiagnosticsNodeManager.SetDiagnosticsEnabled( DefaultSystemContext, m_configuration.ServerConfiguration.DiagnosticsEnabled); var configurationNodeManager = DiagnosticsNodeManager as ConfigurationNodeManager; configurationNodeManager?.CreateServerConfiguration( DefaultSystemContext, m_configuration); // Initialize history capabilities and update Server EventNotifier accordingly DiagnosticsNodeManager.UpdateServerEventNotifier(); Auditing = m_configuration.ServerConfiguration.AuditingEnabled; PropertyState auditing = serverObject.Auditing; auditing.OnSimpleWriteValue += OnWriteAuditing; auditing.OnSimpleReadValue += OnReadAuditing; auditing.Value = Auditing; auditing.RolePermissions = [ new RolePermissionType { RoleId = ObjectIds.WellKnownRole_AuthenticatedUser, Permissions = (uint)(PermissionType.Browse | PermissionType.Read) }, new RolePermissionType { RoleId = ObjectIds.WellKnownRole_SecurityAdmin, Permissions = (uint)( PermissionType.Browse | PermissionType.Write | PermissionType.ReadRolePermissions | PermissionType.Read) } ]; auditing.AccessLevel = AccessLevels.CurrentRead; auditing.UserAccessLevel = AccessLevels.CurrentReadOrWrite; auditing.MinimumSamplingInterval = 1000; } } /// /// Updates the server status before a read. /// private void OnReadServerStatus( ISystemContext context, BaseVariableValue variable, NodeState component) { lock (DiagnosticsLock) { DateTime now = DateTime.UtcNow; NonThreadSafeStatus.Timestamp = now; NonThreadSafeStatus.Value.CurrentTime = now; // update other timestamps in NodeState objects which are used to derive the source timestamp if (variable is ServerStatusValue serverStatusValue && serverStatusValue.Variable is ServerStatusState serverStatusState) { serverStatusState.Timestamp = now; serverStatusState.CurrentTime.Timestamp = now; serverStatusState.State.Timestamp = now; } } } /// /// Returns a copy of the namespace array. /// private ServiceResult OnReadNamespaceArray( ISystemContext context, NodeState node, ref object value) { value = NamespaceUris.ToArray(); return ServiceResult.Good; } /// /// Returns a copy of the server array. /// private ServiceResult OnReadServerArray( ISystemContext context, NodeState node, ref object value) { value = ServerUris.ToArray(); return ServiceResult.Good; } /// /// Returns Diagnostics.EnabledFlag /// private ServiceResult OnReadDiagnosticsEnabledFlag( ISystemContext context, NodeState node, ref object value) { value = DiagnosticsNodeManager.DiagnosticsEnabled; return ServiceResult.Good; } /// /// Sets the Diagnostics.EnabledFlag /// private ServiceResult OnWriteDiagnosticsEnabledFlag( ISystemContext context, NodeState node, ref object value) { bool enabled = (bool)value; DiagnosticsNodeManager.SetDiagnosticsEnabled(DefaultSystemContext, enabled); return ServiceResult.Good; } /// /// Updates the Server.Auditing flag. /// private ServiceResult OnWriteAuditing( ISystemContext context, NodeState node, ref object value) { Auditing = Convert.ToBoolean(value, CultureInfo.InvariantCulture); return ServiceResult.Good; } /// /// Reads the Server.Auditing flag. /// private ServiceResult OnReadAuditing( ISystemContext context, NodeState node, ref object value) { value = Auditing; return ServiceResult.Good; } /// /// Returns a copy of the current diagnostics. /// private ServiceResult OnUpdateDiagnostics( ISystemContext context, NodeState node, ref object value) { lock (ServerDiagnostics) { value = Utils.Clone(ServerDiagnostics); } return ServiceResult.Good; } /// /// Set the property to null if the value is zero, /// to the value otherwise. /// private static PropertyState SetPropertyValue( PropertyState property, uint value) { if (value != 0) { property.Value = value; } else { property = null; } return property; } private readonly ServerProperties m_serverDescription; private readonly ApplicationConfiguration m_configuration; private readonly List m_endpointAddresses; } }