use crate::core_model::SystemHealth;
use std::fs;
impl SystemHealth {
pub fn collect(device_id: String) -> Self {
Self {
unix_time_ms: get_unix_time_ms(),
device_id,
cpu_pct: get_cpu_pct(),
mem_pct: get_mem_pct(),
disk_pct: get_disk_pct("/"),
cpu_temp: get_cpu_temp(),
signal_strength: get_wifi_rssi(),
signal_quality: get_wifi_quality(),
}
}
}
fn get_unix_time_ms() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as i64)
.unwrap_or(0)
}
fn get_cpu_pct() -> f32 {
let loadavg = fs::read_to_string("/proc/loadavg").unwrap_or_default();
let load: f32 = loadavg
.split_whitespace()
.next()
.and_then(|s| s.parse().ok())
.unwrap_or(0.0);
let cpus = std::thread::available_parallelism()
.map(|n| n.get() as f32)
.unwrap_or(1.0);
(load / cpus * 100.0).min(100.0)
}
fn get_mem_pct() -> f32 {
let content = fs::read_to_string("/proc/meminfo").unwrap_or_default();
let mut total = 0u64;
let mut available = 0u64;
for line in content.lines() {
let mut parts = line.split_whitespace();
match parts.next() {
Some("MemTotal:") => total = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0),
Some("MemAvailable:") => {
available = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0)
}
_ => {}
}
}
if total > 0 {
(1.0 - available as f32 / total as f32) * 100.0
} else {
0.0
}
}
fn get_disk_pct(path: &str) -> f32 {
use std::ffi::CString;
use std::mem::MaybeUninit;
let path = match CString::new(path) {
Ok(p) => p,
Err(_) => return 0.0, };
let mut stat = MaybeUninit::<libc::statvfs>::uninit();
unsafe {
if libc::statvfs(path.as_ptr(), stat.as_mut_ptr()) == 0 {
let stat = stat.assume_init();
let total = stat.f_blocks as f64 * stat.f_frsize as f64;
let free = stat.f_bfree as f64 * stat.f_frsize as f64;
if total > 0.0 {
return ((1.0 - free / total) * 100.0) as f32;
}
}
}
0.0
}
fn get_cpu_temp() -> f32 {
let paths = [
"/sys/class/thermal/thermal_zone0/temp",
"/sys/class/hwmon/hwmon0/temp1_input",
"/sys/class/hwmon/hwmon1/temp1_input",
"/sys/class/hwmon/hwmon2/temp1_input",
];
for path in paths {
if let Ok(content) = fs::read_to_string(path) {
if let Ok(millidegrees) = content.trim().parse::<i32>() {
return millidegrees as f32 / 1000.0;
}
}
}
if let Ok(entries) = fs::read_dir("/sys/class/hwmon") {
for entry in entries.flatten() {
let name_path = entry.path().join("name");
if let Ok(name) = fs::read_to_string(&name_path) {
let name = name.trim();
if name == "coretemp" || name == "k10temp" || name == "cpu_thermal" {
let temp_path = entry.path().join("temp1_input");
if let Ok(temp) = fs::read_to_string(&temp_path) {
if let Ok(millidegrees) = temp.trim().parse::<i32>() {
return millidegrees as f32 / 1000.0;
}
}
}
}
}
}
0.0 }
fn get_wifi_rssi() -> f32 {
let content = fs::read_to_string("/proc/net/wireless").unwrap_or_default();
for line in content.lines().skip(2) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 4 {
let signal = parts[3].trim_end_matches('.');
if let Ok(rssi) = signal.parse::<f32>() {
return rssi; }
}
}
0.0
}
fn get_wifi_quality() -> f32 {
let content = fs::read_to_string("/proc/net/wireless").unwrap_or_default();
for line in content.lines().skip(2) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 {
let quality = parts[2].trim_end_matches('.');
if let Ok(q) = quality.parse::<f32>() {
return (q / 70.0 * 100.0).min(100.0);
}
}
}
0.0
}