using System.Globalization; using GSS2.Core.Extentions; using Microsoft.Extensions.Logging; namespace GSS2.Core.Hardware; public class ComputeResourcesService { public record CpuUsage(double AllUsagePercent, List CoreUsagePercents); public record CpuTemperature(double Current, double Critical); public record MemoryUsage( long TotalB, long UsedB, long AvailableB, double TotalkB, double UsedkB, double AvailablekB, double TotalMB, double UsedMB, double AvailableMB, double TotalGB, double UsedGB, double AvailableGB) { public MemoryUsage(long totalB, long usedB, long availableB) : this( TotalB: totalB, UsedB: usedB, AvailableB: availableB, TotalkB: totalB / 1000, UsedkB: usedB / 1000, AvailablekB: availableB / 1000, TotalMB: totalB / Math.Pow(1000.0, 2), UsedMB: usedB / Math.Pow(1000.0, 2), AvailableMB: availableB / Math.Pow(1000.0, 2), TotalGB: totalB / Math.Pow(1000.0, 3), UsedGB: usedB / Math.Pow(1000.0, 3), AvailableGB: availableB / Math.Pow(1000.0, 3) ) { } } public record ComputeResourcesSnapshot(CpuUsage CpuUsage, MemoryUsage MemoryUsage, CpuTemperature CpuTemperature, DateTime Timestamp); private readonly ILogger _logger; private (long idle, long total)? _prevAll = null; private Dictionary? _prevCores = null; public ComputeResourcesService(ILogger logger) { _logger = logger; _logger.LogInformation("Initialization"); _logger.LogInformation("Initialized"); } public ComputeResourcesSnapshot GetSnapshot() { return new ComputeResourcesSnapshot( CpuUsage: GetCpuUsage(), CpuTemperature: GetCpuTemperature(), MemoryUsage: GetMemoryUsage(), Timestamp: DateTime.UtcNow ); } private CpuUsage GetCpuUsage() { static (double usage, long idle, long total) ParceLines(string line, (long idle, long total)? prev) { var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); long user = long.Parse(parts[1]); long nice = long.Parse(parts[2]); long system = long.Parse(parts[3]); long idle = long.Parse(parts[4]); long iowait = line.Length > 5 ? long.Parse(parts[5]) : 0; long irq = line.Length > 6 ? long.Parse(parts[6]) : 0; long softirq = line.Length > 7 ? long.Parse(parts[7]) : 0; long idleTime = idle + iowait; long totalTime = idleTime + user + nice + system + irq + softirq; double usage = 0; if (prev is not null) { long totalDiff = totalTime - prev.Value.total; long idleDiff = idleTime - prev.Value.idle; if (totalDiff > 0) usage = (double)(totalDiff - idleDiff) / totalDiff * 100.0; } return (usage, idleTime, totalTime); } var lines = File.ReadLines("/proc/stat").Where(l => l.StartsWith("cpu")); if (lines.Count() == 0) return new CpuUsage(0, new List()); var (allUsage, allIdle, allTotal) = ParceLines(lines.First(), _prevAll); _prevAll = (allIdle, allTotal); var coreUsages = new List(); foreach (var (i, line) in lines.Skip(1).Enumerate()) { if (_prevCores is null) _prevCores = new Dictionary(); if (!_prevCores.ContainsKey(i)) _prevCores.Add(i, null); var (coreUsage, coreIdle, coreTotal) = ParceLines(line, _prevCores[i]); _prevCores[i] = (coreIdle, coreTotal); coreUsages.Add(coreUsage); } return new CpuUsage( AllUsagePercent: allUsage, CoreUsagePercents: coreUsages ); } private CpuTemperature GetCpuTemperature() { var currentStr = File.ReadAllText("/sys/class/thermal/thermal_zone0/temp").Trim(); var current = int.Parse(currentStr) / 1000.0; var criticalFile = Directory.GetFiles("/sys/class/thermal/thermal_zone0/", "trip_point_*_type") .FirstOrDefault(f => File.ReadAllText(f).Trim() == "critical")? .Replace("_type", "_temp"); double critical = 100.0; if (criticalFile is not null) { var criticalStr = File.ReadAllText(criticalFile).Trim(); critical = int.Parse(criticalStr) / 1000.0; } return new CpuTemperature( Current: current, Critical: critical ); } private MemoryUsage GetMemoryUsage() { var memInfo = File.ReadAllLines("/proc/meminfo") .Select(l => l.Split(':', 2)) .ToDictionary( p => p[0], p => long.Parse(p[1].Trim().Split(' ')[0], CultureInfo.InvariantCulture) * 1024 ); long total = memInfo["MemTotal"]; long available = memInfo.ContainsKey("MemAvailable") ? memInfo["MemAvailable"] : memInfo["MemFree"]; long used = total - available; return new MemoryUsage(totalB: total, usedB: used, availableB: available); } }