diff --git a/WatcherAgent/Cargo.toml b/WatcherAgent/Cargo.toml index 1a8524b..3c79d8d 100644 --- a/WatcherAgent/Cargo.toml +++ b/WatcherAgent/Cargo.toml @@ -19,8 +19,10 @@ nvml-wrapper = "0.10" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["winuser", "pdh", "ifmib", "iphlpapi", "winerror"] } +winapi = { version = "0.3", features = ["winuser", "pdh", "ifmib", "iphlpapi", "winerror" ,"wbemcli", "combaseapi"] } systemstat = "0.2.5" +com = "0.2" +widestring = "0.5" [target.'cfg(unix)'.dependencies] glob = "0.3" \ No newline at end of file diff --git a/WatcherAgent/src/main.rs b/WatcherAgent/src/main.rs index c740f27..e66276c 100644 --- a/WatcherAgent/src/main.rs +++ b/WatcherAgent/src/main.rs @@ -420,10 +420,10 @@ impl MetricsCollector { fn get_cpu_temp() -> Option { println!("Attempting to get CPU temperature..."); + #[cfg(target_os = "linux")] { - // Versuche mehrere Methoden der Reihe nach - // 1. sensors-Befehl + println!(""); if let Ok(output) = Command::new("sensors").output() { let stdout = String::from_utf8_lossy(&output.stdout); for line in stdout.lines() { @@ -472,122 +472,157 @@ fn get_cpu_temp() -> Option { #[cfg(target_os = "windows")] { - use systemstat::{Platform, System as SysStat}; - let sys = SysStat::new(); - match sys.cpu_temp() { - Ok(temp) => return Some(temp), - Err(e) => { - eprintln!("Fehler beim Lesen der CPU-Temperatur: {}", e); - return None; - } + // Method 1: Try WMI first (for modern hardware) + if let Some(temp) = get_cpu_temp_wmi() { + return Some(temp); } + + // Method 2: Fallback to Open Hardware Monitor (HTTP API) + if let Some(temp) = get_cpu_temp_ohm() { + return Some(temp); + } + + // Method 3: Fallback to CoreTemp (shared memory) + if let Some(temp) = get_cpu_temp_coretemp() { + return Some(temp); + } + + eprintln!("All methods failed to read CPU temperature."); + None } #[cfg(not(any(target_os = "linux", target_os = "windows")))] { - // Fallback for unsupported OS println!("CPU temperature retrieval not supported on this OS."); None } } -/* -#[cfg(target_os = "linux")] -{ - // Versuche mehrere Methoden der Reihe nach - // 1. sensors-Befehl - if let Ok(output) = Command::new("sensors").output() { - let stdout = String::from_utf8_lossy(&output.stdout); - for line in stdout.lines() { - if line.contains("Package id") || line.contains("Tdie") || line.contains("CPU Temp") - { - if let Some(temp_str) = line - .split('+') - .nth(1) - .and_then(|s| s.split_whitespace().next()) - { - if let Ok(temp) = temp_str.replace("°C", "").parse::() { - return Some(temp); - } - } +#[cfg(target_os = "windows")] +use com::runtime::init_runtime; +use com::sys::{CLSCTX_INPROC_SERVER, COINIT_MULTITHREADED}; +use widestring::U16CString; +use winapi::shared::wtypes::VT_I4; +use winapi::um::oaidl::VARIANT; +use winapi::um::{combaseapi, wbemcli}; + +#[cfg(target_os = "windows")] + +fn get_cpu_temp_wmi() -> Option { + init_runtime().ok()?; + + unsafe { + let mut locator: *mut wbemcli::IWbemLocator = std::ptr::null_mut(); + let hr = combaseapi::CoCreateInstance( + &wbemcli::CLSID_WbemLocator, + std::ptr::null_mut(), + CLSCTX_INPROC_SERVER, + &wbemcli::IID_IWbemLocator, + &mut locator as *mut _ as *mut _, + ); + + if hr != 0 { + eprintln!("Failed to create WbemLocator (HRESULT: {})", hr); + return None; + } + + let mut services: *mut wbemcli::IWbemServices = std::ptr::null_mut(); + let namespace = U16CString::from_str("root\\wmi").unwrap(); + let hr = (*locator).ConnectServer( + namespace.as_ptr().cast_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + 0, + std::ptr::null_mut(), + std::ptr::null_mut(), + &mut services, + ); + + if hr != 0 { + eprintln!("Failed to connect to WMI (HRESULT: {})", hr); + return None; + } + + let query = U16CString::from_str("SELECT * FROM MSAcpi_ThermalZoneTemperature").unwrap(); + let mut enumerator: *mut wbemcli::IEnumWbemClassObject = std::ptr::null_mut(); + let hr = (*services).ExecQuery( + U16CString::from_str("WQL").unwrap().as_ptr().cast_mut(), + query.as_ptr().cast_mut(), + wbemcli::WBEM_FLAG_FORWARD_ONLY as i32, + std::ptr::null_mut(), + &mut enumerator, + ); + + if hr != 0 { + eprintln!("Failed to execute WMI query (HRESULT: {})", hr); + return None; + } + + let mut temp_celsius = None; + loop { + let mut obj: *mut wbemcli::IWbemClassObject = std::ptr::null_mut(); + let mut returned = 0; + let hr = (*enumerator).Next( + wbemcli::WBEM_INFINITE.try_into().unwrap(), + 1, + &mut obj, + &mut returned, + ); + + if hr != 0 || returned == 0 { + break; + } + + let mut variant = std::mem::zeroed::(); + let hr = (*obj).Get( + U16CString::from_str("CurrentTemperature").unwrap().as_ptr(), + 0, + &mut variant, + std::ptr::null_mut(), + std::ptr::null_mut(), + ); + + if hr == 0 && variant.n1.n2().vt as u32 == VT_I4 { + let temp_kelvin = *variant.n1.n2().n3.intVal() as f32 / 10.0; + Some(temp_kelvin - 273.15); + } else { + return None; } } - } - // 2. Sysfs (Intel/AMD) - if let Ok(content) = fs::read_to_string("/sys/class/thermal/thermal_zone0/temp") { - if let Ok(temp) = content.trim().parse::() { - return Some(temp / 1000.0); - } - } - - // 3. Alternative Sysfs-Pfade - let paths = [ - "/sys/class/hwmon/hwmontemp1_input", - "/sys/class/hwmon/hwmondevice/temp1_input", - ]; - - for path_pattern in &paths { - if let Ok(paths) = glob::glob(path_pattern) { - for path in paths.flatten() { - if let Ok(content) = fs::read_to_string(&path) { - if let Ok(temp) = content.trim().parse::() { - return Some(temp / 1000.0); - } - } - } - } + temp_celsius } } #[cfg(target_os = "windows")] -fn get_cpu_temp() -> Option { - use winapi::um::pdh::{ - PdhAddCounter, PdhCollectQueryData, PdhGetFormattedCounterValue, PdhOpenQuery, - PDH_FMT_DOUBLE, - }; - use winapi::um::pdhmsg::PDH_FMT_COUNTERVALUE; +fn get_cpu_temp_ohm() -> Option { + // Open Hardware Monitor HTTP API fallback + let output = Command::new("curl") + .args(&["-s", "http://localhost:8085/data.json"]) + .output() + .ok()?; - unsafe { - let mut query_handle = 0 as u64; - if PdhOpenQuery(std::ptr::null_mut(), 0, &mut query_handle) != 0 { - return None; - } - - let mut counter_handle = 0 as u64; - if PdhAddCounter( - query_handle, - "\\Processor Information(_Total)\\Temperature", - 0, - &mut counter_handle, - ) != 0 - { - return None; - } - - if PdhCollectQueryData(query_handle) != 0 { - return None; - } - - let mut counter_value = PDH_FMT_COUNTERVALUE { - CStatus: 0, - union: PDH_FMT_DOUBLE { doubleValue: 0.0 }, - }; - if PdhGetFormattedCounterValue( - counter_handle, - PDH_FMT_DOUBLE, - std::ptr::null_mut(), - &mut counter_value, - ) != 0 - { - return None; - } - - Some(counter_value.union.doubleValue as f32) - } + let json: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?; + json["Children"][0]["Children"][0]["Children"] + .as_array()? + .iter() + .find(|s| s["Text"].as_str() == Some("CPU Package")) + .and_then(|s| s["Value"].as_f64()) + .map(|t| t as f32) } -None*/ +#[cfg(target_os = "windows")] +fn get_cpu_temp_coretemp() -> Option { + // CoreTemp shared memory fallback + let output = Command::new("CoreTemp.exe").arg("/S").output().ok()?; + + String::from_utf8_lossy(&output.stdout) + .lines() + .find(|line| line.contains("Core 0")) + .and_then(|line| line.split_whitespace().last()) + .and_then(|s| s.replace("°C", "").parse::().ok()) +} fn get_disk_info() -> (f64, f64, f64) { let mut sys = System::new(); @@ -614,6 +649,7 @@ fn get_disk_info() -> (f64, f64, f64) { total_size as f64 // in Bytes } else { // Fallback: Versuche df unter Linux + println!("Fallback: Using 'df' command to get disk info."); #[cfg(target_os = "linux")] { if let Ok(output) = Command::new("df")