276 lines
7.0 KiB
C#
276 lines
7.0 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using LibreHardwareMonitor.Hardware;
|
|
|
|
public class Telemetry : IDisposable
|
|
{
|
|
private const int UpdateRateDefaultMs = 1000;
|
|
public int UpdateRateMs => UpdateRateDefaultMs;
|
|
|
|
private readonly UdpSender udp = new UdpSender();
|
|
private Computer computer = new Computer();
|
|
|
|
private IHardware? cpuHw;
|
|
private IHardware? gpuHw;
|
|
private IHardware? memHw;
|
|
|
|
private ISensor[] cpuLoadSensors = Array.Empty<ISensor>();
|
|
private ISensor? cpuTempSensor;
|
|
|
|
private ISensor? gpu3DLoadSensor;
|
|
private ISensor? gpuTempSensor;
|
|
private ISensor? gpuVramUsedSensor;
|
|
private ISensor? gpuVramTotalSensor;
|
|
|
|
private ISensor? memUsedSensor;
|
|
private ISensor? memAvailSensor;
|
|
|
|
private static readonly CultureInfo CI = CultureInfo.InvariantCulture;
|
|
|
|
// Restart LHM every 30 minutes
|
|
private readonly TimeSpan restartInterval = TimeSpan.FromMinutes(30);
|
|
private DateTime lastRestart = DateTime.UtcNow;
|
|
|
|
public void Initialize()
|
|
{
|
|
ConfigureComputer();
|
|
computer.Open();
|
|
CacheHardwareAndSensors();
|
|
}
|
|
|
|
private void ConfigureComputer()
|
|
{
|
|
computer.IsCpuEnabled = true;
|
|
computer.IsGpuEnabled = true;
|
|
computer.IsMemoryEnabled = true;
|
|
|
|
// True minimal mode
|
|
computer.IsMotherboardEnabled = false;
|
|
computer.IsControllerEnabled = false;
|
|
computer.IsNetworkEnabled = false;
|
|
computer.IsStorageEnabled = false;
|
|
computer.IsBatteryEnabled = false;
|
|
}
|
|
|
|
private void RestartComputerIfNeeded()
|
|
{
|
|
if (DateTime.UtcNow - lastRestart < restartInterval)
|
|
return;
|
|
|
|
lastRestart = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
computer.Close();
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
computer = new Computer();
|
|
ConfigureComputer();
|
|
computer.Open();
|
|
CacheHardwareAndSensors();
|
|
}
|
|
|
|
private void CacheHardwareAndSensors()
|
|
{
|
|
cpuHw = null;
|
|
gpuHw = null;
|
|
memHw = null;
|
|
|
|
cpuLoadSensors = Array.Empty<ISensor>();
|
|
cpuTempSensor = null;
|
|
|
|
gpu3DLoadSensor = null;
|
|
gpuTempSensor = null;
|
|
gpuVramUsedSensor = null;
|
|
gpuVramTotalSensor = null;
|
|
|
|
memUsedSensor = null;
|
|
memAvailSensor = null;
|
|
|
|
foreach (var hw in computer.Hardware)
|
|
{
|
|
hw.Update();
|
|
|
|
switch (hw.HardwareType)
|
|
{
|
|
case HardwareType.Cpu:
|
|
cpuHw = hw;
|
|
CacheCpuSensors(hw);
|
|
break;
|
|
|
|
case HardwareType.GpuNvidia:
|
|
case HardwareType.GpuAmd:
|
|
case HardwareType.GpuIntel:
|
|
gpuHw = hw;
|
|
CacheGpuSensors(hw);
|
|
break;
|
|
|
|
case HardwareType.Memory:
|
|
memHw = hw;
|
|
CacheMemorySensors(hw);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CacheCpuSensors(IHardware hw)
|
|
{
|
|
var loads = new System.Collections.Generic.List<ISensor>();
|
|
|
|
foreach (var s in hw.Sensors)
|
|
{
|
|
if (s.SensorType == SensorType.Load &&
|
|
s.Name.Contains("CPU Core"))
|
|
loads.Add(s);
|
|
|
|
if (s.SensorType == SensorType.Temperature)
|
|
{
|
|
if (s.Name == "Core (Tctl/Tdie)")
|
|
cpuTempSensor = s;
|
|
else if (cpuTempSensor == null)
|
|
cpuTempSensor = s;
|
|
}
|
|
}
|
|
|
|
cpuLoadSensors = loads.ToArray();
|
|
}
|
|
|
|
private void CacheGpuSensors(IHardware hw)
|
|
{
|
|
foreach (var s in hw.Sensors)
|
|
{
|
|
if (s.SensorType == SensorType.Load)
|
|
{
|
|
if (s.Name == "D3D 3D")
|
|
gpu3DLoadSensor = s;
|
|
else if (gpu3DLoadSensor == null && s.Name == "GPU Core")
|
|
gpu3DLoadSensor = s;
|
|
}
|
|
|
|
if (s.SensorType == SensorType.Temperature)
|
|
{
|
|
if (s.Name == "GPU Core")
|
|
gpuTempSensor = s;
|
|
else if (gpuTempSensor == null)
|
|
gpuTempSensor = s;
|
|
}
|
|
|
|
if (s.SensorType == SensorType.SmallData)
|
|
{
|
|
if (s.Name == "GPU Memory Used")
|
|
gpuVramUsedSensor = s;
|
|
|
|
if (s.Name == "GPU Memory Total")
|
|
gpuVramTotalSensor = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CacheMemorySensors(IHardware hw)
|
|
{
|
|
foreach (var s in hw.Sensors)
|
|
{
|
|
if (s.SensorType == SensorType.Data && s.Name == "Memory Used")
|
|
memUsedSensor = s;
|
|
|
|
if (s.SensorType == SensorType.Data && s.Name == "Memory Available")
|
|
memAvailSensor = s;
|
|
}
|
|
}
|
|
|
|
public void UpdateAndSend()
|
|
{
|
|
RestartComputerIfNeeded();
|
|
|
|
cpuHw?.Update();
|
|
gpuHw?.Update();
|
|
memHw?.Update();
|
|
|
|
float cpu = GetCpuLoadPercent();
|
|
float cpuTemp = GetCpuTemperaturePercent();
|
|
float mem = GetMemoryUsagePercent();
|
|
float gpu3d = GetGpu3DLoad();
|
|
float gpuTemp = GetGpuTemperaturePercent();
|
|
float vram = GetGpuVramPercent();
|
|
|
|
float[] packet =
|
|
{
|
|
cpu,
|
|
cpuTemp,
|
|
mem,
|
|
gpu3d,
|
|
gpuTemp,
|
|
vram,
|
|
0f,
|
|
0f
|
|
};
|
|
|
|
udp.SendFloats(packet);
|
|
}
|
|
|
|
private float GetCpuLoadPercent()
|
|
{
|
|
if (cpuLoadSensors.Length == 0) return 0;
|
|
|
|
float total = 0;
|
|
int count = 0;
|
|
|
|
foreach (var s in cpuLoadSensors)
|
|
{
|
|
total += s.Value ?? 0;
|
|
count++;
|
|
}
|
|
|
|
return count == 0 ? 0 : total / count;
|
|
}
|
|
|
|
private float GetCpuTemperaturePercent()
|
|
{
|
|
float t = cpuTempSensor?.Value ?? 0;
|
|
return Math.Clamp(t, 0, 100);
|
|
}
|
|
|
|
private float GetGpu3DLoad()
|
|
{
|
|
return gpu3DLoadSensor?.Value ?? 0;
|
|
}
|
|
|
|
private float GetGpuTemperaturePercent()
|
|
{
|
|
float t = gpuTempSensor?.Value ?? 0;
|
|
return Math.Clamp(t, 0, 100);
|
|
}
|
|
|
|
private float GetGpuVramPercent()
|
|
{
|
|
float used = gpuVramUsedSensor?.Value ?? 0;
|
|
float total = gpuVramTotalSensor?.Value ?? 0;
|
|
|
|
if (total <= 0) return 0;
|
|
return (used / total) * 100f;
|
|
}
|
|
|
|
private float GetMemoryUsagePercent()
|
|
{
|
|
float used = memUsedSensor?.Value ?? 0;
|
|
float avail = memAvailSensor?.Value ?? 0;
|
|
|
|
float total = used + avail;
|
|
if (total <= 0) return 0;
|
|
|
|
return (used / total) * 100f;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
udp.Dispose();
|
|
computer.Close();
|
|
}
|
|
}
|