Files
watcheragent/WatcherAgent/src/hardware/gpu.rs

106 lines
3.2 KiB
Rust

use anyhow::Result;
use nvml_wrapper::Nvml;
use std::error::Error;
#[derive(Debug)]
pub struct GpuInfo {
pub name: Option<String>,
pub current_load: Option<f64>,
pub current_temp: Option<f64>,
pub vram_total: Option<f64>,
pub vram_used: Option<f64>,
}
pub async fn get_gpu_info() -> Result<GpuInfo, Box<dyn Error + Send + Sync>> {
match get_gpu_metrics() {
Ok((gpu_temp, gpu_load, vram_used, vram_total)) => {
let gpu_name = detect_gpu_name();
Ok(GpuInfo {
name: Some(gpu_name),
current_load: Some(gpu_load),
current_temp: Some(gpu_temp),
vram_total: Some(vram_total),
vram_used: Some(vram_used),
})
}
Err(e) => {
// Graceful fallback: log error, return empty/None values
eprintln!("GPU info unavailable: {e}");
Ok(GpuInfo {
name: Some(detect_gpu_name()),
current_load: None,
current_temp: None,
vram_total: None,
vram_used: None,
})
}
}
}
pub fn get_gpu_metrics() -> Result<(f64, f64, f64, f64), Box<dyn Error + Send + Sync>> {
let nvml = Nvml::init();
if let Ok(nvml) = nvml {
if let Ok(device) = nvml.device_by_index(0) {
let temp = device
.temperature(nvml_wrapper::enum_wrappers::device::TemperatureSensor::Gpu)
.unwrap_or(0) as f64;
let load = device
.utilization_rates()
.map(|u| u.gpu as f64)
.unwrap_or(0.0);
let mem = device.memory_info().ok();
let used = mem.clone().map(|m| m.used as f64).unwrap_or(0.0);
let total = mem.map(|m| m.total as f64).unwrap_or(0.0);
Ok((temp, load, used, total))
} else {
Err(anyhow::anyhow!("No NVIDIA GPU found").into())
}
} else {
Err(anyhow::anyhow!("Failed to initialize NVML").into())
}
}
fn detect_gpu_name() -> String {
try_nvml_gpu_name()
.or_else(fallback_gpu_name)
.unwrap_or_else(|| "Unknown GPU".to_string())
}
fn try_nvml_gpu_name() -> Option<String> {
let nvml = Nvml::init().ok()?;
let device = nvml.device_by_index(0).ok()?;
device.name().ok().map(|s| s.to_string())
}
fn fallback_gpu_name() -> Option<String> {
#[cfg(target_os = "linux")]
{
let output = std::process::Command::new("lshw")
.args(&["-C", "display"])
.output()
.ok()?;
String::from_utf8_lossy(&output.stdout)
.lines()
.find(|l| l.contains("product:"))
.map(|l| l.trim().replace("product:", "").trim().to_string())
}
#[cfg(target_os = "windows")]
{
let output = std::process::Command::new("wmic")
.args(["path", "win32_VideoController", "get", "name"])
.output()
.ok()?;
String::from_utf8_lossy(&output.stdout)
.lines()
.skip(1) // Skip header
.find(|s| !s.trim().is_empty())
.map(|s| s.trim().to_string())
}
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
{
None
}
}