/// <summary>
        /// Creates the subscriptions on the server.
        /// </summary>
        /// <param name="token">A cancellation token. </param>
        /// <returns>A task.</returns>
        private async Task AutoCreateSubscriptionsAsync(CancellationToken token = default(CancellationToken))
        {
            var tcs = new TaskCompletionSource <bool>();
            NotifyCollectionChangedEventHandler handler = async(o, e) =>
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    foreach (var subscription in e.NewItems.OfType <Subscription>())
                    {
                        var target = subscription.Target;
                        if (target == null)
                        {
                            continue;
                        }

                        try
                        {
                            // create the subscription.
                            var subscriptionRequest = new CreateSubscriptionRequest
                            {
                                RequestedPublishingInterval = subscription.PublishingInterval,
                                RequestedMaxKeepAliveCount  = subscription.KeepAliveCount,
                                RequestedLifetimeCount      = Math.Max(subscription.LifetimeCount, 3 * subscription.KeepAliveCount),
                                PublishingEnabled           = subscription.PublishingEnabled
                            };
                            var subscriptionResponse = await this.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false);

                            var id = subscription.SubscriptionId = subscriptionResponse.SubscriptionId;

                            // add the items.
                            if (subscription.MonitoredItems.Count > 0)
                            {
                                var items    = subscription.MonitoredItems.ToList();
                                var requests = items.Select(m => new MonitoredItemCreateRequest {
                                    ItemToMonitor = new ReadValueId {
                                        NodeId = m.NodeId, AttributeId = m.AttributeId, IndexRange = m.IndexRange
                                    }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters {
                                        ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter
                                    }
                                }).ToArray();
                                var itemsRequest = new CreateMonitoredItemsRequest
                                {
                                    SubscriptionId = id,
                                    ItemsToCreate  = requests,
                                };
                                var itemsResponse = await this.CreateMonitoredItemsAsync(itemsRequest).ConfigureAwait(false);

                                for (int i = 0; i < itemsResponse.Results.Length; i++)
                                {
                                    var item   = items[i];
                                    var result = itemsResponse.Results[i];
                                    item.OnCreateResult(target, result);
                                    if (StatusCode.IsBad(result.StatusCode))
                                    {
                                        this.Logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}");
                                    }
                                }
                            }
                        }
                        catch (ServiceResultException ex)
                        {
                            this.Logger?.LogError($"Error creating subscription. {ex.Message}");
                            this.innerChannel.Fault(ex);
                        }
                    }
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    try
                    {
                        // delete the subscriptions.
                        var request = new DeleteSubscriptionsRequest
                        {
                            SubscriptionIds = e.OldItems.OfType <Subscription>().Select(s => s.SubscriptionId).ToArray()
                        };
                        await this.DeleteSubscriptionsAsync(request).ConfigureAwait(false);
                    }
                    catch (ServiceResultException ex)
                    {
                        this.Logger?.LogError($"Error deleting subscriptions. {ex.Message}");
                    }
                }
            };

            using (token.Register(state => ((TaskCompletionSource <bool>)state).TrySetResult(true), tcs, false))
            {
                try
                {
                    handler.Invoke(this.subscriptions, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, this.subscriptions.ToList()));
                    this.subscriptions.CollectionChanged += handler;
                    await tcs.Task;
                }
                finally
                {
                    this.subscriptions.CollectionChanged -= handler;
                    foreach (var subscription in this.subscriptions)
                    {
                        subscription.SubscriptionId = 0;
                    }
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// The state machine manages the state of the subscription.
        /// </summary>
        /// <param name="token">A cancellation token.</param>
        /// <returns>A task.</returns>
        private async Task StateMachineAsync(CancellationToken token = default(CancellationToken))
        {
            while (!token.IsCancellationRequested)
            {
                await this.whenSubscribed.Task;

                this.progress.Report(CommunicationState.Opening);

                try
                {
                    // get a channel.
                    this.innerChannel = await this.application.GetChannelAsync(this.endpointUrl, token);

                    try
                    {
                        // create the subscription.
                        var subscriptionRequest = new CreateSubscriptionRequest
                        {
                            RequestedPublishingInterval = this.publishingInterval,
                            RequestedMaxKeepAliveCount  = this.keepAliveCount,
                            RequestedLifetimeCount      = Math.Max(this.lifetimeCount, 3 * this.keepAliveCount),
                            PublishingEnabled           = true
                        };
                        var subscriptionResponse = await this.innerChannel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(true);

                        // link up the dataflow blocks
                        var id        = this.subscriptionId = subscriptionResponse.SubscriptionId;
                        var linkToken = this.innerChannel.LinkTo(this.actionBlock, pr => pr.SubscriptionId == id);

                        try
                        {
                            // create the monitored items.
                            var items = this.monitoredItems.ToList();
                            if (items.Count > 0)
                            {
                                var requests = items.Select(m => new MonitoredItemCreateRequest {
                                    ItemToMonitor = new ReadValueId {
                                        NodeId = m.NodeId, AttributeId = m.AttributeId, IndexRange = m.IndexRange
                                    }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters {
                                        ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter
                                    }
                                }).ToArray();
                                var itemsRequest = new CreateMonitoredItemsRequest
                                {
                                    SubscriptionId = id,
                                    ItemsToCreate  = requests,
                                };
                                var itemsResponse = await this.innerChannel.CreateMonitoredItemsAsync(itemsRequest);

                                for (int i = 0; i < itemsResponse.Results.Length; i++)
                                {
                                    var item   = items[i];
                                    var result = itemsResponse.Results[i];
                                    item.OnCreateResult(result);
                                    if (StatusCode.IsBad(result.StatusCode))
                                    {
                                        this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}");
                                    }
                                }
                            }

                            this.progress.Report(CommunicationState.Opened);

                            // wait here until channel is closing, unsubscribed or token cancelled.
                            try
                            {
                                await Task.WhenAny(
                                    this.WhenChannelClosingAsync(this.innerChannel, token),
                                    this.whenUnsubscribed.Task);
                            }
                            catch
                            {
                            }
                            finally
                            {
                                this.progress.Report(CommunicationState.Closing);
                            }
                        }
                        catch (Exception ex)
                        {
                            this.logger?.LogError($"Error creating MonitoredItems. {ex.Message}");
                            this.progress.Report(CommunicationState.Faulted);
                        }
                        finally
                        {
                            linkToken.Dispose();
                        }

                        if (this.innerChannel.State == CommunicationState.Opened)
                        {
                            try
                            {
                                // delete the subscription.
                                var deleteRequest = new DeleteSubscriptionsRequest
                                {
                                    SubscriptionIds = new uint[] { id }
                                };
                                await this.innerChannel.DeleteSubscriptionsAsync(deleteRequest);
                            }
                            catch (Exception ex)
                            {
                                this.logger?.LogError($"Error deleting subscription. {ex.Message}");
                                await Task.Delay(2000);
                            }
                        }

                        this.progress.Report(CommunicationState.Closed);
                    }
                    catch (Exception ex)
                    {
                        this.logger?.LogError($"Error creating subscription. {ex.Message}");
                        this.progress.Report(CommunicationState.Faulted);
                        await Task.Delay(2000);
                    }
                }
                catch (Exception ex)
                {
                    this.logger?.LogTrace($"Error getting channel. {ex.Message}");
                    this.progress.Report(CommunicationState.Faulted);
                    await Task.Delay(2000);
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// The state machine manages the state of the subscription.
        /// </summary>
        /// <param name="token">A cancellation token.</param>
        /// <returns>A task.</returns>
        private async Task StateMachineAsync(CancellationToken token = default)
        {
            while (!token.IsCancellationRequested)
            {
                await this.whenSubscribed.Task;

                this.progress.Report(CommunicationState.Opening);

                try
                {
                    if (this.endpointUrl is null)
                    {
                        throw new InvalidOperationException("The endpointUrl field must not be null. Please, use the Subscription attribute properly.");
                    }

                    // get a channel.
                    this.innerChannel = await this.application.GetChannelAsync(this.endpointUrl, token);

                    try
                    {
                        // create the subscription.
                        var subscriptionRequest = new CreateSubscriptionRequest
                        {
                            RequestedPublishingInterval = this.publishingInterval,
                            RequestedMaxKeepAliveCount  = this.keepAliveCount,
                            RequestedLifetimeCount      = Math.Max(this.lifetimeCount, 3 * this.keepAliveCount),
                            PublishingEnabled           = true
                        };
                        var subscriptionResponse = await this.innerChannel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false);

                        // link up the dataflow blocks
                        var id        = this.subscriptionId = subscriptionResponse.SubscriptionId;
                        var linkToken = this.innerChannel.LinkTo(this.actionBlock, pr => pr.SubscriptionId == id);

                        try
                        {
                            // create the monitored items.
                            var items = this.monitoredItems.ToList();
                            if (items.Count > 0)
                            {
                                var requests = items.Select(m => new MonitoredItemCreateRequest {
                                    ItemToMonitor = new ReadValueId {
                                        NodeId = ExpandedNodeId.ToNodeId(m.NodeId, this.InnerChannel.NamespaceUris), AttributeId = m.AttributeId, IndexRange = m.IndexRange
                                    }, MonitoringMode = m.MonitoringMode, RequestedParameters = new MonitoringParameters {
                                        ClientHandle = m.ClientId, DiscardOldest = m.DiscardOldest, QueueSize = m.QueueSize, SamplingInterval = m.SamplingInterval, Filter = m.Filter
                                    }
                                }).ToArray();

                                //split requests array to MaxMonitoredItemsPerCall chunks
                                int maxmonitoreditemspercall = 100;
                                MonitoredItemCreateRequest[] requests_chunk;
                                int chunk_size;
                                for (int i_chunk = 0; i_chunk < requests.Length; i_chunk += maxmonitoreditemspercall)
                                {
                                    chunk_size     = Math.Min(maxmonitoreditemspercall, requests.Length - i_chunk);
                                    requests_chunk = new MonitoredItemCreateRequest[chunk_size];
                                    Array.Copy(requests, i_chunk, requests_chunk, 0, chunk_size);

                                    var itemsRequest = new CreateMonitoredItemsRequest
                                    {
                                        SubscriptionId = id,
                                        ItemsToCreate  = requests_chunk,
                                    };
                                    var itemsResponse = await this.innerChannel.CreateMonitoredItemsAsync(itemsRequest);

                                    if (itemsResponse.Results is { } results)
                                    {
                                        for (int i = 0; i < results.Length; i++)
                                        {
                                            var item   = items[i];
                                            var result = results[i];

                                            if (result is null)
                                            {
                                                this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. The result is null.");
                                                continue;
                                            }

                                            item.OnCreateResult(result);
                                            if (StatusCode.IsBad(result.StatusCode))
                                            {
                                                this.logger?.LogError($"Error creating MonitoredItem for {item.NodeId}. {StatusCodes.GetDefaultMessage(result.StatusCode)}");
                                            }
                                        }
                                    }
                                }
                            }

                            this.progress.Report(CommunicationState.Opened);

                            // wait here until channel is closing, unsubscribed or token cancelled.
                            try
                            {
                                await Task.WhenAny(
                                    this.WhenChannelClosingAsync(this.innerChannel, token),
                                    this.whenUnsubscribed.Task);
                            }
                            catch
                            {
                            }
                            finally
                            {
                                this.progress.Report(CommunicationState.Closing);
                            }
                        }
                        catch (Exception ex)
                        {
                            this.logger?.LogError($"Error creating MonitoredItems. {ex.Message}");
                            this.progress.Report(CommunicationState.Faulted);
                        }
                        finally
                        {
                            linkToken.Dispose();
                        }

                        if (this.innerChannel.State == CommunicationState.Opened)
                        {
                            try
                            {
                                // delete the subscription.
                                var deleteRequest = new DeleteSubscriptionsRequest
                                {
                                    SubscriptionIds = new uint[] { id }
                                };
                                await this.innerChannel.DeleteSubscriptionsAsync(deleteRequest);
                            }
                            catch (Exception ex)
                            {
                                this.logger?.LogError($"Error deleting subscription. {ex.Message}");
                                await Task.Delay(2000);
                            }
                        }

                        this.progress.Report(CommunicationState.Closed);
                    }
                    catch (Exception ex)
                    {
                        this.logger?.LogError($"Error creating subscription. {ex.Message}");
                        this.progress.Report(CommunicationState.Faulted);
                        await Task.Delay(2000);
                    }
                }
                catch (Exception ex)
                {
                    this.logger?.LogTrace($"Error getting channel. {ex.Message}");
                    this.progress.Report(CommunicationState.Faulted);
                    await Task.Delay(2000);
                }
            }
        }