/// <summary> /// Initializes the object. /// </summary> public Subscription( IServerInternal server, Session session, uint subscriptionId, double publishingInterval, uint maxLifetimeCount, uint maxKeepAliveCount, uint maxNotificationsPerPublish, byte priority, bool publishingEnabled, uint maxMessageCount) { if (server == null) throw new ArgumentNullException("server"); if (session == null) throw new ArgumentNullException("session"); m_server = server; m_session = session; m_id = subscriptionId; m_publishingInterval = publishingInterval; m_maxLifetimeCount = maxLifetimeCount; m_maxKeepAliveCount = maxKeepAliveCount; m_maxNotificationsPerPublish = maxNotificationsPerPublish; m_publishingEnabled = publishingEnabled; m_priority = priority; m_publishTimerExpiry = HiResClock.UtcNow.Ticks/TimeSpan.TicksPerMillisecond + (long)publishingInterval; m_keepAliveCounter = maxKeepAliveCount; m_lifetimeCounter = 0; m_waitingForPublish = false; m_maxMessageCount = maxMessageCount; m_sentMessages = new List<NotificationMessage>(); m_monitoredItems = new Dictionary<uint,LinkedListNode<IMonitoredItem>>(); m_itemsToCheck = new LinkedList<IMonitoredItem>(); m_itemsToPublish = new LinkedList<IMonitoredItem>(); m_itemsToTrigger = new Dictionary<uint,List<ITriggeredMonitoredItem>>(); // m_itemsReadyToPublish = new Queue<IMonitoredItem>(); // m_itemsNotificationsAvailable = new LinkedList<IMonitoredItem>(); m_sequenceNumber = 1; // initialize diagnostics. m_diagnostics = new SubscriptionDiagnosticsDataType(); m_diagnostics.SessionId = m_session.Id; m_diagnostics.SubscriptionId = m_id; m_diagnostics.Priority = priority; m_diagnostics.PublishingInterval = publishingInterval; m_diagnostics.MaxKeepAliveCount = maxKeepAliveCount; m_diagnostics.MaxLifetimeCount = maxLifetimeCount; m_diagnostics.MaxNotificationsPerPublish = maxNotificationsPerPublish; m_diagnostics.PublishingEnabled = publishingEnabled; m_diagnostics.ModifyCount = 0; m_diagnostics.EnableCount = 0; m_diagnostics.DisableCount = 0; m_diagnostics.RepublishMessageRequestCount = 0; m_diagnostics.RepublishMessageCount = 0; m_diagnostics.TransferRequestCount = 0; m_diagnostics.TransferredToSameClientCount = 0; m_diagnostics.TransferredToAltClientCount = 0; m_diagnostics.PublishRequestCount = 0; m_diagnostics.DataChangeNotificationsCount = 0; m_diagnostics.EventNotificationsCount = 0; m_diagnostics.NotificationsCount = 0; m_diagnostics.LatePublishRequestCount = 0; m_diagnostics.CurrentKeepAliveCount = 0; m_diagnostics.CurrentLifetimeCount = 0; m_diagnostics.UnacknowledgedMessageCount = 0; m_diagnostics.DiscardedMessageCount = 0; m_diagnostics.MonitoredItemCount = 0; m_diagnostics.DisabledMonitoredItemCount = 0; m_diagnostics.MonitoringQueueOverflowCount = 0; m_diagnostics.NextSequenceNumber = (uint)m_sequenceNumber; ServerSystemContext systemContext = m_server.DefaultSystemContext.Copy(session); m_diagnosticsId = server.DiagnosticsNodeManager.CreateSubscriptionDiagnostics( systemContext, m_diagnostics, OnUpdateDiagnostics); // TraceState("CREATED"); }
/// <summary> /// Reports notifications for any monitored diagnostic nodes. /// </summary> private void DoScan(object alwaysUpdateArrays) { try { lock (Lock) { if (!m_diagnosticsEnabled) { return; } m_lastDiagnosticsScanTime = DateTime.UtcNow; // update server diagnostics. UpdateServerDiagnosticsSummary(); // update session diagnostics. bool sessionsChanged = alwaysUpdateArrays != null; SessionDiagnosticsDataType[] sessionArray = new SessionDiagnosticsDataType[m_sessions.Count]; for (int ii = 0; ii < m_sessions.Count; ii++) { SessionDiagnosticsData diagnostics = m_sessions[ii]; if (UpdateSessionDiagnostics(diagnostics, sessionArray, ii)) { sessionsChanged = true; } } // check of the session diagnostics array node needs to be updated. SessionDiagnosticsArrayState sessionsNode = (SessionDiagnosticsArrayState)FindPredefinedNode( VariableIds.Server_ServerDiagnostics_SessionsDiagnosticsSummary_SessionDiagnosticsArray, typeof(SessionDiagnosticsArrayState)); if (sessionsNode != null && (sessionsNode.Value == null || StatusCode.IsBad(sessionsNode.StatusCode) || sessionsChanged)) { sessionsNode.Value = sessionArray; sessionsNode.ClearChangeMasks(SystemContext, false); } bool sessionsSecurityChanged = alwaysUpdateArrays != null; SessionSecurityDiagnosticsDataType[] sessionSecurityArray = new SessionSecurityDiagnosticsDataType[m_sessions.Count]; for (int ii = 0; ii < m_sessions.Count; ii++) { SessionDiagnosticsData diagnostics = m_sessions[ii]; if (UpdateSessionSecurityDiagnostics(diagnostics, sessionSecurityArray, ii)) { sessionsChanged = true; } } // check of the array node needs to be updated. SessionSecurityDiagnosticsArrayState sessionsSecurityNode = (SessionSecurityDiagnosticsArrayState)FindPredefinedNode( VariableIds.Server_ServerDiagnostics_SessionsDiagnosticsSummary_SessionSecurityDiagnosticsArray, typeof(SessionSecurityDiagnosticsArrayState)); if (sessionsSecurityNode != null && (sessionsSecurityNode.Value == null || StatusCode.IsBad(sessionsSecurityNode.StatusCode) || sessionsSecurityChanged)) { sessionsSecurityNode.Value = sessionSecurityArray; sessionsSecurityNode.ClearChangeMasks(SystemContext, false); } bool subscriptionsChanged = alwaysUpdateArrays != null; SubscriptionDiagnosticsDataType[] subscriptionArray = new SubscriptionDiagnosticsDataType[m_subscriptions.Count]; for (int ii = 0; ii < m_subscriptions.Count; ii++) { SubscriptionDiagnosticsData diagnostics = m_subscriptions[ii]; if (UpdateSubscriptionDiagnostics(diagnostics, subscriptionArray, ii)) { sessionsChanged = true; } } // check of the subscription node needs to be updated. SubscriptionDiagnosticsArrayState subscriptionsNode = (SubscriptionDiagnosticsArrayState)FindPredefinedNode( VariableIds.Server_ServerDiagnostics_SubscriptionDiagnosticsArray, typeof(SubscriptionDiagnosticsArrayState)); if (subscriptionsNode != null && (subscriptionsNode.Value == null || StatusCode.IsBad(subscriptionsNode.StatusCode) || subscriptionsChanged)) { subscriptionsNode.Value = subscriptionArray; subscriptionsNode.ClearChangeMasks(SystemContext, false); } for (int ii = 0; ii < m_sessions.Count; ii++) { SessionDiagnosticsData diagnostics = m_sessions[ii]; List<SubscriptionDiagnosticsDataType> subscriptionDiagnosticsArray = new List<SubscriptionDiagnosticsDataType>(); NodeId sessionId = diagnostics.Summary.NodeId; for (int jj = 0; jj < m_subscriptions.Count; jj++) { SubscriptionDiagnosticsData subscriptionDiagnostics = m_subscriptions[jj]; if (subscriptionDiagnostics.Value.Value == null) { continue; } if (subscriptionDiagnostics.Value.Value.SessionId != sessionId) { continue; } subscriptionDiagnosticsArray.Add(subscriptionDiagnostics.Value.Value); } // update session subscription array. subscriptionsNode = (SubscriptionDiagnosticsArrayState)diagnostics.Summary.CreateChild( SystemContext, BrowseNames.SubscriptionDiagnosticsArray); if (subscriptionsNode != null && (subscriptionsNode.Value == null || StatusCode.IsBad(subscriptionsNode.StatusCode) || subscriptionsChanged)) { subscriptionsNode.Value = subscriptionDiagnosticsArray.ToArray(); subscriptionsNode.ClearChangeMasks(SystemContext, false); } } } } catch (Exception e) { Utils.Trace(e, "Unexpected error during diagnostics scan."); } }
/// <summary> /// Creates the diagnostics node for a subscription. /// </summary> public NodeId CreateSubscriptionDiagnostics( ServerSystemContext systemContext, SubscriptionDiagnosticsDataType diagnostics, NodeValueSimpleEventHandler updateCallback) { NodeId nodeId = null; lock (Lock) { // check if diagnostics have been enabled. if (!m_diagnosticsEnabled) { return null; } SubscriptionDiagnosticsState diagnosticsNode = new SubscriptionDiagnosticsState(null); // create a new instance and assign ids. nodeId = CreateNode( systemContext, null, ReferenceTypeIds.HasComponent, new QualifiedName(diagnostics.SubscriptionId.ToString()), diagnosticsNode); // add reference to subscription array. diagnosticsNode.AddReference( ReferenceTypeIds.HasComponent, true, VariableIds.Server_ServerDiagnostics_SubscriptionDiagnosticsArray); // wrap diagnostics in a thread safe object. SubscriptionDiagnosticsValue diagnosticsValue = new SubscriptionDiagnosticsValue(diagnosticsNode, diagnostics, Lock); diagnosticsValue.CopyPolicy = Opc.Ua.VariableCopyPolicy.Never; diagnosticsValue.OnBeforeRead = OnBeforeReadDiagnostics; // must ensure the first update gets sent. diagnosticsValue.Value = null; diagnosticsValue.Error = StatusCodes.BadWaitingForInitialData; m_subscriptions.Add(new SubscriptionDiagnosticsData(diagnosticsValue, updateCallback)); // add reference from subscription array. SubscriptionDiagnosticsArrayState array = (SubscriptionDiagnosticsArrayState)FindPredefinedNode( VariableIds.Server_ServerDiagnostics_SubscriptionDiagnosticsArray, typeof(SubscriptionDiagnosticsArrayState)); if (array != null) { array.AddReference(ReferenceTypeIds.HasComponent, false, diagnosticsNode.NodeId); } // add reference to session subscription array. diagnosticsNode.AddReference( ReferenceTypeIds.HasComponent, true, diagnostics.SessionId); // add reference from session subscription array. SessionDiagnosticsObjectState sessionNode = (SessionDiagnosticsObjectState)FindPredefinedNode( diagnostics.SessionId, typeof(SessionDiagnosticsObjectState)); if (sessionNode != null) { // add reference from subscription array. array = (SubscriptionDiagnosticsArrayState)sessionNode.CreateChild( systemContext, BrowseNames.SubscriptionDiagnosticsArray); if (array != null) { array.AddReference(ReferenceTypeIds.HasComponent, false, diagnosticsNode.NodeId); } } // send initial update. DoScan(true); } return nodeId; }
/// <summary> /// Updates the subscription diagnostics summary structure. /// </summary> private bool UpdateSubscriptionDiagnostics( SubscriptionDiagnosticsData diagnostics, SubscriptionDiagnosticsDataType[] subscriptionArray, int index) { // get the latest snapshot. object value = null; ServiceResult result = diagnostics.UpdateCallback( SystemContext, diagnostics.Value.Variable, ref value); SubscriptionDiagnosticsDataType newValue = value as SubscriptionDiagnosticsDataType; subscriptionArray[index] = newValue; // check for changes. if (Utils.IsEqual(newValue, diagnostics.Value.Value)) { return false; } diagnostics.Value.Error = null; // check for bad value. if (ServiceResult.IsNotBad(result) && newValue == null) { result = StatusCodes.BadOutOfService; } // check for bad result. if (ServiceResult.IsBad(result)) { diagnostics.Value.Error = result; newValue = null; } // update the value. diagnostics.Value.Value = newValue; diagnostics.Value.Timestamp = DateTime.UtcNow; // notify any monitored items. diagnostics.Value.ChangesComplete(SystemContext); return true; }