namespace ServiceLib.Services.Statistics; public class StatisticsXrayService { private const long linkBase = 1024; private ServerSpeedItem _serverSpeedItem = new(); private readonly Config _config; private bool _exitFlag; private readonly Func? _updateFunc; private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars"; public StatisticsXrayService(Config config, Func updateFunc) { _config = config; _updateFunc = updateFunc; _exitFlag = false; _ = Task.Run(Run); } public void Close() { _exitFlag = true; } private async Task Run() { while (!_exitFlag) { await Task.Delay(1000); try { if (_config.RunningCoreType != ECoreType.Xray) { continue; } var result = await HttpClientHelper.Instance.TryGetAsync(Url); if (result != null) { var server = ParseOutput(result) ?? new ServerSpeedItem(); await _updateFunc?.Invoke(server); } } catch { // ignored } } } private ServerSpeedItem? ParseOutput(string result) { try { var source = JsonUtils.Deserialize(result); if (source?.stats?.outbound == null) { return null; } ServerSpeedItem server = new(); foreach (var key in source.stats.outbound.Keys.Cast()) { var value = source.stats.outbound[key]; if (value == null) { continue; } var state = JsonUtils.Deserialize(value.ToString()); if (key.StartsWith(Global.ProxyTag)) { server.ProxyUp += state.uplink / linkBase; server.ProxyDown += state.downlink / linkBase; } else if (key == Global.DirectTag) { server.DirectUp = state.uplink / linkBase; server.DirectDown = state.downlink / linkBase; } } if (server.DirectDown < _serverSpeedItem.DirectDown || server.ProxyDown < _serverSpeedItem.ProxyDown) { _serverSpeedItem = new(); return null; } ServerSpeedItem curItem = new() { ProxyUp = server.ProxyUp - _serverSpeedItem.ProxyUp, ProxyDown = server.ProxyDown - _serverSpeedItem.ProxyDown, DirectUp = server.DirectUp - _serverSpeedItem.DirectUp, DirectDown = server.DirectDown - _serverSpeedItem.DirectDown, }; _serverSpeedItem = server; return curItem; } catch { // ignored } return null; } }