added plattform specific cputemp and network load detection
This commit is contained in:
@@ -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]
|
||||
|
Reference in New Issue
Block a user