/// <summary> /// Convert to monitored item notifications /// </summary> /// <param name="notification"></param> /// <param name="monitoredItems"></param> /// <returns></returns> public static IEnumerable <MonitoredItemNotificationModel> ToMonitoredItemNotifications( this DataChangeNotification notification, IEnumerable <MonitoredItem> monitoredItems) { for (var i = 0; i < notification.MonitoredItems.Count; i++) { var monitoredItem = monitoredItems.SingleOrDefault( m => m.ClientHandle == notification.MonitoredItems[i].ClientHandle); if (monitoredItem == null) { continue; } var message = notification.MonitoredItems[i].ToMonitoredItemNotification( monitoredItem); if (message == null) { continue; } if (message.DiagnosticInfo == null && notification.DiagnosticInfos != null && i < notification.DiagnosticInfos.Count) { message.DiagnosticInfo = notification.DiagnosticInfos[i]; } yield return(message); } }
private void OnDataChangeCallback(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { _context.Send(o => { var state = (Tuple <UaSubscription, DataChangeNotification>)o; foreach (var itemNotification in state.Item2.MonitoredItems) { var item = state.Item1.FindItemByClientHandle(itemNotification.ClientHandle) as UaItem; if (item != null) { var cacheQueue = item.CacheQueue; if (cacheQueue != null) { cacheQueue.Enqueue(itemNotification.Value); while (cacheQueue.Count > item.CacheQueueSize) { cacheQueue.Dequeue(); } continue; } item.CacheValue = itemNotification.Value; state.Item1.NotifyPropertyChanged(item.DisplayName); } } }, Tuple.Create(this, notification)); }
/// <summary> /// callback to report session's notifications /// </summary> /// <param name="session"></param> /// <param name="e"></param> private void Session_Notification(Session session, NotificationEventArgs e) { try { _logger.Debug("Notification for session '{id}', subscription '{displayName}' - sequence# {sequence}-{publishTime}", session?.Handle is SessionWrapper wrapper ? wrapper?.Id : session?.SessionName, e.Subscription?.DisplayName, e?.NotificationMessage?.SequenceNumber, e.NotificationMessage?.PublishTime); if (e.NotificationMessage.IsEmpty || e.NotificationMessage.NotificationData.Count() == 0) { var keepAlive = new DataChangeNotification() { MonitoredItems = new MonitoredItemNotificationCollection() { new MonitoredItemNotification() { ClientHandle = 0, Value = null, Message = e.NotificationMessage } } }; e.Subscription.FastDataChangeCallback.Invoke(e.Subscription, keepAlive, e.StringTable); } } catch (Exception ex) { _logger.Error(ex, "Failed to process notifications for session '{name}'", session.SessionName); } }
/// <summary> /// Returns the data changes contained in the notification message. /// </summary> public IList <MonitoredItemNotification> GetDataChanges(bool reverse) { List <MonitoredItemNotification> datachanges = new List <MonitoredItemNotification>(); for (int jj = 0; jj < m_notificationData.Count; jj++) { ExtensionObject extension = m_notificationData[jj]; if (ExtensionObject.IsNull(extension)) { continue; } DataChangeNotification notification = extension.Body as DataChangeNotification; if (notification == null) { continue; } if (reverse) { for (int ii = notification.MonitoredItems.Count - 1; ii >= 0; ii--) { MonitoredItemNotification datachange = notification.MonitoredItems[ii]; if (datachange != null) { datachange.Message = this; datachanges.Add(datachange); } } } else { for (int ii = 0; ii < notification.MonitoredItems.Count; ii++) { MonitoredItemNotification datachange = notification.MonitoredItems[ii]; if (datachange != null) { datachange.Message = this; datachanges.Add(datachange); } } } } return(datachanges); }
/// <see cref="BaseListCtrl.UpdateItem" /> protected override void UpdateItem(ListViewItem listItem, object item) { ItemData itemData = item as ItemData; if (itemData == null) { base.UpdateItem(listItem, item); return; } listItem.SubItems[0].Text = String.Format("{0}", itemData.Subscription.DisplayName); listItem.SubItems[1].Text = String.Format("{0}", itemData.NotificationMessage.SequenceNumber); listItem.SubItems[2].Text = String.Format("{0:HH:mm:ss.fff}", itemData.NotificationMessage.PublishTime.ToLocalTime()); int events = 0; int datachanges = 0; int notifications = 0; foreach (ExtensionObject notification in itemData.NotificationMessage.NotificationData) { notifications++; if (ExtensionObject.IsNull(notification)) { continue; } DataChangeNotification datachangeNotification = notification.Body as DataChangeNotification; if (datachangeNotification != null) { datachanges += datachangeNotification.MonitoredItems.Count; } EventNotificationList EventNotification = notification.Body as EventNotificationList; if (EventNotification != null) { events += EventNotification.Events.Count; } } listItem.SubItems[3].Text = String.Format("{0}", notifications); listItem.SubItems[4].Text = String.Format("{0}", datachanges); listItem.SubItems[5].Text = String.Format("{0}", events); listItem.Tag = item; }
private void CheckNotifications(DataChangeNotification dcn) { foreach (var min in dcn.MonitoredItems) { if (this.monitoredItems.TryGetValueByClientId(min.ClientHandle, out MonitoredItemBase item)) { try { item.Publish(min.Value); } catch (Exception ex) { logger?.LogError($"Error publishing value for NodeId {item.NodeId}. {ex.Message}"); } } } }
private void OnDataChange(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { if (!Object.ReferenceEquals(subscription, m_subscription)) { return; } if (this.InvokeRequired) { this.BeginInvoke(new FastDataChangeNotificationEventHandler(OnDataChange), subscription, notification, stringTable); return; } try { foreach (MonitoredItemNotification itemNotification in notification.MonitoredItems) { MonitoredItem monitoredItem = subscription.FindItemByClientHandle(itemNotification.ClientHandle); if (monitoredItem == null) { continue; } DataRow row = (DataRow)monitoredItem.Handle; if (row.RowState == DataRowState.Detached) { continue; } UpdateRow(row, itemNotification); if (m_EditComplexValueDlg != null && Object.ReferenceEquals(m_EditComplexValueDlg.Tag, monitoredItem)) { m_EditComplexValueDlg.UpdateValue(monitoredItem.ResolvedNodeId, monitoredItem.AttributeId, null, itemNotification.Value.Value); } } } catch (Exception exception) { ClientUtils.HandleException(this.Text, exception); } }
/// <summary> /// Sends publish requests in the background for the currently active subscription. /// </summary> public void Publish(object state) { try { uint subscriptionId; ListOfUInt32 availableSequenceNumbers; bool moreNotifications; NotificationMessage notificationMessage; ListOfStatusCode results; ListOfDiagnosticInfo diagnosticInfos; m_client.Publish( m_client.CreateRequestHeader(), new ListOfSubscriptionAcknowledgement(), out subscriptionId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out results, out diagnosticInfos); LastMessageTimeTB.Text = String.Format("{0:HH:mm:ss}", notificationMessage.PublishTime.ToLocalTime()); foreach (ExtensionObject extension in notificationMessage.NotificationData) { if (extension.TypeId == new ExpandedNodeId(Objects.DataChangeNotification_Encoding_DefaultXml)) { DataChangeNotification body = (DataChangeNotification)extension.ParseBody(typeof(DataChangeNotification)); foreach (MonitoredItemNotification notification in body.MonitoredItems) { UpdateValue(notification); } } } } catch (Exception exception) { MessageBox.Show(exception.Message); } ThreadPool.QueueUserWorkItem(Publish); }
void Session_Notification(Session session, NotificationEventArgs e) { lock (m_lock) { if (m_messageCount == 0) { m_firstMessageTime = DateTime.UtcNow; m_totalItemUpdateCount = 0; m_itemUpdateCounts = new int[m_itemCount]; } m_messageCount++; m_lastMessageTime = DateTime.UtcNow; int count = 0; for (int ii = 0; ii < e.NotificationMessage.NotificationData.Count; ii++) { DataChangeNotification notification = e.NotificationMessage.NotificationData[ii].Body as DataChangeNotification; if (notification == null) { continue; } for (int jj = 0; jj < notification.MonitoredItems.Count; jj++) { count++; int clientHandle = (int)notification.MonitoredItems[jj].ClientHandle; m_totalItemUpdateCount++; if (clientHandle >= 0 && clientHandle < m_itemUpdateCounts.Length) { m_itemUpdateCounts[clientHandle]++; } } } // ReportMessage("OnDataChange. Time={0} ({3}), Count={1}/{2}", DateTime.UtcNow.ToString("mm:ss.fff"), count, m_totalItemUpdateCount, (m_lastMessageTime - m_firstMessageTime).TotalMilliseconds); } }
public void Setup() { _notification = new DataChangeNotification(); for (int i = 0; i < _numberOfElements; i++) { _notification.MonitoredItems.Add(new MonitoredItemNotification() { Message = new NotificationMessage(), Value = new DataValue(new Variant(i)), ClientHandle = (uint)i }); } _monitoredItems = new List <MonitoredItem>(_numberOfElements); for (uint i = 0; i < _numberOfElements; i++) { _monitoredItems.Add(new MonitoredItem(i)); } }
public static IEnumerable <MonitoredItemNotificationModel> ToMonitoredItemNotifications( DataChangeNotification notification, IEnumerable <MonitoredItem> monitoredItems) { for (var i = 0; i < notification.MonitoredItems.Count; i++) { var monitoredItem = monitoredItems.SingleOrDefault( m => m.ClientHandle == notification?.MonitoredItems[i]?.ClientHandle); if (monitoredItem == null) { continue; } var message = notification?.MonitoredItems[i]? .ToMonitoredItemNotification(monitoredItem); if (message == null) { continue; } yield return(message); } }
/// <summary> /// Handles a datachange notifications. /// </summary> private void MonitoredItem_Notification(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { if (!Object.ReferenceEquals(subscription, m_subscription)) { return; } lock (m_lock) { for (int ii = 0; ii < notification.MonitoredItems.Count; ii++) { MonitoredItem monitoredItem = m_subscription.FindItemByClientHandle(notification.MonitoredItems[ii].ClientHandle); if (monitoredItem != null) { Opc.Ua.Server.MonitoredItem localItem = (Opc.Ua.Server.MonitoredItem)monitoredItem.Handle; localItem.QueueValue(notification.MonitoredItems[ii].Value, null); } } } }
/// <see cref="BaseListCtrl.UpdateItem" /> protected override void UpdateItem(ListViewItem listItem, object item) { ItemData itemData = item as ItemData; if (itemData == null) { base.UpdateItem(listItem, item); return; } int events = 0; int datachanges = 0; int notifications = 0; foreach (ExtensionObject notification in itemData.NotificationMessage.NotificationData) { notifications++; if (ExtensionObject.IsNull(notification)) { continue; } DataChangeNotification datachangeNotification = notification.Body as DataChangeNotification; if (datachangeNotification != null) { datachanges += datachangeNotification.MonitoredItems.Count; } EventNotificationList EventNotification = notification.Body as EventNotificationList; if (EventNotification != null) { events += EventNotification.Events.Count; } } listItem.Tag = item; }
/// <summary> /// callback to report session's notifications /// </summary> /// <param name="session"></param> /// <param name="e"></param> private void Session_Notification(Session session, NotificationEventArgs e) { _logger.Debug("Notification for session: {Session}, subscription {Subscription} -sequence# {Sequence}-{PublishTime}", session.SessionName, e.Subscription?.DisplayName, e.NotificationMessage?.SequenceNumber, e.NotificationMessage.PublishTime); if (e.NotificationMessage.IsEmpty || e.NotificationMessage.NotificationData.Count() == 0) { var keepAlive = new DataChangeNotification() { MonitoredItems = new MonitoredItemNotificationCollection() { new MonitoredItemNotification() { ClientHandle = 0, Value = null, Message = e.NotificationMessage } } }; e.Subscription.FastDataChangeCallback.Invoke(e.Subscription, keepAlive, e.StringTable); } }
/// <summary> /// Subscription data changed /// </summary> /// <param name="subscription"></param> /// <param name="notification"></param> /// <param name="stringTable"></param> private void OnSubscriptionDataChanged(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { try { if (OnSubscriptionChange == null) { return; } var message = new SubscriptionNotificationModel { ServiceMessageContext = subscription.Session.MessageContext, ApplicationUri = subscription.Session.Endpoint.Server.ApplicationUri, EndpointUrl = subscription.Session.Endpoint.EndpointUrl, SubscriptionId = Id, Notifications = notification .ToMonitoredItemNotifications(subscription.MonitoredItems) .ToList() }; OnSubscriptionChange?.Invoke(this, message); } catch (Exception ex) { _logger.Debug(ex, "Exception processing subscription notification"); } }
/// <summary> /// Convert to monitored item notifications /// </summary> /// <param name="notification"></param> /// <param name="monitoredItems"></param> /// <returns></returns> public static IEnumerable <MonitoredItemNotificationModel> ToMonitoredItemNotifications( this DataChangeNotification notification, IEnumerable <MonitoredItem> monitoredItems) { var handles = new Dictionary <uint, MonitoredItem>(1024); foreach (var monitoredItem in monitoredItems) { handles.Add(monitoredItem.ClientHandle, monitoredItem); } foreach (var monitoredItemWithNotification in notification.MonitoredItems) { if (handles.TryGetValue(monitoredItemWithNotification.ClientHandle, out var monitoredItem)) { var message = monitoredItemWithNotification.ToMonitoredItemNotification(monitoredItem); if (message == null) { continue; } yield return(message); } } }
private void OnFastDataChange(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { OpcUaDeviceOutParamEntity opcUaDeviceOutParamEntity = new OpcUaDeviceOutParamEntity(); opcUaDeviceOutParamEntity.StatusCode = (uint)DeviceStatusCode.SubscriptionOK; opcUaDeviceOutParamEntity.SubScriptionValueList = new List <Tuple <string, object> >(); foreach (MonitoredItemNotification itemNotification in notification.MonitoredItems) { MonitoredItem item = subscription.FindItemByClientHandle(itemNotification.ClientHandle); if (item == null) { continue; } foreach (var value in item.DequeueValues()) { opcUaDeviceOutParamEntity.SubScriptionValueList.Add(Tuple.Create(item.ResolvedNodeId.ToString(), value.Value)); } } DeviceEventArgs <DeviceParamEntityBase> args = new DeviceEventArgs <DeviceParamEntityBase>(opcUaDeviceOutParamEntity); Notification.Invoke(this, args); }
/// <summary> /// Construct a message from the queues. /// </summary> private NotificationMessage ConstructMessage( Queue<EventFieldList> events, Queue<MonitoredItemNotification> datachanges, Queue<DiagnosticInfo> datachangeDiagnostics, out int notificationCount) { notificationCount = 0; NotificationMessage message = new NotificationMessage(); message.SequenceNumber = (uint)m_sequenceNumber; message.PublishTime = DateTime.UtcNow; Utils.IncrementIdentifier(ref m_sequenceNumber); lock (m_diagnostics) { m_diagnostics.NextSequenceNumber = (uint)m_sequenceNumber; } // add events. if (events.Count > 0 && notificationCount < m_maxNotificationsPerPublish) { EventNotificationList notification = new EventNotificationList(); while (events.Count > 0 && notificationCount < m_maxNotificationsPerPublish) { notification.Events.Add(events.Dequeue()); notificationCount++; } message.NotificationData.Add(new ExtensionObject(notification)); } // add datachanges (space permitting). if (datachanges.Count > 0 && notificationCount < m_maxNotificationsPerPublish) { bool diagnosticsExist = false; DataChangeNotification notification = new DataChangeNotification(); notification.MonitoredItems = new MonitoredItemNotificationCollection(datachanges.Count); notification.DiagnosticInfos = new DiagnosticInfoCollection(datachanges.Count); while (datachanges.Count > 0 && notificationCount < m_maxNotificationsPerPublish) { MonitoredItemNotification datachange = datachanges.Dequeue(); notification.MonitoredItems.Add(datachange); DiagnosticInfo diagnosticInfo = datachangeDiagnostics.Dequeue(); if (diagnosticInfo != null) { diagnosticsExist = true; } notification.DiagnosticInfos.Add(diagnosticInfo); notificationCount++; } // clear diagnostics if not used. if (!diagnosticsExist) { notification.DiagnosticInfos.Clear(); } message.NotificationData.Add(new ExtensionObject(notification)); } return message; }
/// <summary> /// Called when a data change arrives. /// </summary> /// <param name="subscription">The subscription.</param> /// <param name="notification">The notification.</param> /// <param name="stringTable">The string table.</param> void OnDataChange(Subscription subscription, DataChangeNotification notification, IList<string> stringTable) { try { lock (m_lock) { for (int jj = 0; jj < notification.MonitoredItems.Count; jj++) { uint clientHandle = notification.MonitoredItems[jj].ClientHandle; // find item. ComDaGroupItem item = null; if (!m_itemsByMonitoredItem.TryGetValue(clientHandle, out item)) { continue; } // convert data change. DaValue result = m_manager.Mapper.GetLocalDataValue(notification.MonitoredItems[jj].Value); // update cache. UpdateCache(item, result, false); } } } catch (Exception exception) { Utils.Trace("Unexpected error during CacheUpdate. {0}", exception.Message); } }
/// <summary> /// Handles a datachange notifications. /// </summary> private void MonitoredItem_Notification(Subscription subscription, DataChangeNotification notification, IList<string> stringTable) { if (!Object.ReferenceEquals(subscription, m_subscription)) { return; } lock (m_lock) { for (int ii = 0; ii < notification.MonitoredItems.Count; ii++) { MonitoredItem monitoredItem = m_subscription.FindItemByClientHandle(notification.MonitoredItems[ii].ClientHandle); if (monitoredItem != null) { Opc.Ua.Server.MonitoredItem localItem = (Opc.Ua.Server.MonitoredItem)monitoredItem.Handle; localItem.QueueValue(notification.MonitoredItems[ii].Value, null); } } } }
/// <summary> /// Raised when a publish response arrives from the server. /// </summary> static void Session_Notification(Session session, NotificationEventArgs e) { NotificationMessage message = e.NotificationMessage; // check for keep alive. if (message.NotificationData.Count == 0) { Console.WriteLine( "===>>> Subscription KeepAlive: SubscriptionId={0} MessageId={1} Time={2:HH:mm:ss.fff}", e.Subscription.Id, message.SequenceNumber, message.PublishTime.ToLocalTime()); return; } DataChangeNotification dcn = (DataChangeNotification)ExtensionObject.ToEncodeable(message.NotificationData[0]); // Console.WriteLine("{0:mm:ss.fff} - SeqNo={1}, Items={2}", message.PublishTime, message.SequenceNumber, dcn.MonitoredItems.Count); int count = 0; // get the data changes (oldest to newest). foreach (MonitoredItemNotification datachange in message.GetDataChanges(false)) { // lookup the monitored item. MonitoredItem monitoredItem = e.Subscription.FindItemByClientHandle(datachange.ClientHandle); if (monitoredItem == null) { Console.WriteLine("MonitoredItem ClientHandle not known: {0}", datachange.ClientHandle); continue; } // this is called on another thread so we need to synchronize before accessing the node. lock (m_lock) { NodeOfInterest node = monitoredItem.Handle as NodeOfInterest; //Console.WriteLine( // "Update for {0}: {1} Status={2} Timestamp={3:HH:mm:ss.fff}", // node.DisplayName, // datachange.Value.WrappedValue, // datachange.Value.StatusCode, // datachange.Value.SourceTimestamp.ToLocalTime()); node.Value = datachange.Value; Save(datachange.ClientHandle, node.Value); count++; } } if (count > NotificationsPerPublish) { Console.WriteLine("Too many notifications in Publish: {0}/{1}", count, NotificationsPerPublish); } lock (m_publishes) { m_notifications++; if (m_lastDump.AddSeconds(1) > DateTime.UtcNow) { return; } int sampleCount = 0; int itemCount = 0; DateTime timestamp = DateTime.MinValue; while (m_publishes.Count > 0) { DataValue value1 = m_publishes.Dequeue(); if (timestamp < value1.SourceTimestamp) { if (timestamp != DateTime.MinValue) { //Console.WriteLine( // "Items = {1}, Timestamp = {0:mm:ss.fff}", // timestamp, // itemCount); } timestamp = value1.SourceTimestamp; itemCount = 0; } sampleCount++; itemCount++; } //Console.WriteLine( // "Items = {1}, Timestamp = {0:mm:ss.fff}", // timestamp, // itemCount); uint expectedSamples = 10000; // (uint)((1000.0/SamplingInterval)*ItemsToMonitor); uint expectedNotifications = 50; Console.WriteLine( "{0:mm:ss.fff}-{1:mm:ss.fff}, Messages = {2}/{3}, Samples = {4}/{5}, MissedSamples = {6}", m_lastDump, m_lastDump.AddSeconds(1), m_notifications, expectedNotifications, sampleCount, expectedSamples, (int)m_expectedSamples - (int)m_actualSamples); m_lastDump = m_lastDump.AddSeconds(1); m_lastMessage = message.SequenceNumber; m_dumpCount++; m_notifications = 0; m_actualSamples += (uint)sampleCount; m_expectedSamples += expectedSamples; if (m_dumpCount == 10) { m_actualSamples = 0; m_expectedSamples = 0; } } }
/// <summary> /// Checks if the subscription is ready to publish and returns a notification message. /// </summary> public NotificationMessage Publish() { lock (m_lock) { long currentTime = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; // check of it is time for a publish. if (m_lastPublishTime + m_publishingInterval < currentTime) { ListOfMonitoredItemNotification notifications = new ListOfMonitoredItemNotification(); ListOfDiagnosticInfo diagnosticInfos = new ListOfDiagnosticInfo(); // check each monitored item for data changes to send. foreach (MonitoredItem monitoredItem in m_monitoredItems.Values) { while (monitoredItem.Values.Count > 0) { MonitoredItemNotification notification = new MonitoredItemNotification(); notification.ClientHandle = monitoredItem.Parameters.ClientHandle; notification.Value = monitoredItem.Values.Dequeue(); notifications.Add(notification); diagnosticInfos.Add(monitoredItem.DiagnosticInfos.Dequeue()); } } // check if any notifications were found. if (notifications.Count > 0) { // subscriptions can produce different types of notifications so the notification parameter // is an extensible parameter. This means the object must be manually serialized and wrapped in // an ExtensionObject which specifies the type of data contained in the Body. The complete // UA SDK takes care this housekeeping and will serialize extensible parameters automatically. DataChangeNotification body = new DataChangeNotification(); body.MonitoredItems = notifications; body.DiagnosticInfos = diagnosticInfos; ExtensionObject extension = new ExtensionObject( new ExpandedNodeId(Objects.DataChangeNotification_Encoding_DefaultXml), body); // construct the message and assign a new sequence number. NotificationMessage message = new NotificationMessage(); message.SequenceNumber = ++m_nextSequenceNumber; message.PublishTime = DateTime.UtcNow; message.NotificationData = new ListOfExtensionObject(); message.NotificationData.Add(extension); m_lastPublishTime = currentTime; m_nextKeepAliveTime = (long)(currentTime + m_publishingInterval * m_keepAliveCount); return(message); } } // check if it is time for a keep alive. if (m_nextKeepAliveTime < currentTime) { NotificationMessage message = new NotificationMessage(); message.SequenceNumber = m_nextSequenceNumber; message.PublishTime = DateTime.UtcNow; message.NotificationData = new ListOfExtensionObject(); m_nextKeepAliveTime = (long)(currentTime + m_publishingInterval * m_keepAliveCount); return(message); } return(null); } }
/// <summary> /// Subscription data changed /// </summary> /// <param name="subscription"></param> /// <param name="notification"></param> /// <param name="stringTable"></param> private void OnSubscriptionDataChanged(Subscription subscription, DataChangeNotification notification, IList <string> stringTable) { try { if (OnSubscriptionChange == null) { return; } if (notification == null) { _logger.Warning("DataChange for subscription: {Subscription} having empty notification", subscription.DisplayName); return; } if (_currentlyMonitored == null) { _logger.Information("DataChange for subscription: {Subscription} having no monitored items yet", subscription.DisplayName); return; } // check if notification is a keep alive var isKeepAlive = notification?.MonitoredItems?.Count == 1 && notification?.MonitoredItems?.First().ClientHandle == 0 && notification?.MonitoredItems?.First().Message?.NotificationData?.Count == 0; var sequenceNumber = notification?.MonitoredItems?.First().Message?.SequenceNumber; var publishTime = (notification?.MonitoredItems?.First().Message?.PublishTime). GetValueOrDefault(DateTime.MinValue); _logger.Debug("DataChange for subscription: {Subscription}, sequence#: " + "{Sequence} isKeepAlive{KeepAlive}, publishTime: {PublishTime}", subscription.DisplayName, sequenceNumber, isKeepAlive, publishTime); var message = new SubscriptionNotificationModel { ServiceMessageContext = subscription.Session.MessageContext, ApplicationUri = subscription.Session.Endpoint.Server.ApplicationUri, EndpointUrl = subscription.Session.Endpoint.EndpointUrl, SubscriptionId = Id, Notifications = (!isKeepAlive) ? notification.ToMonitoredItemNotifications( subscription.MonitoredItems).ToList() : new List <MonitoredItemNotificationModel>() }; message.IsKeyMessage = true; // add the heartbeat for monitored items that did not receive a a datachange notification // Try access lock if we cannot continue... List <MonitoredItemWrapper> currentlyMonitored = null; if (_lock.Wait(0)) { try { currentlyMonitored = _currentlyMonitored; } finally { _lock.Release(); } } if (currentlyMonitored != null) { // add the heartbeat for monitored items that did not receive a // a datachange notification foreach (var item in currentlyMonitored) { if (isKeepAlive || !notification.MonitoredItems.Exists(m => m.ClientHandle == item.Item.ClientHandle)) { if (item.TriggerHeartbeat(publishTime)) { var heartbeatValue = item.Item.LastValue.ToMonitoredItemNotification(item.Item); if (heartbeatValue != null) { heartbeatValue.SequenceNumber = sequenceNumber; heartbeatValue.IsHeartbeat = true; heartbeatValue.PublishTime = publishTime; message.Notifications.Add(heartbeatValue); } } else { // just reset the heartbeat for the items already processed item.TriggerHeartbeat(publishTime); } } } } OnSubscriptionChange?.Invoke(this, message); } catch (Exception ex) { _logger.Debug(ex, "Exception processing subscription notification"); } }
private void OnDataChange(Subscription subscription, DataChangeNotification notification, IList<string> stringTable) { if (!Object.ReferenceEquals(subscription, m_subscription)) { return; } if (this.InvokeRequired) { this.BeginInvoke(new FastDataChangeNotificationEventHandler(OnDataChange), subscription, notification, stringTable); return; } try { foreach (MonitoredItemNotification itemNotification in notification.MonitoredItems) { MonitoredItem monitoredItem = subscription.FindItemByClientHandle(itemNotification.ClientHandle); if (monitoredItem == null) { continue; } DataRow row = (DataRow)monitoredItem.Handle; if (row.RowState == DataRowState.Detached) { continue; } UpdateRow(row, itemNotification); if (m_EditComplexValueDlg != null && Object.ReferenceEquals(m_EditComplexValueDlg.Tag, monitoredItem)) { m_EditComplexValueDlg.UpdateValue(monitoredItem.ResolvedNodeId, monitoredItem.AttributeId, null, itemNotification.Value.Value); } } } catch (Exception exception) { ClientUtils.HandleException(this.Text, exception); } }
/// <summary> /// Checks if the subscription is ready to publish and returns a notification message. /// </summary> public NotificationMessage Publish() { lock (m_lock) { long currentTime = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; // check of it is time for a publish. if (m_lastPublishTime + m_publishingInterval < currentTime) { ListOfMonitoredItemNotification notifications = new ListOfMonitoredItemNotification(); ListOfDiagnosticInfo diagnosticInfos = new ListOfDiagnosticInfo(); // check each monitored item for data changes to send. foreach (MonitoredItem monitoredItem in m_monitoredItems.Values) { while (monitoredItem.Values.Count > 0) { MonitoredItemNotification notification = new MonitoredItemNotification(); notification.ClientHandle = monitoredItem.Parameters.ClientHandle; notification.Value = monitoredItem.Values.Dequeue(); notifications.Add(notification); diagnosticInfos.Add(monitoredItem.DiagnosticInfos.Dequeue()); } } // check if any notifications were found. if (notifications.Count > 0) { // subscriptions can produce different types of notifications so the notification parameter // is an extensible parameter. This means the object must be manually serialized and wrapped in // an ExtensionObject which specifies the type of data contained in the Body. The complete // UA SDK takes care this housekeeping and will serialize extensible parameters automatically. DataChangeNotification body = new DataChangeNotification(); body.MonitoredItems = notifications; body.DiagnosticInfos = diagnosticInfos; ExtensionObject extension = new ExtensionObject( new ExpandedNodeId(Objects.DataChangeNotification_Encoding_DefaultXml), body); // construct the message and assign a new sequence number. NotificationMessage message = new NotificationMessage(); message.SequenceNumber = ++m_nextSequenceNumber; message.PublishTime = DateTime.UtcNow; message.NotificationData = new ListOfExtensionObject(); message.NotificationData.Add(extension); m_lastPublishTime = currentTime; m_nextKeepAliveTime = (long)(currentTime + m_publishingInterval * m_keepAliveCount); return message; } } // check if it is time for a keep alive. if (m_nextKeepAliveTime < currentTime) { NotificationMessage message = new NotificationMessage(); message.SequenceNumber = m_nextSequenceNumber; message.PublishTime = DateTime.UtcNow; message.NotificationData = new ListOfExtensionObject(); m_nextKeepAliveTime = (long)(currentTime + m_publishingInterval * m_keepAliveCount); return message; } return null; } }
/// <summary> /// Verifies that the initial data change was received. /// </summary> private bool VerifyInitialDataChange( Subscription subscription, DataChangeNotification notification, DateTime receiveTime, IList<MonitoredItem> monitoredItems, ref int totalCount, Dictionary<uint,MonitoredItem> updatedItems) { bool success = true; bool errorReported = false; // determine the maximum requested diagnostics. uint diagnosticsMask = 0; DiagnosticInfoCollection diagnosticInfos = notification.DiagnosticInfos; for (int ii = 0; ii < notification.MonitoredItems.Count; ii++) { totalCount++; MonitoredItemNotification update = notification.MonitoredItems[ii]; // find matching item. MonitoredItem monitoredItem = null; for (int jj = 0; jj < monitoredItems.Count; jj++) { monitoredItem = monitoredItems[jj]; if (update.ClientHandle == monitoredItem.ClientHandle) { MonitoredItem existingItem = null; if (updatedItems.TryGetValue(update.ClientHandle, out existingItem)) { Log( "Unexpected notification returned from server for MonitoredItem. ClientHandle = {0}, Node = {1}, Attribute = {2}, NewValue = {3}, OldValue={4}", update.ClientHandle, monitoredItem.Node, Attributes.GetBrowseName(monitoredItem.AttributeId), update.Value.WrappedValue, existingItem.Value.Value); } updatedItems[update.ClientHandle] = monitoredItem; break; } monitoredItem = null; } if (monitoredItem == null) { Log( "Unexpected notification returned from server for Node. ClientHandle = {0}, Value = {1}", update.ClientHandle, update.Value.WrappedValue); success = false; break; } if (monitoredItem.Value != null) { Log( "Duplicate notification for MonitoredItem for Node {0}. NodeId = {1}, AttributeId = {2}", monitoredItem.Node, monitoredItem.Node.NodeId, Attributes.GetBrowseName(monitoredItem.AttributeId)); success = false; break; } double initialDelay = CalculateInterval(monitoredItem.UpdateTime, receiveTime); if (initialDelay > subscription.PublishingInterval + m_idealTimingError) { bool fatal = initialDelay > subscription.PublishingInterval*2; if (!errorReported) { Log( "{0}: Late notification for MonitoredItem for Node {1}. NodeId = {2}, AttributeId = {3}, Delay = {4}ms, MaxDelay = {5}ms", "TIMING ERROR", monitoredItem.Node, monitoredItem.Node.NodeId, Attributes.GetBrowseName(monitoredItem.AttributeId), initialDelay, subscription.PublishingInterval); errorReported = true; } if (fatal) { success = false; break; } } monitoredItem.Value = update.Value; // verify timestamps. if (!VerifyTimestamps(monitoredItem.Node, monitoredItem.AttributeId, monitoredItem.TimestampsToReturn, update.Value)) { success = false; break; } // check if diagnostics could exist. if (update.Value.StatusCode != StatusCodes.Good) { diagnosticsMask |= monitoredItem.DiagnosticsMasks; if ((monitoredItem.DiagnosticsMasks & (uint)DiagnosticsMasks.OperationAll) != 0) { if (diagnosticInfos == null || diagnosticInfos.Count < ii || diagnosticInfos[ii] == null) { Log( "Missing DiagnosticInfo for MonitoredItem for Node {0}. NodeId = {1}, AttributeId = {2}", monitoredItem.Node, monitoredItem.Node.NodeId, Attributes.GetBrowseName(monitoredItem.AttributeId)); success = false; break; } } } // verify error. if (StatusCode.IsBad(update.Value.StatusCode)) { if (!VerifyBadAttribute(monitoredItem.Node, monitoredItem.AttributeId, update.Value.StatusCode)) { success = false; break; } continue; } // verify success. if (!VerifyGoodAttribute(monitoredItem.Node, monitoredItem.AttributeId, update.Value)) { success = false; break; } } // check for unnecessary diagnostics. if ((diagnosticsMask & (uint)DiagnosticsMasks.OperationAll) == 0) { if (diagnosticInfos != null && diagnosticInfos.Count > 0) { Log("Returned non-empty DiagnosticInfos array during Publish."); return false; } } return success; }
/// <summary> /// The delegate used to receive data change notifications via a direct function call instead of a .NET Event. /// </summary> public void OnDataChangeNotification(Opc.Ua.Client.Subscription subscription, DataChangeNotification notification, IList<string> stringTable) { for (int ii = 0; ii < notification.MonitoredItems.Count; ii++) { MonitoredItem localItem = null; DataValue value = null; ServiceResult error = null; lock (subscription.Session) { Opc.Ua.Client.MonitoredItem monitoredItem = subscription.FindItemByClientHandle(notification.MonitoredItems[ii].ClientHandle); if (monitoredItem != null) { MonitoredItemNotification value2 = notification.MonitoredItems[ii]; if (value2.Value.StatusCode != StatusCodes.Good) { error = new ServiceResult(value2.Value.StatusCode, value2.DiagnosticInfo, stringTable); } value = value2.Value; value.WrappedValue = m_mapper.ToLocalVariant(value2.Value.WrappedValue); value.ServerTimestamp = DateTime.UtcNow; localItem = (MonitoredItem)monitoredItem.Handle; } } localItem.QueueValue(value, error); } }