modulized everthing
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -123,7 +123,7 @@ jobs:
|
||||
|
||||
docker-build:
|
||||
name: Build Linux Docker Image
|
||||
needs: [native-build, windows-cross]
|
||||
needs: [native-build, windows-cross, detect-project]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::hardware::HardwareInfo;
|
||||
use crate::models::{HardwareDto, HeartbeatDto, IdResponse, MetricDto, RegistrationDto};
|
||||
use anyhow::Result;
|
||||
use reqwest::{Client, StatusCode};
|
||||
@@ -20,16 +21,16 @@ pub async fn register_with_server(base_url: &str) -> Result<(i32, String), Box<d
|
||||
.build()?;
|
||||
|
||||
// Collect hardware info
|
||||
let hardware = HardwareDto::collect().await?;
|
||||
let hardware = HardwareInfo::collect().await?;
|
||||
|
||||
// Prepare registration data
|
||||
let registration = RegistrationDto {
|
||||
id: server_id,
|
||||
ip_address: registered_ip.clone(),
|
||||
cpu_type: hardware.cpu_type,
|
||||
cpu_cores: hardware.cpu_cores,
|
||||
gpu_type: hardware.gpu_type,
|
||||
ram_size: hardware.ram_size,
|
||||
cpu_type: hardware.cpu.name.clone().unwrap_or_default(),
|
||||
cpu_cores: (hardware.cpu.cores).unwrap_or_default(),
|
||||
gpu_type: hardware.gpu.name.clone().unwrap_or_default(),
|
||||
ram_size: hardware.memory.total.unwrap_or_default(),
|
||||
};
|
||||
|
||||
// Try to register (will retry on failure)
|
||||
|
@@ -1,79 +0,0 @@
|
||||
use crate::models::HardwareDto;
|
||||
use nvml_wrapper::Nvml;
|
||||
use sysinfo::System;
|
||||
|
||||
impl HardwareDto {
|
||||
pub async fn collect() -> anyhow::Result<Self> {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_cpu_all();
|
||||
sys.refresh_memory();
|
||||
|
||||
let cpus = sys.cpus();
|
||||
let cpu_type = if !cpus.is_empty() {
|
||||
cpus[0].brand().to_string()
|
||||
} else {
|
||||
"Unknown CPU".to_string()
|
||||
};
|
||||
let cpu_cores = cpus.len() as i32;
|
||||
let ram_bytes = sys.total_memory() as f64;
|
||||
let gpu_type = Self::detect_gpu_name();
|
||||
let ip_address = local_ip_address::local_ip()?.to_string();
|
||||
|
||||
Ok(Self {
|
||||
cpu_type,
|
||||
cpu_cores,
|
||||
gpu_type,
|
||||
ram_size: ram_bytes,
|
||||
ip_address,
|
||||
})
|
||||
}
|
||||
|
||||
fn detect_gpu_name() -> String {
|
||||
// First try NVML (NVIDIA Management Library)
|
||||
if let Some(name) = Self::try_nvml_gpu_name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
// Fallback to OS-specific commands
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("lshw")
|
||||
.args(&["-C", "display"])
|
||||
.output()
|
||||
{
|
||||
if let Some(name) = String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.find(|l| l.contains("product:"))
|
||||
.map(|l| l.trim().replace("product:", "").trim().to_string())
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("wmic")
|
||||
.args(&["path", "win32_VideoController", "get", "name"])
|
||||
.output()
|
||||
{
|
||||
if let Some(name) = String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.nth(1)
|
||||
.map(|s| s.trim().to_string())
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fails
|
||||
"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())
|
||||
}
|
||||
}
|
@@ -0,0 +1,256 @@
|
||||
use anyhow::Result;
|
||||
use std::error::Error;
|
||||
//use std::result::Result;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CpuInfo {
|
||||
pub name: Option<String>,
|
||||
pub cores: Option<i32>,
|
||||
pub current_load: Option<f64>,
|
||||
pub current_temp: Option<f64>,
|
||||
}
|
||||
|
||||
pub async fn get_cpu_info() -> Result<CpuInfo, Box<dyn Error>> {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_cpu_all();
|
||||
|
||||
let cpus = sys.cpus();
|
||||
Ok(CpuInfo {
|
||||
name: Some(
|
||||
cpus.first()
|
||||
.map(|c| c.brand().to_string())
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
cores: Some(cpus.len() as i32),
|
||||
current_load: get_cpu_load(&mut sys).await.ok(),
|
||||
current_temp: get_cpu_temp().await.ok(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_cpu_load(sys: &mut System) -> Result<f64, Box<dyn Error>> {
|
||||
sys.refresh_cpu_all();
|
||||
tokio::task::yield_now().await; // Allow other tasks to run
|
||||
Ok(sys.global_cpu_usage() as f64)
|
||||
}
|
||||
|
||||
pub async fn get_cpu_temp() -> Result<f64, Box<dyn Error>> {
|
||||
println!("Attempting to get CPU temperature...");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
println!("");
|
||||
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::<f32>() {
|
||||
return Some(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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::<f32>() {
|
||||
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::<f32>() {
|
||||
return Some(temp / 1000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not find CPU temperature using sensors or sysfs"
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn failed(hr: winapi::shared::winerror::HRESULT) -> bool {
|
||||
hr < 0
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use com::runtime::init_runtime;
|
||||
use com::sys::CLSCTX_INPROC_SERVER;
|
||||
use widestring::U16CString;
|
||||
use winapi::shared::rpcdce::*;
|
||||
use winapi::shared::wtypes::VT_I4;
|
||||
use winapi::um::oaidl::VARIANT;
|
||||
use winapi::um::objidlbase::EOAC_NONE;
|
||||
use winapi::um::{combaseapi, wbemcli};
|
||||
|
||||
init_runtime().ok();
|
||||
|
||||
unsafe {
|
||||
use anyhow::Ok;
|
||||
|
||||
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 Err(("Failed to create WbemLocator").into());
|
||||
}
|
||||
|
||||
let mut services: *mut wbemcli::IWbemServices = std::ptr::null_mut();
|
||||
let namespace = U16CString::from_str("root\\cimv2").unwrap(); // Changed to more common namespace
|
||||
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);
|
||||
(*locator).Release();
|
||||
return Err(("Failed to connect to WMI").into());
|
||||
}
|
||||
|
||||
// Set security levels
|
||||
let hr = combaseapi::CoSetProxyBlanket(
|
||||
services as *mut _,
|
||||
RPC_C_AUTHN_WINNT,
|
||||
RPC_C_AUTHZ_NONE,
|
||||
std::ptr::null_mut(),
|
||||
RPC_C_AUTHN_LEVEL_CALL,
|
||||
RPC_C_IMP_LEVEL_IMPERSONATE,
|
||||
std::ptr::null_mut(),
|
||||
EOAC_NONE,
|
||||
);
|
||||
|
||||
if hr != 0 {
|
||||
eprintln!("Failed to set proxy blanket (HRESULT: {})", hr);
|
||||
(*services).Release();
|
||||
(*locator).Release();
|
||||
return Err(("Failed to set proxy blanket").into());
|
||||
}
|
||||
|
||||
// Try different temperature queries - some systems might have different WMI classes
|
||||
let queries = [
|
||||
"SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation",
|
||||
"SELECT * FROM MSAcpi_ThermalZoneTemperature",
|
||||
"SELECT * FROM Win32_TemperatureProbe",
|
||||
];
|
||||
|
||||
let mut result = None;
|
||||
|
||||
for query_str in queries.iter() {
|
||||
let query = U16CString::from_str(query_str).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 {
|
||||
continue; // Try next query if this one fails
|
||||
}
|
||||
|
||||
let mut obj: *mut wbemcli::IWbemClassObject = std::ptr::null_mut();
|
||||
let mut returned = 0;
|
||||
let hr = (*enumerator).Next(
|
||||
wbemcli::WBEM_INFINITE as i32, // Fixed: cast directly to i32
|
||||
1,
|
||||
&mut obj,
|
||||
&mut returned,
|
||||
);
|
||||
|
||||
if failed(hr) {
|
||||
eprintln!("Failed to enumerate WMI objects (HRESULT: {})", hr);
|
||||
(*enumerator).Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if returned == 0 {
|
||||
// No more items
|
||||
(*enumerator).Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if hr == 0 && returned > 0 {
|
||||
let mut variant = std::mem::zeroed::<VARIANT>();
|
||||
// Try different possible property names
|
||||
let property_names = ["CurrentTemperature", "Temperature", "CurrentReading"];
|
||||
|
||||
for prop in property_names.iter() {
|
||||
let hr = (*obj).Get(
|
||||
U16CString::from_str(prop).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;
|
||||
result = Some(temp_kelvin - 273.15); // Convert to Celsius
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(*obj).Release();
|
||||
(*enumerator).Release();
|
||||
if result.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !enumerator.is_null() {
|
||||
(*enumerator).Release();
|
||||
}
|
||||
}
|
||||
|
||||
(*services).Release();
|
||||
(*locator).Release();
|
||||
|
||||
return Ok(result.unwrap() as f64).map_err(|e| e.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||
{
|
||||
println!("CPU temperature retrieval not supported on this OS.");
|
||||
Err(anyhow::anyhow!("CPU temperature retrieval not supported on this OS").into())
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,113 @@
|
||||
use anyhow::Result;
|
||||
use sysinfo::{Components, Disks, System};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DiskInfo {
|
||||
pub total: Option<f64>,
|
||||
pub used: Option<f64>,
|
||||
pub free: Option<f64>,
|
||||
}
|
||||
|
||||
pub async fn get_disk_info() -> Result<DiskInfo> {
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
let mut total = 0;
|
||||
let mut used = 0;
|
||||
|
||||
for disk in disks.list() {
|
||||
if disk.total_space() > 100 * 1024 * 1024 {
|
||||
// > 100MB
|
||||
total += disk.total_space();
|
||||
used += disk.total_space() - disk.available_space();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DiskInfo {
|
||||
total: Some(total as f64),
|
||||
used: Some(used as f64),
|
||||
free: Some((total - used) as f64),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_disk_usage() -> (f64, f64, f64) {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_all();
|
||||
//sys.refresh_disks_list();
|
||||
|
||||
let mut total_size = 0u64;
|
||||
let mut total_used = 0u64;
|
||||
let mut count = 0;
|
||||
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
for disk in disks.list() {
|
||||
// Ignoriere CD-ROMs und kleine Systempartitionen
|
||||
println!(
|
||||
"Disk_Name: {:?}, Disk_Kind: {}, Total: {}, Available: {}",
|
||||
disk.name(),
|
||||
disk.kind(),
|
||||
disk.total_space(),
|
||||
disk.available_space()
|
||||
);
|
||||
if disk.total_space() > 100 * 1024 * 1024 {
|
||||
// > 100MB
|
||||
total_size += disk.total_space();
|
||||
total_used += disk.total_space() - disk.available_space();
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
let components = Components::new_with_refreshed_list();
|
||||
for component in &components {
|
||||
if let Some(temperature) = component.temperature() {
|
||||
println!(
|
||||
"Component_Label: {}, Temperature: {}°C",
|
||||
component.label(),
|
||||
temperature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Berechnungen
|
||||
let size_b = if count > 0 {
|
||||
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")]
|
||||
{
|
||||
use std::process::Command;
|
||||
if let Ok(output) = Command::new("df")
|
||||
.arg("-B1")
|
||||
.arg("--output=size,used")
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines().skip(1) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() == 2 {
|
||||
if let (Ok(size), Ok(used)) =
|
||||
(parts[0].parse::<u64>(), parts[1].parse::<u64>())
|
||||
{
|
||||
total_size += size;
|
||||
total_used += used;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
total_size as f64 // in Bytes
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
0.0
|
||||
}
|
||||
};
|
||||
|
||||
let usage = if total_size > 0 {
|
||||
(total_used as f64 / total_size as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
(size_b, usage, 0.0) // Disk-Temp bleibt 0.0 ohne spezielle Hardware
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
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>> {
|
||||
let nvml = Nvml::init()?;
|
||||
let device = nvml.device_by_index(0)?;
|
||||
|
||||
let (used, total) = get_gpu_vram_usage(&device)?;
|
||||
Ok(GpuInfo {
|
||||
name: device.name().ok(),
|
||||
current_load: get_gpu_load(&device).ok(),
|
||||
current_temp: get_gpu_temp(&device).ok(),
|
||||
vram_total: Some(total as f64),
|
||||
vram_used: Some(used as f64),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_gpu_load(device: &nvml_wrapper::Device) -> Result<f64, Box<dyn Error>> {
|
||||
Ok(device.utilization_rates().unwrap().gpu as f64)
|
||||
}
|
||||
|
||||
pub fn get_gpu_temp(device: &nvml_wrapper::Device) -> Result<f64, Box<dyn Error>> {
|
||||
Ok(device
|
||||
.temperature(nvml_wrapper::enum_wrappers::device::TemperatureSensor::Gpu)
|
||||
.unwrap() as f64)
|
||||
}
|
||||
|
||||
pub fn get_gpu_vram_usage(device: &nvml_wrapper::Device) -> Result<(f64, f64), Box<dyn Error>> {
|
||||
let mem_info = device.memory_info().unwrap();
|
||||
Ok((mem_info.used as f64, mem_info.total as f64))
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
use anyhow::Result;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryInfo {
|
||||
pub total: Option<f64>,
|
||||
pub used: Option<f64>,
|
||||
pub free: Option<f64>,
|
||||
}
|
||||
|
||||
pub async fn get_memory_info() -> Result<MemoryInfo> {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_memory();
|
||||
|
||||
Ok(MemoryInfo {
|
||||
total: Some(sys.total_memory() as f64),
|
||||
used: Some(sys.used_memory() as f64),
|
||||
free: Some(sys.free_memory() as f64),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_memory_usage(sys: &mut System) -> f64 {
|
||||
sys.refresh_memory();
|
||||
(sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0
|
||||
}
|
||||
|
35
WatcherAgent/src/hardware/mod.rs
Normal file
35
WatcherAgent/src/hardware/mod.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//use anyhow::Result;
|
||||
use std::error::Error;
|
||||
|
||||
mod cpu;
|
||||
mod disk;
|
||||
mod gpu;
|
||||
mod memory;
|
||||
mod network;
|
||||
|
||||
pub use cpu::get_cpu_info;
|
||||
pub use disk::get_disk_info;
|
||||
pub use gpu::get_gpu_info;
|
||||
pub use memory::get_memory_info;
|
||||
pub use network::get_network_info;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HardwareInfo {
|
||||
pub cpu: cpu::CpuInfo,
|
||||
pub gpu: gpu::GpuInfo,
|
||||
pub memory: memory::MemoryInfo,
|
||||
pub disk: disk::DiskInfo,
|
||||
pub network: network::NetworkInfo,
|
||||
}
|
||||
|
||||
impl HardwareInfo {
|
||||
pub async fn collect() -> anyhow::Result<Self, Box<dyn Error>> {
|
||||
Ok(Self {
|
||||
cpu: get_cpu_info().await?,
|
||||
gpu: get_gpu_info().await?,
|
||||
memory: get_memory_info().await?,
|
||||
disk: get_disk_info().await?,
|
||||
network: get_network_info().await?,
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,191 @@
|
||||
use std::error::Error;
|
||||
use std::result::Result;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkInfo {
|
||||
pub interfaces: Option<Vec<String>>,
|
||||
pub rx_bytes: Option<f64>,
|
||||
pub tx_bytes: Option<f64>,
|
||||
}
|
||||
|
||||
pub struct NetworkMonitor {
|
||||
prev_rx: u64,
|
||||
prev_tx: u64,
|
||||
last_update: Instant,
|
||||
}
|
||||
|
||||
impl NetworkMonitor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
prev_rx: 0,
|
||||
prev_tx: 0,
|
||||
last_update: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_usage(&mut self) -> Result<(f64, f64), Box<dyn Error>> {
|
||||
let (current_rx, current_tx) = get_network_bytes()?;
|
||||
let elapsed = self.last_update.elapsed().as_secs_f64();
|
||||
self.last_update = Instant::now();
|
||||
|
||||
let rx_rate = if current_rx >= self.prev_rx {
|
||||
(current_rx - self.prev_rx) as f64 / elapsed
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let tx_rate = if current_tx >= self.prev_tx {
|
||||
(current_tx - self.prev_tx) as f64 / elapsed
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
self.prev_rx = current_rx;
|
||||
self.prev_tx = current_tx;
|
||||
|
||||
Ok((rx_rate, tx_rate))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_network_info() -> Result<NetworkInfo, Box<dyn Error>> {
|
||||
let (rx, tx) = get_network_bytes()?;
|
||||
Ok(NetworkInfo {
|
||||
interfaces: Some(get_network_interfaces()),
|
||||
rx_bytes: Some(rx as f64),
|
||||
tx_bytes: Some(tx as f64),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_network_bytes() -> Result<(u64, u64), Box<dyn Error>> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::ptr::null_mut;
|
||||
use winapi::shared::ifmib::MIB_IFTABLE;
|
||||
use winapi::um::iphlpapi::GetIfTable;
|
||||
|
||||
unsafe {
|
||||
// Erste Abfrage zur Bestimmung der benötigten Puffergröße
|
||||
let mut buffer_size = 0u32;
|
||||
if GetIfTable(null_mut(), &mut buffer_size, 0)
|
||||
!= winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER
|
||||
{
|
||||
return Err(
|
||||
anyhow::anyhow!("Failed to get buffer size for network interfaces").into(),
|
||||
);
|
||||
}
|
||||
|
||||
// Puffer allozieren
|
||||
let mut buffer = vec![0u8; buffer_size as usize];
|
||||
let if_table = buffer.as_mut_ptr() as *mut MIB_IFTABLE;
|
||||
|
||||
// Tatsächliche Daten abrufen
|
||||
if GetIfTable(if_table, &mut buffer_size, 0) != 0 {
|
||||
return Err(anyhow::anyhow!("Failed to get network interface table").into());
|
||||
}
|
||||
|
||||
// Daten auswerten
|
||||
let mut rx_total = 0u64;
|
||||
let mut tx_total = 0u64;
|
||||
|
||||
for i in 0..(*if_table).dwNumEntries {
|
||||
let row = &*((*if_table).table.as_ptr().offset(i as isize));
|
||||
rx_total += row.dwInOctets as u64;
|
||||
tx_total += row.dwOutOctets as u64;
|
||||
}
|
||||
|
||||
if rx_total == 0 && tx_total == 0 {
|
||||
return Err(anyhow::anyhow!("No network data available").into());
|
||||
} else {
|
||||
Ok((rx_total, tx_total))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::fs;
|
||||
|
||||
let mut rx_total = 0u64;
|
||||
let mut tx_total = 0u64;
|
||||
if let Ok(dir) = fs::read_dir("/sys/class/net") {
|
||||
for entry in dir.flatten() {
|
||||
let iface = entry.file_name();
|
||||
let iface_name = iface.to_string_lossy();
|
||||
|
||||
// Ignoriere virtuelle Interfaces
|
||||
if !iface_name.starts_with("lo") && !iface_name.starts_with("virbr") {
|
||||
if let (Ok(rx), Ok(tx)) = (
|
||||
fs::read_to_string(entry.path().join("statistics/rx_bytes")),
|
||||
fs::read_to_string(entry.path().join("statistics/tx_bytes")),
|
||||
) {
|
||||
rx_total += rx.trim().parse::<u64>().unwrap_or(0);
|
||||
tx_total += tx.trim().parse::<u64>().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rx_total == 0 && tx_total == 0 {
|
||||
return Err(anyhow::anyhow!("No network data available").into());
|
||||
} else {
|
||||
return Ok((rx_total, tx_total));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
Err("No network data available for this OS".into())
|
||||
}
|
||||
|
||||
fn get_network_interfaces() -> Vec<String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
use std::ptr::null_mut;
|
||||
use winapi::shared::ifmib::MIB_IFTABLE;
|
||||
use winapi::um::iphlpapi::GetIfTable;
|
||||
|
||||
unsafe {
|
||||
let mut buffer_size = 0u32;
|
||||
if GetIfTable(null_mut(), &mut buffer_size, 0)
|
||||
!= winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER
|
||||
{
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut buffer = vec![0u8; buffer_size as usize];
|
||||
let if_table = buffer.as_mut_ptr() as *mut MIB_IFTABLE;
|
||||
|
||||
if GetIfTable(if_table, &mut buffer_size, 0) != 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
(0..(*if_table).dwNumEntries)
|
||||
.map(|i| {
|
||||
let row = &*((*if_table).table.as_ptr().offset(i as isize));
|
||||
let descr = CStr::from_ptr(row.bDescr.as_ptr() as *const i8)
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
descr.trim().to_string()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::fs;
|
||||
|
||||
let mut interfaces = vec![];
|
||||
if let Ok(dir) = fs::read_dir("/sys/class/net") {
|
||||
for entry in dir.flatten() {
|
||||
let iface = entry.file_name();
|
||||
interfaces.push(iface.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
interfaces
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
vec![]
|
||||
}
|
||||
|
@@ -1,507 +1,50 @@
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
use sysinfo::{Components, Disks, System};
|
||||
use tokio::time::Instant;
|
||||
|
||||
use crate::api;
|
||||
use crate::models::{MetricDto, NetworkState};
|
||||
use nvml_wrapper::Nvml;
|
||||
use crate::hardware::HardwareInfo;
|
||||
use crate::models::MetricDto;
|
||||
|
||||
pub struct Collector {
|
||||
sys: System,
|
||||
nvml: Option<Nvml>,
|
||||
server_id: i32,
|
||||
ip_address: String,
|
||||
network_state: NetworkState,
|
||||
}
|
||||
|
||||
impl NetworkState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
prev_rx: 0,
|
||||
prev_tx: 0,
|
||||
last_update: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Collector {
|
||||
pub fn new(server_id: i32, ip_address: String) -> Self {
|
||||
Self {
|
||||
sys: System::new(),
|
||||
nvml: Nvml::init().ok(),
|
||||
server_id,
|
||||
ip_address,
|
||||
network_state: NetworkState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self, base_url: &str) -> anyhow::Result<(), Box<dyn Error>> {
|
||||
loop {
|
||||
let metrics = self.collect();
|
||||
let metrics = self.collect().await?;
|
||||
api::send_metrics(base_url, &metrics).await?;
|
||||
tokio::time::sleep(Duration::from_secs(20)).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect(&mut self) -> MetricDto {
|
||||
self.sys.refresh_all();
|
||||
pub async fn collect(&mut self) -> Result<MetricDto, Box<dyn Error>> {
|
||||
let hardware = HardwareInfo::collect().await?;
|
||||
|
||||
// CPU - updated for sysinfo 0.35
|
||||
let cpu_load = self.sys.global_cpu_usage() as f64;
|
||||
let cpu_temp = get_cpu_temp().unwrap_or(0.0) as f64;
|
||||
|
||||
// RAM - updated for sysinfo 0.35
|
||||
let total_memory = self.sys.total_memory() as f64;
|
||||
let used_memory = self.sys.used_memory() as f64;
|
||||
let ram_load = (used_memory / total_memory) * 100.0;
|
||||
let ram_size = total_memory;
|
||||
|
||||
// Disk - updated for sysinfo 0.35
|
||||
let (disk_size, disk_usage, disk_temp) = get_disk_info();
|
||||
// GPU (NVIDIA)
|
||||
let (gpu_temp, gpu_load, vram_used, vram_total) = if let Some(nvml) = &self.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); // B
|
||||
let total = mem.map(|m| (m.total as f64)).unwrap_or(0.0); // B
|
||||
(temp, load, used, total)
|
||||
} else {
|
||||
(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
} else {
|
||||
(0.0, 0.0, 0.0, 0.0)
|
||||
};
|
||||
|
||||
// Network metrics
|
||||
let (current_rx, current_tx) = get_network_traffic().unwrap_or((0, 0));
|
||||
let elapsed_secs = self.network_state.last_update.elapsed().as_secs_f64();
|
||||
self.network_state.last_update = Instant::now();
|
||||
|
||||
// Calculate the difference since the last call
|
||||
let net_in = if current_rx >= self.network_state.prev_rx {
|
||||
((current_rx - self.network_state.prev_rx) as f64 * 8.0) / elapsed_secs
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let net_out = if current_tx >= self.network_state.prev_tx {
|
||||
((current_tx - self.network_state.prev_tx) as f64 * 8.0) / elapsed_secs
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// Store the current values for the next call
|
||||
self.network_state.prev_rx = current_rx;
|
||||
self.network_state.prev_tx = current_tx;
|
||||
|
||||
MetricDto {
|
||||
Ok(MetricDto {
|
||||
server_id: self.server_id,
|
||||
ip_address: self.ip_address.clone(),
|
||||
cpu_load,
|
||||
cpu_temp,
|
||||
gpu_load,
|
||||
gpu_temp,
|
||||
gpu_vram_size: vram_total,
|
||||
gpu_vram_usage: if vram_total > 0.0 {
|
||||
(vram_used / vram_total) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
ram_load,
|
||||
ram_size,
|
||||
disk_size,
|
||||
disk_usage: disk_usage,
|
||||
disk_temp: disk_temp, // not supported
|
||||
net_in,
|
||||
net_out,
|
||||
}
|
||||
cpu_load: hardware.cpu.current_load.unwrap_or_default(),
|
||||
cpu_temp: hardware.cpu.current_temp.unwrap_or_default(),
|
||||
gpu_load: hardware.gpu.current_load.unwrap_or_default(),
|
||||
gpu_temp: hardware.gpu.current_temp.unwrap_or_default(),
|
||||
gpu_vram_size: hardware.gpu.vram_total.unwrap_or_default(),
|
||||
gpu_vram_usage: hardware.gpu.vram_used.unwrap_or_default(),
|
||||
ram_load: hardware.memory.used.unwrap_or_default(),
|
||||
ram_size: hardware.memory.total.unwrap_or_default(),
|
||||
disk_size: hardware.disk.total.unwrap_or_default(),
|
||||
disk_usage: hardware.disk.used.unwrap_or_default(),
|
||||
disk_temp: 0.0, // not supported
|
||||
net_rx: hardware.network.rx_bytes.unwrap_or_default(),
|
||||
net_tx: hardware.network.tx_bytes.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cpu_temp() -> Option<f32> {
|
||||
println!("Attempting to get CPU temperature...");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
println!("");
|
||||
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::<f32>() {
|
||||
return Some(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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::<f32>() {
|
||||
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::<f32>() {
|
||||
return Some(temp / 1000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn failed(hr: winapi::shared::winerror::HRESULT) -> bool {
|
||||
hr < 0
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use com::runtime::init_runtime;
|
||||
use com::sys::CLSCTX_INPROC_SERVER;
|
||||
use widestring::U16CString;
|
||||
use winapi::shared::rpcdce::*;
|
||||
use winapi::shared::wtypes::VT_I4;
|
||||
use winapi::um::oaidl::VARIANT;
|
||||
use winapi::um::objidlbase::EOAC_NONE;
|
||||
use winapi::um::{combaseapi, wbemcli};
|
||||
|
||||
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\\cimv2").unwrap(); // Changed to more common namespace
|
||||
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);
|
||||
(*locator).Release();
|
||||
return None;
|
||||
}
|
||||
|
||||
// Set security levels
|
||||
let hr = combaseapi::CoSetProxyBlanket(
|
||||
services as *mut _,
|
||||
RPC_C_AUTHN_WINNT,
|
||||
RPC_C_AUTHZ_NONE,
|
||||
std::ptr::null_mut(),
|
||||
RPC_C_AUTHN_LEVEL_CALL,
|
||||
RPC_C_IMP_LEVEL_IMPERSONATE,
|
||||
std::ptr::null_mut(),
|
||||
EOAC_NONE,
|
||||
);
|
||||
|
||||
if hr != 0 {
|
||||
eprintln!("Failed to set proxy blanket (HRESULT: {})", hr);
|
||||
(*services).Release();
|
||||
(*locator).Release();
|
||||
return None;
|
||||
}
|
||||
|
||||
// Try different temperature queries - some systems might have different WMI classes
|
||||
let queries = [
|
||||
"SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation",
|
||||
"SELECT * FROM MSAcpi_ThermalZoneTemperature",
|
||||
"SELECT * FROM Win32_TemperatureProbe",
|
||||
];
|
||||
|
||||
let mut result = None;
|
||||
|
||||
for query_str in queries.iter() {
|
||||
let query = U16CString::from_str(query_str).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 {
|
||||
continue; // Try next query if this one fails
|
||||
}
|
||||
|
||||
let mut obj: *mut wbemcli::IWbemClassObject = std::ptr::null_mut();
|
||||
let mut returned = 0;
|
||||
let hr = (*enumerator).Next(
|
||||
wbemcli::WBEM_INFINITE as i32, // Fixed: cast directly to i32
|
||||
1,
|
||||
&mut obj,
|
||||
&mut returned,
|
||||
);
|
||||
|
||||
if failed(hr) {
|
||||
eprintln!("Failed to enumerate WMI objects (HRESULT: {})", hr);
|
||||
(*enumerator).Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if returned == 0 {
|
||||
// No more items
|
||||
(*enumerator).Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if hr == 0 && returned > 0 {
|
||||
let mut variant = std::mem::zeroed::<VARIANT>();
|
||||
// Try different possible property names
|
||||
let property_names = ["CurrentTemperature", "Temperature", "CurrentReading"];
|
||||
|
||||
for prop in property_names.iter() {
|
||||
let hr = (*obj).Get(
|
||||
U16CString::from_str(prop).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;
|
||||
result = Some(temp_kelvin - 273.15); // Convert to Celsius
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(*obj).Release();
|
||||
(*enumerator).Release();
|
||||
if result.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !enumerator.is_null() {
|
||||
(*enumerator).Release();
|
||||
}
|
||||
}
|
||||
|
||||
(*services).Release();
|
||||
(*locator).Release();
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||
{
|
||||
println!("CPU temperature retrieval not supported on this OS.");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_network_traffic() -> Option<(u64, u64)> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::ptr::null_mut;
|
||||
use winapi::shared::ifmib::MIB_IFTABLE;
|
||||
use winapi::um::iphlpapi::GetIfTable;
|
||||
|
||||
unsafe {
|
||||
// Erste Abfrage zur Bestimmung der benötigten Puffergröße
|
||||
let mut buffer_size = 0u32;
|
||||
if GetIfTable(null_mut(), &mut buffer_size, 0)
|
||||
!= winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// Puffer allozieren
|
||||
let mut buffer = vec![0u8; buffer_size as usize];
|
||||
let if_table = buffer.as_mut_ptr() as *mut MIB_IFTABLE;
|
||||
|
||||
// Tatsächliche Daten abrufen
|
||||
if GetIfTable(if_table, &mut buffer_size, 0) != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Daten auswerten
|
||||
let mut rx_total = 0u64;
|
||||
let mut tx_total = 0u64;
|
||||
|
||||
for i in 0..(*if_table).dwNumEntries {
|
||||
let row = &*((*if_table).table.as_ptr().offset(i as isize));
|
||||
rx_total += row.dwInOctets as u64;
|
||||
tx_total += row.dwOutOctets as u64;
|
||||
}
|
||||
|
||||
if rx_total == 0 && tx_total == 0 {
|
||||
return None;
|
||||
} else {
|
||||
return Some((rx_total, tx_total));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::fs;
|
||||
|
||||
let mut rx_total = 0u64;
|
||||
let mut tx_total = 0u64;
|
||||
if let Ok(dir) = fs::read_dir("/sys/class/net") {
|
||||
for entry in dir.flatten() {
|
||||
let iface = entry.file_name();
|
||||
let iface_name = iface.to_string_lossy();
|
||||
|
||||
// Ignoriere virtuelle Interfaces
|
||||
if !iface_name.starts_with("lo") && !iface_name.starts_with("virbr") {
|
||||
if let (Ok(rx), Ok(tx)) = (
|
||||
fs::read_to_string(entry.path().join("statistics/rx_bytes")),
|
||||
fs::read_to_string(entry.path().join("statistics/tx_bytes")),
|
||||
) {
|
||||
rx_total += rx.trim().parse::<u64>().unwrap_or(0);
|
||||
tx_total += tx.trim().parse::<u64>().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rx_total == 0 && tx_total == 0 {
|
||||
return None;
|
||||
} else {
|
||||
return Some((rx_total, tx_total));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_disk_info() -> (f64, f64, f64) {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_all();
|
||||
//sys.refresh_disks_list();
|
||||
|
||||
let mut total_size = 0u64;
|
||||
let mut total_used = 0u64;
|
||||
let mut count = 0;
|
||||
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
for disk in disks.list() {
|
||||
// Ignoriere CD-ROMs und kleine Systempartitionen
|
||||
println!(
|
||||
"Disk_Name: {:?}, Disk_Kind: {}, Total: {}, Available: {}",
|
||||
disk.name(),
|
||||
disk.kind(),
|
||||
disk.total_space(),
|
||||
disk.available_space()
|
||||
);
|
||||
if disk.total_space() > 100 * 1024 * 1024 {
|
||||
// > 100MB
|
||||
total_size += disk.total_space();
|
||||
total_used += disk.total_space() - disk.available_space();
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
let components = Components::new_with_refreshed_list();
|
||||
for component in &components {
|
||||
if let Some(temperature) = component.temperature() {
|
||||
println!(
|
||||
"Component_Label: {}, Temperature: {}°C",
|
||||
component.label(),
|
||||
temperature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Berechnungen
|
||||
let size_b = if count > 0 {
|
||||
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")]
|
||||
{
|
||||
use std::process::Command;
|
||||
if let Ok(output) = Command::new("df")
|
||||
.arg("-B1")
|
||||
.arg("--output=size,used")
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines().skip(1) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() == 2 {
|
||||
if let (Ok(size), Ok(used)) =
|
||||
(parts[0].parse::<u64>(), parts[1].parse::<u64>())
|
||||
{
|
||||
total_size += size;
|
||||
total_used += used;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
total_size as f64 // in Bytes
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
0.0
|
||||
}
|
||||
};
|
||||
|
||||
let usage = if total_size > 0 {
|
||||
(total_used as f64 / total_size as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
(size_b, usage, 0.0) // Disk-Temp bleibt 0.0 ohne spezielle Hardware
|
||||
}
|
||||
|
@@ -47,9 +47,9 @@ pub struct MetricDto {
|
||||
#[serde(rename = "disk_Temp")]
|
||||
pub disk_temp: f64,
|
||||
#[serde(rename = "net_In")]
|
||||
pub net_in: f64,
|
||||
pub net_rx: f64,
|
||||
#[serde(rename = "net_Out")]
|
||||
pub net_out: f64,
|
||||
pub net_tx: f64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -73,9 +73,3 @@ pub struct HardwareDto {
|
||||
pub ram_size: f64,
|
||||
pub ip_address: String,
|
||||
}
|
||||
|
||||
pub struct NetworkState {
|
||||
pub prev_rx: u64,
|
||||
pub prev_tx: u64,
|
||||
pub last_update: Instant,
|
||||
}
|
||||
|
Reference in New Issue
Block a user