/// <summary> /// Adds tags to the subscription. /// </summary> /// <param name="identity">The identity of the caller.</param> /// <param name="tagNames">The tags to subscribe to.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will add the tag subscriptions. /// </returns> /// <exception cref="ObjectDisposedException">The object has been disposed.</exception> /// <exception cref="ArgumentNullException"><paramref name="identity"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentException"><paramref name="tagNames"/> does not contain any non-null-or-empty entries.</exception> public async Task AddTags(ClaimsPrincipal identity, IEnumerable <string> tagNames, CancellationToken cancellationToken) { if (_isDisposed) { throw new ObjectDisposedException(GetType().FullName); } if (identity == null) { throw new ArgumentNullException(nameof(identity)); } var distinctTagNames = tagNames?.Where(x => !String.IsNullOrWhiteSpace(x)) .Select(x => x.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); if (distinctTagNames?.Length == 0) { throw new ArgumentException(Resources.Error_AtLeastOneTagNameRequired, nameof(tagNames)); } var tags = await _historian.GetTags(identity, distinctTagNames, cancellationToken).ConfigureAwait(false); foreach (var item in tags.Values) { if (!_subscribedTags.TryAdd(item.Id, item)) { continue; } Action <TagValue> onValueReceived = value => { if (_isDisposed) { return; } ValuesReceived?.Invoke(new Dictionary <string, IEnumerable <TagValue> >(StringComparer.OrdinalIgnoreCase) { { item.Name, new [] { value } } }); }; _subscriptions[item.Id] = item.CreateSnapshotSubscription(onValueReceived); var snapshot = item.ReadSnapshotValue(identity); if (snapshot != null) { onValueReceived.Invoke(snapshot); } item.Deleted += UnsubscribeTag; } }
/// <summary> /// Starts the SignalR connection. /// </summary> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will start the connection. /// </returns> public async Task Start(CancellationToken cancellationToken) { if (IsConnected) { return; } await Stop(cancellationToken).ConfigureAwait(false); var connectionBuilder = new HubConnectionBuilder().WithJsonProtocol(); connectionBuilder.WithMessageHandler(_client.MessageHandler); if (_loggerFactory != null) { connectionBuilder.WithLoggerFactory(_loggerFactory); } // If the API client has an Authorization header configured, we'll grab the token and pass // it as a query string parameter to SignalR, since we cannot pass authorization headers // to the websocket. if (_client.Authorization != null) { connectionBuilder.WithUrl($"{_client.HttpClient.BaseAddress}/hubs/snapshot?token={Uri.EscapeDataString(_client.Authorization.Parameter)}"); } else { connectionBuilder.WithUrl($"{_client.HttpClient.BaseAddress}/hubs/snapshot"); } _hubConnection = connectionBuilder.WithMessagePackProtocol().Build(); _hubConnection.Connected += () => { IsConnected = true; if (_logger?.IsEnabled(LogLevel.Information) ?? false) { _logger.LogInformation("Connected to SignalR hub."); } return(Task.CompletedTask); }; _hubConnection.Connected += () => Connected != null?Connected.Invoke() : Task.CompletedTask; _hubConnection.Closed += ex => { IsConnected = false; if (ex != null) { _logger?.LogError("Disconnected from SignalR hub due to an exception.", ex); } else { if (_logger?.IsEnabled(LogLevel.Information) ?? false) { _logger.LogInformation("Disconnected from SignalR hub."); } } return(Task.CompletedTask); }; _hubConnection.Closed += ex => Closed != null?Closed.Invoke(ex) : Task.CompletedTask; _hubConnection.On <IDictionary <string, TagValueDto[]> >("ValuesReceived", x => { if (x?.Count == 0) { return; } // Invoke ValuesReceived in a background task so that we don't block the websocket // input stream while the handlers are executing. Task.Run(() => { if (_logger?.IsEnabled(LogLevel.Trace) ?? false) { _logger.LogTrace($"Received {x.Sum(tag => tag.Value?.Length ?? 0)} values for {x.Count} tags."); } ValuesReceived?.Invoke(x); }); }); if (_logger?.IsEnabled(LogLevel.Information) ?? false) { _logger.LogInformation("Connecting to SignalR hub."); } await Task.WhenAny(_hubConnection.StartAsync(), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); }