use crate::LOGGER; use crate::backend::{BackendThread, Registrator}; use all_smi::AllSmi; use battery::State; use battery::units::Time; use battery::units::power::watt; use battery::units::ratio::percent; use battery::units::time::millisecond; use crossbeam_channel::{Receiver, Sender}; use jmb_logger_rs::*; use std::thread; use std::time::Duration; use sysinfo::{Disks, System}; const POLL_INTERVAL: u64 = 10_000; #[derive(Clone, Debug)] pub struct Stats { pub cpu: Option, pub ram: Option, pub disks: Vec<(String, u8)>, pub gpus: Vec<(String, u8)>, pub battery_charge: u8, pub battery_state: State, pub battery_draw: f32, pub battery_time: u64, } pub struct StatsBackend { subscribers: Vec>, system: Option, disks: Option, } impl BackendThread for StatsBackend { fn subscribe(registrator: Registrator) -> (Receiver, ()) { let (tx, rx) = crossbeam_channel::unbounded::(); let _ = registrator.send(tx); (rx, ()) } fn subscribers(&self) -> &Vec> { &self.subscribers } fn init(registration_rx: Receiver>) { thread::spawn(move || { let mut instance = Self { subscribers: Vec::new(), system: System::new().ok(), disks: Disks::new().ok(), }; let manager = battery::Manager::new(); let smi = AllSmi::new(); loop { while let Ok(tx) = registration_rx.try_recv() { trace!(&LOGGER, "Added Stats Subscriber: {tx:?}"); instance.subscribers.push(tx); } let mut cpu = None; let mut ram = None; if let Some(system) = instance.system.as_mut() { system.refresh_cpu_usage(); system.refresh_memory(); cpu = Some(system.global_cpu_usage() as u8); ram = Some((system.used_memory() * 100 / system.total_memory()) as u8); } let mut disk_stats = Vec::new(); if let Some(disks) = instance.disks.as_mut() { disks.refresh(true); disk_stats = disks .iter() .map(|disk| { let mount_point = disk.mount_point().display().to_string(); let used_space = disk.total_space() - disk.available_space(); let percentage = (used_space * 100 / disk.total_space()) as u8; (mount_point, percentage) }) .collect(); } let mut gpus: Vec<(String, u8)> = Vec::new(); if let Ok(smi) = &smi { gpus = smi .get_gpu_info() .iter() .map(|info| (info.name.clone(), info.utilization as u8)) .collect(); } let mut battery_charge: u8 = 255; // Use > 100 as invalid value let mut battery_state = State::Unknown; let mut battery_draw: f32 = 0.0; let mut battery_time: u64 = 0; if let Ok(manager) = &manager && let Ok(mut batteries) = manager.batteries() { match batteries.next() { Some(Ok(battery)) => { battery_charge = battery.state_of_charge().get::().round() as u8; battery_state = battery.state(); battery_draw = battery.energy_rate().get::(); battery_time = if battery_state == State::Charging { battery .time_to_full() .unwrap_or(Time::new::(0.0)) .get::() as u64 } else if battery_state == State::Discharging { battery .time_to_empty() .unwrap_or(Time::new::(0.0)) .get::() as u64 } else { 0 }; } Some(Err(e)) => error!(&LOGGER, "Error accessing battery: {e:?}"), None => {} } } let msg = Stats { cpu, ram, disks: disk_stats, gpus, battery_charge, battery_state, battery_draw, battery_time, }; instance.publish(msg); thread::sleep(Duration::from_millis(POLL_INTERVAL)); } }); } }