diff --git a/src/canvas/mod.rs b/src/canvas/mod.rs new file mode 100644 index 00000000..fb5d23eb --- /dev/null +++ b/src/canvas/mod.rs @@ -0,0 +1,6 @@ +use tui::{ + layout::{Constraint, Direction, Layout}, + style::{Color, Style}, + widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget}, + Terminal, +}; diff --git a/src/main.rs b/src/main.rs index 1efe1256..a2a3e6bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ extern crate log; enum Event { Input(I), - Update(widgets::Data), + Update(Box), } #[tokio::main] @@ -58,8 +58,8 @@ async fn main() -> Result<(), io::Error> { let tx = tx.clone(); loop { futures::executor::block_on(data_state.update_data()); // TODO: Fix - tx.send(Event::Update(data_state.data.clone())).unwrap(); - thread::sleep(Duration::from_millis(update_rate_in_milliseconds - 1000)); + tx.send(Event::Update(Box::from(data_state.data.clone()))).unwrap(); + thread::sleep(Duration::from_millis(update_rate_in_milliseconds)); } }); } @@ -90,7 +90,7 @@ async fn main() -> Result<(), io::Error> { } Event::Update(data) => { try_debug(&log, "Update event fired!"); - app_data = data; + app_data = *data; widgets::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse); try_debug(&log, "Update event complete."); } @@ -121,7 +121,7 @@ fn draw_data(terminal : &mut Terminal, app_data : vec![ disk.name.to_string(), disk.mount_point.to_string(), - format!("{:.2}%", disk.used_space as f64 / disk.total_space as f64 * 100_f64), + format!("{:.1}%", disk.used_space as f64 / disk.total_space as f64 * 100_f64), (disk.free_space / 1024).to_string() + "GB", (disk.total_space / 1024).to_string() + "GB", ] @@ -135,14 +135,31 @@ fn draw_data(terminal : &mut Terminal, app_data : vec![ process.pid.to_string(), process.command.to_string(), - format!("{:.2}%", process.cpu_usage_percent), - format!("{:.2}%", process.mem_usage_percent), + format!("{:.1}%", process.cpu_usage_percent), + format!( + "{:.1}%", + if let Some(mem_usage) = process.mem_usage_percent { + mem_usage + } + else if let Some(mem_usage_in_mb) = process.mem_usage_mb { + if let Some(mem_data) = app_data.memory.last() { + mem_usage_in_mb as f64 / mem_data.mem_total_in_mb as f64 * 100_f64 + } + else { + 0_f64 + } + } + else { + 0_f64 + } + ), ] .into_iter(), Style::default().fg(Color::LightGreen), ) }); + // TODO: Convert this into a separate func! terminal.draw(|mut f| { let vertical_chunks = Layout::default() .direction(Direction::Vertical) @@ -173,22 +190,17 @@ fn draw_data(terminal : &mut Terminal, app_data : // Set up blocks and their components // CPU usage graph + let x_axis : Axis = Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 10.0]); + let y_axis : Axis = Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 10.0]); Chart::default() .block(Block::default().title("CPU Usage").borders(Borders::ALL)) - .x_axis(Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 10.0]).labels(&["0.0", "10.0"])) - .y_axis(Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 10.0]).labels(&["0.0", "10.0"])) - .datasets(&[ - Dataset::default() - .name("data1") - .marker(Marker::Dot) - .style(Style::default().fg(Color::Cyan)) - .data(&[(0.0, 5.0), (1.0, 6.0), (1.5, 6.434)]), - Dataset::default() - .name("data2") - .marker(Marker::Braille) - .style(Style::default().fg(Color::Magenta)) - .data(&[(4.0, 5.0), (5.0, 8.0), (7.66, 13.5)]), - ]) + .x_axis(x_axis) + .y_axis(y_axis) + .datasets(&[Dataset::default() + .name("data1") + .marker(Marker::Dot) + .style(Style::default().fg(Color::Cyan)) + .data(&[(0.0, 5.0), (1.0, 6.0), (1.5, 6.434)])]) .render(&mut f, top_chunks[0]); //Memory usage graph diff --git a/src/widgets/cpu.rs b/src/widgets/cpu.rs index b8a629e1..73a313f9 100644 --- a/src/widgets/cpu.rs +++ b/src/widgets/cpu.rs @@ -1,9 +1,11 @@ +use std::time::Instant; use sysinfo::{ProcessorExt, System, SystemExt}; -#[derive(Clone, Default)] +#[derive(Clone)] pub struct CPUData { pub cpu_name : Box, pub cpu_usage : u32, + pub instant : Instant, } pub fn get_cpu_data_list(sys : &System) -> Result, heim::Error> { @@ -14,6 +16,7 @@ pub fn get_cpu_data_list(sys : &System) -> Result, heim::Error> { cpu_vec.push(CPUData { cpu_name : Box::from(cpu.get_name()), cpu_usage : (cpu.get_cpu_usage() * 100_f32).ceil() as u32, + instant : Instant::now(), }) } diff --git a/src/widgets/disks.rs b/src/widgets/disks.rs index 0d8f26c5..4eb7133b 100644 --- a/src/widgets/disks.rs +++ b/src/widgets/disks.rs @@ -1,4 +1,5 @@ use heim_common::prelude::StreamExt; +use std::time::Instant; #[derive(Clone, Default)] pub struct DiskData { @@ -9,11 +10,12 @@ pub struct DiskData { pub total_space : u64, } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct IOData { pub mount_point : Box, pub read_bytes : u64, pub write_bytes : u64, + pub instant : Instant, } pub async fn get_io_usage_list(get_physical : bool) -> Result, heim::Error> { @@ -26,6 +28,7 @@ pub async fn get_io_usage_list(get_physical : bool) -> Result, heim: mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")), read_bytes : io.read_bytes().get::(), write_bytes : io.write_bytes().get::(), + instant : Instant::now(), }) } } @@ -37,6 +40,7 @@ pub async fn get_io_usage_list(get_physical : bool) -> Result, heim: mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")), read_bytes : io.read_bytes().get::(), write_bytes : io.write_bytes().get::(), + instant : Instant::now(), }) } } diff --git a/src/widgets/mem.rs b/src/widgets/mem.rs index 3fa03ef9..c7c06f8d 100644 --- a/src/widgets/mem.rs +++ b/src/widgets/mem.rs @@ -1,9 +1,11 @@ use heim_common::units::information; +use std::time::Instant; -#[derive(Clone, Default)] +#[derive(Clone)] pub struct MemData { pub mem_total_in_mb : u64, pub mem_used_in_mb : u64, + pub instant : Instant, } pub async fn get_mem_data_list() -> Result { @@ -12,6 +14,7 @@ pub async fn get_mem_data_list() -> Result { Ok(MemData { mem_total_in_mb : memory.total().get::(), mem_used_in_mb : memory.total().get::() - memory.available().get::(), + instant : Instant::now(), }) } @@ -21,5 +24,6 @@ pub async fn get_swap_data_list() -> Result { Ok(MemData { mem_total_in_mb : memory.total().get::(), mem_used_in_mb : memory.used().get::(), + instant : Instant::now(), }) } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index f1332a71..9721788a 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -22,17 +22,23 @@ fn set_if_valid(result : &Result, value_t } } +fn push_if_valid(result : &Result, vector_to_push : &mut Vec) { + if let Ok(result) = result { + vector_to_push.push(result.clone()); + } +} + #[derive(Default, Clone)] pub struct Data { - pub list_of_cpu_packages : Vec, - pub list_of_io : Vec, - pub list_of_physical_io : Vec, - pub memory : mem::MemData, - pub swap : mem::MemData, + pub list_of_cpu_packages : Vec>, + pub list_of_io : Vec>, + pub list_of_physical_io : Vec>, + pub memory : Vec, + pub swap : Vec, pub list_of_temperature : Vec, - pub network : network::NetworkData, - pub list_of_processes : Vec, - pub list_of_disks : Vec, + pub network : Vec, + pub list_of_processes : Vec, // Only need to keep a list of processes... + pub list_of_disks : Vec, // Only need to keep a list of disks and their data } pub struct DataState { @@ -56,17 +62,17 @@ impl DataState { self.sys.refresh_network(); // What we want to do: For timed data, if there is an error, just do not add. For other data, just don't update! - set_if_valid(&network::get_network_data(&self.sys), &mut self.data.network); - set_if_valid(&cpu::get_cpu_data_list(&self.sys), &mut self.data.list_of_cpu_packages); + push_if_valid(&network::get_network_data(&self.sys), &mut self.data.network); + push_if_valid(&cpu::get_cpu_data_list(&self.sys), &mut self.data.list_of_cpu_packages); // TODO: We can convert this to a multi-threaded task... - set_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory); - set_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap); - set_if_valid(&processes::get_sorted_processes_list(self.data.memory.mem_total_in_mb).await, &mut self.data.list_of_processes); + push_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory); + push_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap); + set_if_valid(&processes::get_sorted_processes_list().await, &mut self.data.list_of_processes); set_if_valid(&disks::get_disk_usage_list().await, &mut self.data.list_of_disks); - set_if_valid(&disks::get_io_usage_list(false).await, &mut self.data.list_of_io); - set_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io); + push_if_valid(&disks::get_io_usage_list(false).await, &mut self.data.list_of_io); + push_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io); set_if_valid(&temperature::get_temperature_data().await, &mut self.data.list_of_temperature); debug!("End updating..."); } diff --git a/src/widgets/processes.rs b/src/widgets/processes.rs index a3dee05d..37ef11f4 100644 --- a/src/widgets/processes.rs +++ b/src/widgets/processes.rs @@ -18,7 +18,8 @@ pub enum ProcessSorting { pub struct ProcessData { pub pid : u32, pub cpu_usage_percent : f64, - pub mem_usage_percent : f64, + pub mem_usage_percent : Option, + pub mem_usage_mb : Option, pub command : String, } @@ -74,9 +75,11 @@ fn get_cpu_use_val() -> std::io::Result { Ok(val[0].parse::().unwrap_or(0_f64) + val[1].parse::().unwrap_or(0_f64) + val[2].parse::().unwrap_or(0_f64) + val[3].parse::().unwrap_or(0_f64)) } -async fn linux_cpu_usage(pid : u32, before_cpu_val : f64) -> std::io::Result { +async fn linux_cpu_usage(pid : u32) -> std::io::Result { // Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556 let before_proc_val = get_process_cpu_stats(pid)?; + let before_cpu_val = get_cpu_use_val()?; + futures_timer::Delay::new(std::time::Duration::from_millis(1000)).await.unwrap(); let after_proc_val = get_process_cpu_stats(pid)?; let after_cpu_val = get_cpu_use_val()?; @@ -85,31 +88,30 @@ async fn linux_cpu_usage(pid : u32, before_cpu_val : f64) -> std::io::Result std::io::Result { - let before_cpu_val = get_cpu_use_val()?; - - debug!("Process: |{}|", process); if process.trim().to_string().is_empty() { return Ok(ProcessData { pid : 0, command : "".to_string(), - mem_usage_percent : 0_f64, + mem_usage_percent : None, + mem_usage_mb : None, cpu_usage_percent : 0_f64, }); } let pid = (&process[..11]).trim().to_string().parse::().unwrap_or(0); let command = (&process[11..61]).trim().to_string(); - let mem_usage_percent = (&process[62..]).trim().to_string().parse::().unwrap_or(0_f64); + let mem_usage_percent = Some((&process[62..]).trim().to_string().parse::().unwrap_or(0_f64)); Ok(ProcessData { pid, command, mem_usage_percent, - cpu_usage_percent : linux_cpu_usage(pid, before_cpu_val).await?, + mem_usage_mb : None, + cpu_usage_percent : linux_cpu_usage(pid).await?, }) } -pub async fn get_sorted_processes_list(total_mem : u64) -> Result, heim::Error> { +pub async fn get_sorted_processes_list() -> Result, heim::Error> { let mut process_vector : Vec = Vec::new(); if cfg!(target_os = "linux") { @@ -141,7 +143,8 @@ pub async fn get_sorted_processes_list(total_mem : u64) -> Result()), - mem_usage_percent : mem_measurement.rss().get::() as f64 / total_mem as f64 * 100_f64, + mem_usage_percent : None, + mem_usage_mb : Some(mem_measurement.rss().get::()), }); } } diff --git a/src/widgets/temperature.rs b/src/widgets/temperature.rs index f564647f..d1c49c1a 100644 --- a/src/widgets/temperature.rs +++ b/src/widgets/temperature.rs @@ -1,6 +1,6 @@ use heim_common::{prelude::StreamExt, units::thermodynamic_temperature}; -#[derive(Clone, Default)] +#[derive(Clone)] pub struct TempData { pub component_name : Box, pub temperature : f32,