added plattform specific cputemp and network load detection

This commit is contained in:
2025-07-31 09:31:11 +02:00
parent a96b3ce832
commit 114009d34d

View File

@@ -323,17 +323,62 @@ impl MetricsCollector {
// Disk
let disk = self.sys.disks().first();
let (disk_size, disk_used) = if let Some(d) = disk {
let total = d.total_space();
let available = d.available_space();
(
(total as f64) / 1024.0 / 1024.0 / 1024.0, // Convert to GB
(total - available) as f64 / total as f64 * 100.0,
)
} else {
(0.0, 0.0)
};
// In collect_metrics():
let (disk_size, disk_usage, disk_temp) = {
let mut total_size = 0u64;
let mut total_used = 0u64;
let mut temp = 0.0;
let mut count = 0;
for disk in self.sys.disks() {
total_size += disk.total_space();
total_used += disk.total_space() - disk.available_space();
count += 1;
}
// Disk temperature (Linux only)
#[cfg(target_os = "linux")]
{
if let Ok(dir) = fs::read_dir("/sys/block") {
for entry in dir.flatten() {
if let Some(disk_name) = entry.file_name().to_str() {
if disk_name.starts_with("sd") || disk_name.starts_with("nvme") {
let temp_path = format!(
"/sys/block/{}/device/hwmon/hwmon*/temp1_input",
disk_name
);
if let Ok(paths) = glob::glob(&temp_path) {
for path in paths.flatten() {
if let Ok(content) = fs::read_to_string(path) {
if let Ok(t) = content.trim().parse::<f32>() {
temp += t / 1000.0; // Convert millidegrees
break;
}
}
}
}
}
}
}
}
}
let size_gb = if count > 0 {
(total_size as f64) / 1024.0 / 1024.0 / 1024.0
} else {
0.0
};
let usage = if total_size > 0 {
(total_used as f64 / total_size as f64) * 100.0
} else {
0.0
};
let avg_temp = if count > 0 { temp / count as f64 } else { 0.0 };
(size_gb, usage, avg_temp)
};
// 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) {
@@ -381,7 +426,7 @@ impl MetricsCollector {
ram_load,
ram_size,
disk_size,
disk_usage: disk_used,
disk_usage: disk_usage,
disk_temp: 0.0, // not supported
net_in: net_in_bits,
net_out: net_out_bits,
@@ -390,39 +435,103 @@ impl MetricsCollector {
}
fn get_cpu_temp() -> Option<f32> {
let output = Command::new("sensors").output().ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if line.to_lowercase().contains("package id") || line.to_lowercase().contains("cpu temp") {
if let Some(temp_str) = line.split_whitespace().find(|s| s.contains("°C")) {
let number: String = temp_str
.chars()
.filter(|c| c.is_digit(10) || *c == '.')
.collect();
return number.parse::<f32>().ok();
#[cfg(target_os = "linux")]
{
// Linux: sensors command or sysfs
if let Ok(output) = Command::new("sensors").output() {
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if line.to_lowercase().contains("package id")
|| line.to_lowercase().contains("cpu temp")
{
if let Some(temp_str) = line.split_whitespace().find(|s| s.contains("°C")) {
let number: String = temp_str
.chars()
.filter(|c| c.is_ascii_digit() || *c == '.')
.collect();
return number.parse::<f32>().ok();
}
}
}
}
// Fallback to sysfs (common path for 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); // Convert millidegrees to degrees
}
}
}
#[cfg(target_os = "windows")]
{
// Windows: WMI query
let output = Command::new("wmic")
.args(&["cpu", "get", "Temperature", "/Value"])
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if line.starts_with("Temperature=") {
if let Ok(temp) = line.replace("Temperature=", "").trim().parse::<f32>() {
return Some(temp); // Returns in Celsius
}
}
}
}
None
}
fn get_network_traffic() -> Option<(u64, u64)> {
let content = fs::read_to_string("/proc/net/dev").ok()?;
let mut rx_total = 0u64;
let mut tx_total = 0u64;
#[cfg(target_os = "linux")]
{
let content = fs::read_to_string("/proc/net/dev").ok()?;
let mut rx_total = 0u64;
let mut tx_total = 0u64;
for line in content.lines().skip(2) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 17 {
continue;
for line in content.lines().skip(2) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 10 || parts[0].ends_with(":") {
continue;
}
if parts[0].contains("lo:") {
continue;
}
rx_total += parts[1].parse::<u64>().unwrap_or(0);
tx_total += parts[9].parse::<u64>().unwrap_or(0);
}
if parts[0].contains("lo:") {
continue;
}
rx_total += parts[1].parse::<u64>().ok()?;
tx_total += parts[9].parse::<u64>().ok()?;
Some((rx_total, tx_total))
}
#[cfg(target_os = "windows")]
{
use std::process::Stdio;
let output = Command::new("netstat")
.args(&["-e"])
.stdout(Stdio::piped())
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut lines = stdout.lines();
// Find the line with statistics
while let Some(line) = lines.next() {
if line.contains("Bytes") {
if let Some(stats_line) = lines.next() {
let parts: Vec<&str> = stats_line.split_whitespace().collect();
if parts.len() >= 2 {
let rx = parts[0].parse::<u64>().unwrap_or(0);
let tx = parts[1].parse::<u64>().unwrap_or(0);
return Some((rx, tx));
}
}
}
}
None
}
Some((rx_total, tx_total))
}
#[tokio::main]