//! Shared agent state: sysinfo handles and hot JSON cache. #[cfg(target_os = "linux")] use std::collections::HashMap; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; use sysinfo::{Components, Disks, Networks, System}; use tokio::sync::Mutex; use std::time::{Duration, Instant}; pub type SharedSystem = Arc>; pub type SharedComponents = Arc>; pub type SharedDisks = Arc>; pub type SharedNetworks = Arc>; #[cfg(target_os = "linux")] #[derive(Default)] pub struct ProcCpuTracker { pub last_total: u64, pub last_per_pid: HashMap, } #[derive(Clone)] pub struct AppState { pub sys: SharedSystem, pub components: SharedComponents, pub disks: SharedDisks, pub networks: SharedNetworks, pub hostname: String, // For correct per-process CPU% using /proc deltas (Linux only path uses this tracker) #[cfg(target_os = "linux")] pub proc_cpu: Arc>, // Connection tracking (to allow future idle sleeps if desired) pub client_count: Arc, pub auth_token: Option, // GPU negative cache (probe once). gpu_checked=true after first attempt; gpu_present reflects result. pub gpu_checked: Arc, pub gpu_present: Arc, // Lightweight on-demand caches (TTL based) to cap CPU under bursty polling. pub cache_metrics: Arc>>, pub cache_disks: Arc>>>, pub cache_processes: Arc>>, } #[derive(Clone, Debug)] pub struct CacheEntry { pub at: Option, pub value: Option, } impl CacheEntry { pub fn new() -> Self { Self { at: None, value: None } } pub fn is_fresh(&self, ttl: Duration) -> bool { self.at.is_some_and(|t| t.elapsed() < ttl) && self.value.is_some() } pub fn set(&mut self, v: T) { self.value = Some(v); self.at = Some(Instant::now()); } pub fn take_clone(&self) -> Option where T: Clone, { self.value.clone() } } impl AppState { pub fn new() -> Self { let sys = System::new(); let components = Components::new_with_refreshed_list(); let disks = Disks::new_with_refreshed_list(); let networks = Networks::new_with_refreshed_list(); Self { sys: Arc::new(Mutex::new(sys)), components: Arc::new(Mutex::new(components)), disks: Arc::new(Mutex::new(disks)), networks: Arc::new(Mutex::new(networks)), hostname: System::host_name().unwrap_or_else(|| "unknown".into()), #[cfg(target_os = "linux")] proc_cpu: Arc::new(Mutex::new(ProcCpuTracker::default())), client_count: Arc::new(AtomicUsize::new(0)), auth_token: std::env::var("SOCKTOP_TOKEN") .ok() .filter(|s| !s.is_empty()), gpu_checked: Arc::new(AtomicBool::new(false)), gpu_present: Arc::new(AtomicBool::new(false)), cache_metrics: Arc::new(Mutex::new(CacheEntry::new())), cache_disks: Arc::new(Mutex::new(CacheEntry::new())), cache_processes: Arc::new(Mutex::new(CacheEntry::new())), } } }