forked from amkovkov/GranuSightSoftware2
144 lines
5.2 KiB
C#
144 lines
5.2 KiB
C#
using System.Globalization;
|
|
|
|
using GSS2.Core.Extensions;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace GSS2.Core.Hardware;
|
|
|
|
public class ComputeResourcesService
|
|
{
|
|
public record CpuUsage(double AllUsagePercent, List<double> 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<ComputeResourcesService> _logger;
|
|
|
|
private (long idle, long total)? _prevAll = null;
|
|
private Dictionary<int, (long idle, long total)?>? _prevCores = null;
|
|
|
|
public ComputeResourcesService(ILogger<ComputeResourcesService> 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) ParseLines(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<double>());
|
|
|
|
var (allUsage, allIdle, allTotal) = ParseLines(lines.First(), _prevAll);
|
|
_prevAll = (allIdle, allTotal);
|
|
|
|
var coreUsages = new List<double>();
|
|
|
|
foreach (var (i, line) in lines.Skip(1).Enumerate())
|
|
{
|
|
if (_prevCores is null)
|
|
_prevCores = new Dictionary<int, (long idle, long total)?>();
|
|
if (!_prevCores.ContainsKey(i))
|
|
_prevCores.Add(i, null);
|
|
var (coreUsage, coreIdle, coreTotal) = ParseLines(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);
|
|
}
|
|
}
|