using CryptoExchange.Net.Objects; using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.Trackers.UserData.ItemTrackers; using CryptoExchange.Net.Trackers.UserData.Objects; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace CryptoExchange.Net.Trackers.UserData { /// /// User data tracker /// public abstract class UserDataTracker { /// /// Logger /// protected readonly ILogger _logger; /// /// Listen key to use for subscriptions /// protected string? _listenKey; /// /// Cts /// protected CancellationTokenSource? _cts; /// /// List of data trackers /// protected abstract UserDataItemTracker[] DataTrackers { get; } /// /// Symbol tracker /// protected internal UserDataSymbolTracker SymbolTracker { get; } /// public string? UserIdentifier { get; } /// /// Connected status changed /// public event Action? OnConnectedChange; /// /// Exchange name /// public string Exchange { get; } /// /// Whether all trackers are full connected /// public bool Connected => DataTrackers.All(x => x.Connected); /// /// Currently tracked symbols /// public IEnumerable TrackedSymbols => SymbolTracker.GetTrackedSymbols(); /// /// ctor /// public UserDataTracker( ILogger logger, string exchange, UserDataTrackerConfig config, string? userIdentifier) { if (config.OnlyTrackProvidedSymbols && !config.TrackedSymbols.Any()) throw new ArgumentException(nameof(config.TrackedSymbols), "Conflicting options; `OnlyTrackProvidedSymbols` but no symbols specific in `TrackedSymbols`"); _logger = logger; SymbolTracker = new UserDataSymbolTracker(logger, config); Exchange = exchange; UserIdentifier = userIdentifier; } /// /// Start the data tracker /// public async Task StartAsync() { _cts = new CancellationTokenSource(); foreach(var tracker in DataTrackers) tracker.OnConnectedChange += (x) => OnConnectedChange?.Invoke(tracker.DataType, x); var result = await DoStartAsync().ConfigureAwait(false); if (!result) return result; var tasks = new List>(); foreach (var dataTracker in DataTrackers) tasks.Add(dataTracker.StartAsync(_listenKey)); await Task.WhenAll(tasks).ConfigureAwait(false); if (!tasks.All(x => x.Result.Success)) { await Task.WhenAll(DataTrackers.Select(x => x.StopAsync())).ConfigureAwait(false); return tasks.First(x => !x.Result.Success).Result; } return CallResult.SuccessResult; } /// /// Implementation specific start logic /// protected abstract Task DoStartAsync(); /// /// Stop the data tracker /// public async Task StopAsync() { _logger.LogDebug("Stopping UserDataTracker"); _cts?.Cancel(); var tasks = new List(); foreach (var dataTracker in DataTrackers) tasks.Add(dataTracker.StopAsync()); await DoStopAsync().ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false); _logger.LogDebug("Stopped UserDataTracker"); } /// /// Stop implementation /// /// protected virtual Task DoStopAsync() => Task.CompletedTask; } }