diff --git a/CryptoExchange.Net/Clients/BaseSocketClient.cs b/CryptoExchange.Net/Clients/BaseSocketClient.cs index c913a33..01258b8 100644 --- a/CryptoExchange.Net/Clients/BaseSocketClient.cs +++ b/CryptoExchange.Net/Clients/BaseSocketClient.cs @@ -108,5 +108,19 @@ namespace CryptoExchange.Net.Clients } return result.ToString(); } + + /// + /// Returns the state of all socket api clients + /// + /// + public List GetSocketApiClientStates() + { + var result = new List(); + foreach (var client in ApiClients.OfType()) + { + result.Add(client.GetState()); + } + return result; + } } } diff --git a/CryptoExchange.Net/Clients/SocketApiClient.cs b/CryptoExchange.Net/Clients/SocketApiClient.cs index c0a68bc..3121e3d 100644 --- a/CryptoExchange.Net/Clients/SocketApiClient.cs +++ b/CryptoExchange.Net/Clients/SocketApiClient.cs @@ -622,32 +622,76 @@ namespace CryptoExchange.Net.Clients /// public string GetSubscriptionsState(bool includeSubDetails = true) { - var sb = new StringBuilder(); - sb.AppendLine($"{GetType().Name}"); - sb.AppendLine($" Connections: {socketConnections.Count}"); - sb.AppendLine($" Subscriptions: {CurrentSubscriptions}"); - sb.AppendLine($" Download speed: {IncomingKbps} kbps"); - foreach (var connection in socketConnections) + return GetState(includeSubDetails).ToString(); + } + + /// + /// Gets the state of the client + /// + /// True to get details for each subscription + /// + public SocketApiClientState GetState(bool includeSubDetails = true) + { + var connectionStates = new List(); + foreach (var socketIdAndConnection in socketConnections) { - sb.AppendLine($" Id: {connection.Key}"); - sb.AppendLine($" Address: {connection.Value.ConnectionUri}"); - sb.AppendLine($" Subscriptions: {connection.Value.UserSubscriptionCount}"); - sb.AppendLine($" Status: {connection.Value.Status}"); - sb.AppendLine($" Authenticated: {connection.Value.Authenticated}"); - sb.AppendLine($" Download speed: {connection.Value.IncomingKbps} kbps"); - sb.AppendLine($" Subscriptions:"); - if (includeSubDetails) - { - foreach (var subscription in connection.Value.Subscriptions) - { - sb.AppendLine($" Id: {subscription.Id}"); - sb.AppendLine($" Confirmed: {subscription.Confirmed}"); - sb.AppendLine($" Invocations: {subscription.TotalInvocations}"); - sb.AppendLine($" Identifiers: [{string.Join(", ", subscription.ListenerIdentifiers)}]"); - } - } + SocketConnection connection = socketIdAndConnection.Value; + SocketConnection.SocketConnectionState connectionState = connection.GetState(includeSubDetails); + connectionStates.Add(connectionState); + } + + return new SocketApiClientState(socketConnections.Count, CurrentSubscriptions, IncomingKbps, connectionStates); + } + + /// + /// Get the current state of the client + /// + /// Number of sockets for this client + /// Total number of subscriptions + /// Total download speed + /// State of each socket connection + public record SocketApiClientState( + int Connections, + int Subscriptions, + double DownloadSpeed, + List ConnectionStates) + { + /// + /// Print the state of the client + /// + /// + /// + protected virtual bool PrintMembers(StringBuilder sb) + { + sb.AppendLine(); + sb.AppendLine($"\tTotal connections: {Connections}"); + sb.AppendLine($"\tTotal subscriptions: {Subscriptions}"); + sb.AppendLine($"\tDownload speed: {DownloadSpeed} kbps"); + sb.AppendLine($"\tConnections:"); + ConnectionStates.ForEach(cs => + { + sb.AppendLine($"\t\tId: {cs.Id}"); + sb.AppendLine($"\t\tAddress: {cs.Address}"); + sb.AppendLine($"\t\tTotal subscriptions: {cs.Subscriptions}"); + sb.AppendLine($"\t\tStatus: {cs.Status}"); + sb.AppendLine($"\t\tAuthenticated: {cs.Authenticated}"); + sb.AppendLine($"\t\tDownload speed: {cs.DownloadSpeed} kbps"); + sb.AppendLine($"\t\tPending queries: {cs.PendingQueries}"); + if (cs.SubscriptionStates?.Count > 0) + { + sb.AppendLine($"\t\tSubscriptions:"); + cs.SubscriptionStates.ForEach(subState => + { + sb.AppendLine($"\t\t\tId: {subState.Id}"); + sb.AppendLine($"\t\t\tConfirmed: {subState.Confirmed}"); + sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}"); + sb.AppendLine($"\t\t\tIdentifiers: [{string.Join(",", subState.Identifiers)}]"); + }); + } + }); + + return true; } - return sb.ToString(); } /// diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs index 637b503..6389ad4 100644 --- a/CryptoExchange.Net/Sockets/SocketConnection.cs +++ b/CryptoExchange.Net/Sockets/SocketConnection.cs @@ -19,6 +19,28 @@ namespace CryptoExchange.Net.Sockets /// public class SocketConnection { + /// + /// State of a the connection + /// + /// The id of the socket connection + /// The connection URI + /// Number of subscriptions on this socket + /// Socket status + /// If the connection is authenticated + /// Download speed over this socket + /// Number of non-completed queries + /// State for each subscription on this socket + public record SocketConnectionState( + int Id, + string Address, + int Subscriptions, + SocketStatus Status, + bool Authenticated, + double DownloadSpeed, + int PendingQueries, + List SubscriptionStates + ); + /// /// Connection lost event /// @@ -620,6 +642,24 @@ namespace CryptoExchange.Net.Sockets return _listeners.OfType().SingleOrDefault(s => s.Id == id); } + /// + /// Get the state of the connection + /// + /// + public SocketConnectionState GetState(bool includeSubDetails) + { + return new SocketConnectionState( + SocketId, + ConnectionUri.AbsoluteUri, + UserSubscriptionCount, + Status, + Authenticated, + IncomingKbps, + PendingQueries: _listeners.OfType().Count(x => !x.Completed), + includeSubDetails ? Subscriptions.Select(sub => sub.GetState()).ToList() : new List() + ); + } + /// /// Send a query request and wait for an answer /// diff --git a/CryptoExchange.Net/Sockets/Subscription.cs b/CryptoExchange.Net/Sockets/Subscription.cs index 9fd1f21..ac5141c 100644 --- a/CryptoExchange.Net/Sockets/Subscription.cs +++ b/CryptoExchange.Net/Sockets/Subscription.cs @@ -156,6 +156,29 @@ namespace CryptoExchange.Net.Sockets { Exception?.Invoke(e); } + + /// + /// State of this subscription + /// + /// The id of the subscription + /// True when the subscription query is handled (either accepted or rejected) + /// Number of times this subscription got a message + /// Identifiers the subscription is listening to + public record SubscriptionState( + int Id, + bool Confirmed, + int Invocations, + HashSet Identifiers + ); + + /// + /// Get the state of this subscription + /// + /// + public SubscriptionState GetState() + { + return new SubscriptionState(Id, Confirmed, TotalInvocations, ListenerIdentifiers); + } } ///