159 lines
7.0 KiB
C#
159 lines
7.0 KiB
C#
using LibreHardwareMonitor.Hardware;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Inventory.Core
|
|
{
|
|
public class HealthMonitor
|
|
{
|
|
private class UpdateVisitor : IVisitor
|
|
{
|
|
public void VisitComputer(IComputer computer)
|
|
{
|
|
computer.Traverse(this);
|
|
}
|
|
public void VisitHardware(IHardware hardware)
|
|
{
|
|
hardware.Update();
|
|
foreach (IHardware subHardware in hardware.SubHardware) subHardware.Accept(this);
|
|
}
|
|
public void VisitSensor(ISensor sensor) { }
|
|
public void VisitParameter(IParameter parameter) { }
|
|
}
|
|
|
|
private ISensor? FindSensor(IHardware hardware, SensorType type, params string[] names)
|
|
{
|
|
foreach (var name in names)
|
|
{
|
|
var sensor = hardware.Sensors.FirstOrDefault(s => s.SensorType == type && s.Name.Contains(name, StringComparison.OrdinalIgnoreCase));
|
|
if (sensor != null)
|
|
{
|
|
return sensor;
|
|
}
|
|
}
|
|
// If no name matches, return the first sensor of that type as a last resort.
|
|
return hardware.Sensors.FirstOrDefault(s => s.SensorType == type);
|
|
}
|
|
|
|
public HealthMetricsReport CollectHealthMetrics()
|
|
{
|
|
var report = new HealthMetricsReport();
|
|
|
|
try
|
|
{
|
|
var computer = new Computer
|
|
{
|
|
IsCpuEnabled = true,
|
|
IsGpuEnabled = true,
|
|
IsMemoryEnabled = true,
|
|
IsMotherboardEnabled = true, // For fan speeds
|
|
IsStorageEnabled = true,
|
|
IsBatteryEnabled = true
|
|
};
|
|
|
|
computer.Open();
|
|
computer.Accept(new UpdateVisitor());
|
|
|
|
// CPU Metrics
|
|
var cpu = computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.Cpu);
|
|
if (cpu != null)
|
|
{
|
|
report.CpuTemp = FindSensor(cpu, SensorType.Temperature, "CPU Package", "Core (Average)")?.Value;
|
|
report.CpuLoad = FindSensor(cpu, SensorType.Load, "CPU Total")?.Value;
|
|
report.CpuPower = FindSensor(cpu, SensorType.Power, "CPU Package", "CPU Total")?.Value;
|
|
}
|
|
|
|
// GPU Metrics
|
|
// Prioritize dedicated GPUs (Nvidia, AMD) over integrated (Intel)
|
|
var gpu = computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.GpuNvidia || h.HardwareType == HardwareType.GpuAmd)
|
|
?? computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.GpuIntel);
|
|
|
|
if (gpu != null)
|
|
{
|
|
report.GpuTemp = FindSensor(gpu, SensorType.Temperature, "GPU Core")?.Value;
|
|
report.GpuLoad = FindSensor(gpu, SensorType.Load, "GPU Core", "D3D 3D", "GPU Utilization")?.Value;
|
|
report.GpuPower = FindSensor(gpu, SensorType.Power, "GPU Package", "GPU Power", "GPU Total")?.Value;
|
|
report.GpuClock = FindSensor(gpu, SensorType.Clock, "GPU Core")?.Value;
|
|
}
|
|
|
|
// Memory Metrics
|
|
var memory = computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.Memory);
|
|
if (memory != null)
|
|
{
|
|
report.RamLoad = FindSensor(memory, SensorType.Load, "Memory")?.Value;
|
|
}
|
|
|
|
// Drive Health
|
|
foreach (var hardware in computer.Hardware.Where(h => h.HardwareType == HardwareType.Storage))
|
|
{
|
|
var tempSensor = hardware.Sensors.FirstOrDefault(s => s.SensorType == SensorType.Temperature);
|
|
var failureSensor = hardware.Sensors.FirstOrDefault(s => s.Name == "Predict Failure");
|
|
|
|
double? healthPercentage = null;
|
|
|
|
// Prioritize "Remaining Life" sensors, as they are a direct measure.
|
|
var remainingLifeSensor = hardware.Sensors.FirstOrDefault(s => s.SensorType == SensorType.Level && s.Name.Contains("Remaining Life"));
|
|
if (remainingLifeSensor?.Value != null)
|
|
{
|
|
healthPercentage = remainingLifeSensor.Value;
|
|
}
|
|
else
|
|
{
|
|
// Fallback to "Percentage Used" sensors.
|
|
var usedSensor = hardware.Sensors.FirstOrDefault(s => s.SensorType == SensorType.Level && s.Name.Contains("Used"));
|
|
if (usedSensor?.Value != null)
|
|
healthPercentage = 100 - usedSensor.Value;
|
|
}
|
|
|
|
report.DriveHealth.Add(new DriveHealthInfo {
|
|
Model = hardware.Name,
|
|
Temperature = tempSensor?.Value,
|
|
IsFailing = failureSensor?.Value == 1,
|
|
HealthPercentage = healthPercentage
|
|
});
|
|
}
|
|
|
|
// Fan Speeds (from all hardware)
|
|
// Recursively search for fan sensors in all hardware and sub-hardware.
|
|
foreach (var hardware in computer.Hardware)
|
|
{
|
|
CollectFanSpeedsRecursive(hardware, report.FanSpeeds);
|
|
}
|
|
|
|
// Battery Health
|
|
var battery = computer.Hardware.FirstOrDefault(h => h.HardwareType == HardwareType.Battery);
|
|
if (battery != null)
|
|
{
|
|
var fullChargeCapacity = FindSensor(battery, SensorType.Energy, "Full Charge Capacity", "Fully Charged Capacity")?.Value;
|
|
var designCapacity = FindSensor(battery, SensorType.Energy, "Design Capacity", "Designed Capacity")?.Value;
|
|
if (fullChargeCapacity.HasValue && designCapacity.HasValue && designCapacity > 0)
|
|
{
|
|
report.BatteryHealth = Math.Round((double)(fullChargeCapacity.Value / designCapacity.Value * 100));
|
|
}
|
|
}
|
|
computer.Close();
|
|
}
|
|
catch { /* Silently fail if LibreHardwareMonitor can't run */ }
|
|
|
|
return report;
|
|
}
|
|
|
|
private void CollectFanSpeedsRecursive(IHardware hardware, List<FanSpeedInfo> fanSpeeds)
|
|
{
|
|
// Add fans from the current hardware
|
|
foreach (var sensor in hardware.Sensors.Where(s => s.SensorType == SensorType.Fan && s.Value.HasValue))
|
|
{
|
|
fanSpeeds.Add(new FanSpeedInfo
|
|
{
|
|
Name = $"{hardware.Name} - {sensor.Name}",
|
|
Rpm = sensor.Value
|
|
});
|
|
}
|
|
|
|
// Recurse into sub-hardware
|
|
foreach (var subHardware in hardware.SubHardware)
|
|
CollectFanSpeedsRecursive(subHardware, fanSpeeds);
|
|
}
|
|
}
|
|
} |