using System.Collections.ObjectModel; using Microsoft.Extensions.Logging; using CommunityToolkit.Mvvm.ComponentModel; using Avalonia; using Avalonia.Threading; using SkiaSharp; using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView.Painting; using LiveChartsCore.Defaults; using GSS2.Core.Hardware; namespace GSS2.UI.Core.ViewModels; public partial class ResourcesViewModel : ViewModelBase, IDisposable { private bool _disposedValue; private readonly ComputeResourcesService _computeResourcesService; private readonly ILogger _logger; private readonly int _intervalMs = 500; private readonly int _chartsHistorySize = 120; private readonly System.Timers.Timer _updateTimer; [ObservableProperty] private Rect _bounds; [ObservableProperty] private bool _isChartsColumnVisible; [ObservableProperty] private bool _isBarsColumnVisible; [ObservableProperty] private ResourceChartViewModel _cpuUsageChartViewModel; [ObservableProperty] private ResourceChartViewModel _cpuTemperatureChartViewModel; [ObservableProperty] private ResourceChartViewModel _memoryUsageChartViewModel; [ObservableProperty] private ResourceCpuUsageBarsViewModel _cpuUsageBarsViewModel; [ObservableProperty] private ResourceBarViewModel _cpuTemperatureBarViewModel; [ObservableProperty] private ResourceBarViewModel _memoryUsageBarViewModel; public ResourcesViewModel( ILogger logger, ILogger resourceCpuUsageBarsViewModelLogger, ILogger resourceChartViewModelCpuUsageLogger, ILogger resourceChartViewModelCpuTemperatureLogger, ILogger resourceChartViewModelMemoryUsageLogger, ILogger resourceBarViewModelCpuUsageLogger, ILogger resourceBarViewModelCpu0UsageLogger, ILogger resourceBarViewModelCpu1UsageLogger, ILogger resourceBarViewModelCpu2UsageLogger, ILogger resourceBarViewModelCpu3UsageLogger, ILogger resourceBarViewModelCpuTemperatureLogger, ILogger resourceBarViewModelMemoryUsageLogger, ComputeResourcesService computeResourcesService ) { _logger = logger; _logger.LogInformation("Initialization"); _computeResourcesService = computeResourcesService; ResourceChartViewModel CreateChartViewModel(ILogger logger, string name, double minLimit, double maxLimit, double minStep) { return new ResourceChartViewModel( logger: logger, series: [ new LineSeries { Name = name, Values = new ObservableCollection(Enumerable.Repeat(0.0, _chartsHistorySize)), Fill = new SolidColorPaint(SKColors.DeepSkyBlue.WithAlpha(127), 0), GeometrySize = 0, ShowDataLabels = false, Stroke = new SolidColorPaint(SKColors.DeepSkyBlue, 1), GeometryFill = new SolidColorPaint(SKColors.Transparent, 0), GeometryStroke = new SolidColorPaint(SKColors.Transparent, 0), IsHoverable = false, DataPadding = new LiveChartsCore.Drawing.LvcPoint(0.1f, 0.1f) } ], yAxes: [ new Axis { MinLimit = minLimit, MaxLimit = maxLimit, MinStep = minStep, Name = name } ], xAxes: [ new Axis { IsVisible = false, Padding = new LiveChartsCore.Drawing.Padding(0) } ] ); } ResourceBarViewModel CreateBarViewModel(ILogger logger, Func, string> formatter) { return new ResourceBarViewModel( logger: logger, series: [ new PieSeries { Values = new ObservableCollection { new ObservableValue(1) }, Fill = new SolidColorPaint(SKColors.DeepSkyBlue, 0), ShowDataLabels = true, IsHoverable = false, HoverPushout = 0, DataLabelsFormatter = formatter, DataLabelsPosition = LiveChartsCore.Measure.PolarLabelsPosition.ChartCenter, DataLabelsPaint = new SolidColorPaint(SKColors.White) }, new PieSeries { Values = new ObservableCollection { new ObservableValue(1) }, Fill = new SolidColorPaint(SKColors.DeepSkyBlue.WithAlpha(64), 0), ShowDataLabels = false, IsHoverable = false, HoverPushout = 0 } ] ); } CpuUsageChartViewModel = CreateChartViewModel(resourceChartViewModelCpuUsageLogger, "Нагрузка ЦПУ, %", 0, 100, 10); CpuTemperatureChartViewModel = CreateChartViewModel(resourceChartViewModelCpuTemperatureLogger, "Температура ЦПУ, ℃", 0, 100, 10); MemoryUsageChartViewModel = CreateChartViewModel(resourceChartViewModelMemoryUsageLogger, "Нагрузка ОЗУ, МБ", 0, 8000, 1000); CpuUsageBarsViewModel = new ResourceCpuUsageBarsViewModel( resourceCpuUsageBarsViewModelLogger, CreateBarViewModel(resourceBarViewModelCpuUsageLogger, value => $"{value.Model?.Value?.ToString("00.00")} %"), CreateBarViewModel(resourceBarViewModelCpu0UsageLogger, value => $"{value.Model?.Value?.ToString("00.00")} %"), CreateBarViewModel(resourceBarViewModelCpu1UsageLogger, value => $"{value.Model?.Value?.ToString("00.00")} %"), CreateBarViewModel(resourceBarViewModelCpu2UsageLogger, value => $"{value.Model?.Value?.ToString("00.00")} %"), CreateBarViewModel(resourceBarViewModelCpu3UsageLogger, value => $"{value.Model?.Value?.ToString("00.00")} %") ); CpuTemperatureBarViewModel = CreateBarViewModel(resourceBarViewModelCpuTemperatureLogger, value => $"{value.Model?.Value?.ToString("00.00")} ℃"); MemoryUsageBarViewModel = CreateBarViewModel(resourceBarViewModelMemoryUsageLogger, value => $"{value.Model?.Value?.ToString("000.00")} МБ"); Update(); _updateTimer = new System.Timers.Timer(_intervalMs); _updateTimer.Elapsed += UpdateTimerElapsed; _updateTimer.AutoReset = true; _updateTimer.Start(); _logger.LogInformation("Initialized"); } partial void OnBoundsChanged(Rect value) { if (value.Width < 300) { IsChartsColumnVisible = false; IsBarsColumnVisible = true; } else if (value.Width < 400) { IsChartsColumnVisible = true; IsBarsColumnVisible = false; } else { IsChartsColumnVisible = true; IsBarsColumnVisible = true; } } private void UpdateTimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea) => Update(); private void Update() { try { var snapshot = _computeResourcesService.GetSnapshot(); Dispatcher.UIThread.Post(() => { if (CpuTemperatureChartViewModel.YAxes.Count() == 1) CpuTemperatureChartViewModel.YAxes.First().MaxLimit = snapshot.CpuTemperature.Critical; if (MemoryUsageChartViewModel.YAxes.Count() == 1) MemoryUsageChartViewModel.YAxes.First().MaxLimit = snapshot.MemoryUsage.TotalMB; CpuUsageChartViewModel.AppendValue(Math.Round(snapshot.CpuUsage.AllUsagePercent, 2), _chartsHistorySize); CpuTemperatureChartViewModel.AppendValue(Math.Round(snapshot.CpuTemperature.Current, 2), _chartsHistorySize); MemoryUsageChartViewModel.AppendValue(Math.Round(snapshot.MemoryUsage.UsedMB, 2), _chartsHistorySize); CpuUsageBarsViewModel.Cpu.UpdateValue(Math.Round(snapshot.CpuUsage.AllUsagePercent, 2), 100); CpuUsageBarsViewModel.Cpu0.UpdateValue(Math.Round(snapshot.CpuUsage.CoreUsagePercents.Skip(0).First(), 2), 100); CpuUsageBarsViewModel.Cpu1.UpdateValue(Math.Round(snapshot.CpuUsage.CoreUsagePercents.Skip(1).First(), 2), 100); CpuUsageBarsViewModel.Cpu2.UpdateValue(Math.Round(snapshot.CpuUsage.CoreUsagePercents.Skip(2).First(), 2), 100); CpuUsageBarsViewModel.Cpu3.UpdateValue(Math.Round(snapshot.CpuUsage.CoreUsagePercents.Skip(3).First(), 2), 100); CpuTemperatureBarViewModel.UpdateValue(Math.Round(snapshot.CpuTemperature.Current, 2), snapshot.CpuTemperature.Critical); MemoryUsageBarViewModel.UpdateValue(Math.Round(snapshot.MemoryUsage.UsedMB, 2), snapshot.MemoryUsage.TotalMB); }); } catch (Exception e) { _logger.LogWarning(e, "Exception while update"); } } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { try { _updateTimer.Stop(); _updateTimer.Dispose(); } catch { } } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }