/// <summary> /// handle subscription change messages /// </summary> /// <param name="sender"></param> /// <param name="sequenceNumber"></param> /// <param name="notification"></param> private void CallMessageReceiverDelegates(object sender, uint sequenceNumber, SubscriptionNotificationModel notification) { try { var message = new DataSetMessageModel { // TODO: Filter changes on the monitored items contained in the template Notifications = notification.Notifications.ToList(), ServiceMessageContext = notification.ServiceMessageContext, SubscriptionId = notification.SubscriptionId, SequenceNumber = sequenceNumber, ApplicationUri = notification.ApplicationUri, EndpointUrl = notification.EndpointUrl, TimeStamp = DateTime.UtcNow, PublisherId = _outer._publisherId, Writer = _dataSetWriter, WriterGroup = _outer._writerGroup }; lock (_lock) { if (_outer.NumberOfInvokedMessages >= kNumberOfInvokedMessagesResetThreshold) { _outer._logger.Debug("Message counter has been reset to prevent overflow. " + "So far, {NumberOfInvokedMessages} messages has been invoked by message source.", _outer.NumberOfInvokedMessages); _outer.NumberOfInvokedMessages = 0; } _outer.NumberOfInvokedMessages += message.Notifications.Count(); _outer.OnMessage?.Invoke(sender, message); } } catch (Exception ex) { _outer._logger.Debug(ex, "Failed to produce message"); } }
/// <summary> /// Handle subscription change messages /// </summary> /// <param name="sender"></param> /// <param name="notification"></param> private async void OnSubscriptionChangedAsync(object sender, SubscriptionNotificationModel notification) { var sequenceNumber = (uint)Interlocked.Increment(ref _currentSequenceNumber); if (_keyFrameCount.HasValue && _keyFrameCount.Value != 0 && (sequenceNumber % _keyFrameCount.Value) == 0) { var snapshot = await Try.Async(() => Subscription.GetSnapshotAsync()).ConfigureAwait(false); if (snapshot != null) { notification = snapshot; } } CallMessageReceiverDelegates(sender, sequenceNumber, notification); }
/// <summary> /// handle subscription change messages /// </summary> /// <param name="sender"></param> /// <param name="sequenceNumber"></param> /// <param name="notification"></param> private void CallMessageReceiverDelegates(object sender, uint sequenceNumber, SubscriptionNotificationModel notification) { try { var message = new DataSetMessageModel { // TODO: Filter changes on the monitored items contained in the template Notifications = notification.Notifications.ToList(), ServiceMessageContext = notification.ServiceMessageContext, SubscriptionId = notification.SubscriptionId, SequenceNumber = sequenceNumber, ApplicationUri = notification.ApplicationUri, EndpointUrl = notification.EndpointUrl, TimeStamp = notification.Timestamp, PublisherId = _outer._publisherId, Writer = _dataSetWriter, WriterGroup = _outer._writerGroup }; lock (_lock) { if (_outer.DataChangesCount >= kNumberOfInvokedMessagesResetThreshold || _outer.ValueChangesCount >= kNumberOfInvokedMessagesResetThreshold) { // reset both _outer._logger.Information("Notifications counter has been reset to prevent overflow. " + "So far, {DataChangesCount} data changes and {ValueChangesCount}" + " value changes were invoked by message source.", _outer.DataChangesCount, _outer.ValueChangesCount); _outer.DataChangesCount = 0; _outer.ValueChangesCount = 0; _outer.FireOnCounterResetEvent(); } _outer.ValueChangesCount += (ulong)message.Notifications.Count(); _outer.DataChangesCount++; _outer.OnMessage?.Invoke(sender, message); } } catch (Exception ex) { _outer._logger.Debug(ex, "Failed to produce message"); } }
/// <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> /// Monitored item notification handler /// </summary> /// <param name="monitoredItem"></param> /// <param name="e"></param> private void OnMonitoredItemChanged(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { try { if (OnMonitoredItemChange == null) { return; } if (e?.NotificationValue == null || monitoredItem?.Subscription?.Session == null) { return; } if (!(e.NotificationValue is MonitoredItemNotification notification)) { return; } if (!(notification.Value is DataValue value)) { return; } var message = new SubscriptionNotificationModel { ServiceMessageContext = monitoredItem.Subscription.Session.MessageContext, ApplicationUri = monitoredItem.Subscription.Session.Endpoint.Server.ApplicationUri, EndpointUrl = monitoredItem.Subscription.Session.Endpoint.EndpointUrl, SubscriptionId = Id, Notifications = new List <MonitoredItemNotificationModel> { notification.ToMonitoredItemNotification(monitoredItem) } }; OnMonitoredItemChange(this, message); } catch (Exception ex) { _logger.Debug(ex, "Exception processing monitored item notification"); } }
/// <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"); } }