From 1a4a261db61e3b9199261a9bc62dfa00507d8f38 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sun, 15 Sep 2019 14:16:18 -0400 Subject: [PATCH] Added scrolling event, need to implement across processes now. --- TODO.md | 34 ++- src/app.rs | 62 ++++- src/app/data_collection/processes.rs | 8 +- src/canvas.rs | 22 +- src/constants.rs | 3 + src/convert_data.rs | 260 ++++++++++++++++++++ src/main.rs | 346 +++++---------------------- 7 files changed, 429 insertions(+), 306 deletions(-) create mode 100644 src/constants.rs create mode 100644 src/convert_data.rs diff --git a/TODO.md b/TODO.md index c0e2a653..0db24577 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,7 @@ # To-Do List +## Pre-release (bare minimum) + * ~~Get each function working as a POC~~ * ~~Separate each component for readability, finalize project structure~~ @@ -8,24 +10,36 @@ * ~~Write tui display, charting~~ -* Scaling in and out +* ~~FIX PROCESSES AHHHHHH~~ -* Add custom error because it's really messy - -* Remove any ``unwrap()``. - -* Scrolling event +* Scrolling in at least processes * Keybindings -~~* FIX PROCESSES AHHHHHH~~ +## After making public + +* Scaling in and out (zoom), may need to show zoom levels + +* It would be maybe a good idea to see if we can run the process calculation across ALL cpus...? + +* ~~Add custom error because it's really messy~~ Done, but need to implement across rest of app! + +* Remove any ``unwrap()``, ensure no crashing! + +* Scrolling event in lists + +* Switching between panels + +* Truncate columns if needed for tables * Refactor everything because it's a mess -* Test for Windows support, mac support +* Test for Windows support, mac support, other. May be doable, depends on sysinfo and how much I know about other OSes probably. * Efficiency!!! -* Potentially process managing? Depends on the libraries... +* Filtering in processes (that is, allow searching) -* Filtering in processes (ie: search) +* Help screen + +* Potentially process managing? Depends on the libraries... diff --git a/src/app.rs b/src/app.rs index 743b1f2c..2461a188 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,15 +1,30 @@ pub mod data_collection; use data_collection::{processes, temperature}; +#[allow(dead_code)] +// Probably only use the list elements... +pub enum ApplicationPosition { + CPU, + MEM, + DISK, + TEMP, + NETWORK, + PROCESS, +} + pub struct App { pub should_quit : bool, pub process_sorting_type : processes::ProcessSorting, pub process_sorting_reverse : bool, pub to_be_resorted : bool, - pub current_selected_process_position : u64, + pub currently_selected_process_position : u64, + pub currently_selected_disk_position : u64, + pub currently_selected_temperature_position : u64, pub temperature_type : temperature::TemperatureType, pub update_rate_in_milliseconds : u64, pub show_average_cpu : bool, + pub current_application_position : ApplicationPosition, + pub current_zoom_level_percent : f64, // Make at most 200, least 50? } impl App { @@ -19,10 +34,14 @@ impl App { should_quit : false, process_sorting_reverse : true, to_be_resorted : false, - current_selected_process_position : 0, + currently_selected_process_position : 0, + currently_selected_disk_position : 0, + currently_selected_temperature_position : 0, temperature_type, update_rate_in_milliseconds, show_average_cpu, + current_application_position : ApplicationPosition::PROCESS, + current_zoom_level_percent : 100.0, } } @@ -89,4 +108,43 @@ impl App { pub fn on_down(&mut self) { } + + pub fn decrement_position_count(&mut self) { + match self.current_application_position { + ApplicationPosition::PROCESS => self.change_process_position(1), + ApplicationPosition::TEMP => self.change_temp_position(1), + ApplicationPosition::DISK => self.change_disk_position(1), + _ => {} + } + } + + pub fn increment_position_count(&mut self) { + match self.current_application_position { + ApplicationPosition::PROCESS => self.change_process_position(-1), + ApplicationPosition::TEMP => self.change_temp_position(-1), + ApplicationPosition::DISK => self.change_disk_position(-1), + _ => {} + } + } + + fn change_process_position(&mut self, num_to_change_by : i32) { + if self.currently_selected_process_position + num_to_change_by as u64 > 0 { + self.currently_selected_process_position += num_to_change_by as u64; + } + // else if self.currently_selected_process_position < // TODO: Need to finish this! This should never go PAST the number of elements + } + + fn change_temp_position(&mut self, num_to_change_by : i32) { + if self.currently_selected_temperature_position + num_to_change_by as u64 > 0 { + self.currently_selected_temperature_position += num_to_change_by as u64; + } + // else if self.currently_selected_temperature_position < // TODO: Need to finish this! This should never go PAST the number of elements + } + + fn change_disk_position(&mut self, num_to_change_by : i32) { + if self.currently_selected_disk_position + num_to_change_by as u64 > 0 { + self.currently_selected_disk_position += num_to_change_by as u64; + } + // else if self.currently_selected_disk_position < // TODO: Need to finish this! This should never go PAST the number of elements + } } diff --git a/src/app/data_collection/processes.rs b/src/app/data_collection/processes.rs index 3f8512e0..e688b560 100644 --- a/src/app/data_collection/processes.rs +++ b/src/app/data_collection/processes.rs @@ -4,7 +4,6 @@ use heim_common::{ }; use std::{collections::HashMap, process::Command}; -#[allow(dead_code)] #[derive(Clone)] pub enum ProcessSorting { CPU, @@ -37,6 +36,9 @@ fn vangelis_cpu_usage_calculation(prev_idle : &mut f64, prev_non_idle : &mut f64 let stat_results = std::fs::read_to_string(path)?; let first_line = stat_results.split('\n').collect::>()[0]; + + // TODO: Consider grabbing by number of threads instead, and summing the total? + // ie: 4 threads, so: (prev - curr) / cpu_0 + ... + (prev - curr) / cpu_n instead? This might be how top does it? let val = first_line.split_whitespace().collect::>(); // SC in case that the parsing will fail due to length: @@ -167,7 +169,9 @@ fn convert_ps(process : &str, cpu_usage_percentage : f64, prev_pid_stats : &mut }) } -pub async fn get_sorted_processes_list(prev_idle : &mut f64, prev_non_idle : &mut f64, prev_pid_stats : &mut HashMap) -> Result, heim::Error> { +pub async fn get_sorted_processes_list( + prev_idle : &mut f64, prev_non_idle : &mut f64, prev_pid_stats : &mut std::collections::HashMap, +) -> Result, heim::Error> { let mut process_vector : Vec = Vec::new(); if cfg!(target_os = "linux") { diff --git a/src/canvas.rs b/src/canvas.rs index 40f13ec0..c6154351 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -70,7 +70,7 @@ pub fn draw_data(terminal : &mut Terminal, app_dat // CPU usage graph { let x_axis : Axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]); - let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 100.0]).labels(&["0%", "100%"]); + let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 100.5]).labels(&["0%", "100%"]); let mut dataset_vector : Vec = Vec::new(); @@ -116,7 +116,7 @@ pub fn draw_data(terminal : &mut Terminal, app_dat //Memory usage graph { let x_axis : Axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]); - let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 100.0]).labels(&["0%", "100%"]); // Offset as the zero value isn't drawn otherwise... + let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 100.5]).labels(&["0%", "100%"]); // Offset as the zero value isn't drawn otherwise... Chart::default() .block(Block::default().title("Memory Usage").borders(Borders::ALL).border_style(border_style)) .x_axis(x_axis) @@ -148,16 +148,20 @@ pub fn draw_data(terminal : &mut Terminal, app_dat // Disk usage table { + // TODO: We have to dynamically remove some of these table elements based on size... let width = f64::from(middle_divided_chunk_2[1].width); - Table::new(["Disk", "Mount", "Used", "Total", "Free"].iter(), disk_rows) + Table::new(["Disk", "Mount", "Used", "Total", "Free", "R/s", "W/s"].iter(), disk_rows) .block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style)) .header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD)) .widths(&[ - (width * 0.25) as u16, - (width * 0.2) as u16, - (width * 0.15) as u16, - (width * 0.15) as u16, - (width * 0.15) as u16, + // Must make sure these are NEVER zero! It will fail to display! Seems to only be this... + (width * 0.2) as u16 + 1, + (width * 0.2) as u16 + 1, + (width * 0.1) as u16 + 1, + (width * 0.1) as u16 + 1, + (width * 0.1) as u16 + 1, + (width * 0.1) as u16 + 1, + (width * 0.1) as u16 + 1, ]) .render(&mut f, middle_divided_chunk_2[1]); } @@ -165,7 +169,7 @@ pub fn draw_data(terminal : &mut Terminal, app_dat // Network graph { let x_axis : Axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]); - let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 1_000_000.0]).labels(&["0GB", "1GB"]); + let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 1_000_000.5]).labels(&["0GB", "1GB"]); Chart::default() .block(Block::default().title("Network").borders(Borders::ALL).border_style(border_style)) .x_axis(x_axis) diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 00000000..e6c6d17a --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,3 @@ +// TODO: Store like three minutes of data, then change how much is shown based on scaling! +pub const STALE_MAX_MILLISECONDS : u64 = 60 * 1000; // We wish to store at most 60 seconds worth of data. This may change in the future, or be configurable. +pub const TICK_RATE_IN_MILLISECONDS : u64 = 200; // We use this as it's a good value to work with. diff --git a/src/convert_data.rs b/src/convert_data.rs new file mode 100644 index 00000000..e3b70831 --- /dev/null +++ b/src/convert_data.rs @@ -0,0 +1,260 @@ +use crate::{app::data_collection, constants}; +use constants::*; + +pub fn update_temp_row(app_data : &data_collection::Data, temp_type : &data_collection::temperature::TemperatureType) -> Vec> { + let mut sensor_vector : Vec> = Vec::new(); + + for sensor in &app_data.list_of_temperature_sensor { + sensor_vector.push(vec![ + sensor.component_name.to_string(), + (sensor.temperature.ceil() as u64).to_string() + + match temp_type { + data_collection::temperature::TemperatureType::Celsius => "C", + data_collection::temperature::TemperatureType::Kelvin => "K", + data_collection::temperature::TemperatureType::Fahrenheit => "F", + }, + ]); + } + + sensor_vector +} + +// TODO: IO count NEEDS TO BE DONE!!!!! +pub fn update_disk_row(app_data : &data_collection::Data) -> Vec> { + let mut disk_vector : Vec> = Vec::new(); + for disk in &app_data.list_of_disks { + disk_vector.push(vec![ + disk.name.to_string(), + disk.mount_point.to_string(), + format!("{:.1}%", disk.used_space as f64 / disk.total_space as f64 * 100_f64), + if disk.free_space < 1024 { + disk.free_space.to_string() + "MB" + } + else { + (disk.free_space / 1024).to_string() + "GB" + }, + if disk.total_space < 1024 { + disk.total_space.to_string() + "MB" + } + else { + (disk.total_space / 1024).to_string() + "GB" + }, + ]); + } + + disk_vector +} + +pub fn update_process_row(app_data : &data_collection::Data) -> Vec> { + let mut process_vector : Vec> = Vec::new(); + + for process in &app_data.list_of_processes { + process_vector.push(vec![ + process.pid.to_string(), + process.command.to_string(), + 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 + } + ), + ]); + } + + process_vector +} + +pub fn update_cpu_data_points(show_avg_cpu : bool, app_data : &data_collection::Data) -> Vec<(String, Vec<(f64, f64)>)> { + let mut cpu_data_vector : Vec<(String, Vec<(f64, f64)>)> = Vec::new(); + let mut cpu_collection : Vec> = Vec::new(); + + if !app_data.list_of_cpu_packages.is_empty() { + // I'm sorry for the if statement but I couldn't be bothered here... + for cpu_num in (if show_avg_cpu { 0 } else { 1 })..app_data.list_of_cpu_packages.last().unwrap().cpu_vec.len() { + let mut this_cpu_data : Vec<(f64, f64)> = Vec::new(); + + for data in &app_data.list_of_cpu_packages { + let current_time = std::time::Instant::now(); + let current_cpu_usage = data.cpu_vec[cpu_num].cpu_usage; + + let new_entry = ( + ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), + current_cpu_usage, + ); + + // Now, inject our joining points... + if !this_cpu_data.is_empty() { + let previous_element_data = *(this_cpu_data.last().unwrap()); + for idx in 0..50 { + this_cpu_data.push(( + previous_element_data.0 + ((new_entry.0 - previous_element_data.0) / 50.0 * f64::from(idx)), + previous_element_data.1 + ((new_entry.1 - previous_element_data.1) / 50.0 * f64::from(idx)), + )); + } + } + + this_cpu_data.push(new_entry); + } + + cpu_collection.push(this_cpu_data); + } + + // Finally, add it all onto the end + for (i, data) in cpu_collection.iter().enumerate() { + cpu_data_vector.push(( + // + 1 to skip total CPU if show_avg_cpu is false + format!( + "{:4}: ", + &*(app_data.list_of_cpu_packages.last().unwrap().cpu_vec[i + if show_avg_cpu { 0 } else { 1 }].cpu_name) + ) + .to_uppercase() + &format!("{:3}%", (data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)), + data.clone(), + )) + } + } + + cpu_data_vector +} + +pub fn update_mem_data_points(app_data : &data_collection::Data) -> Vec<(f64, f64)> { + convert_mem_data(&app_data.memory) +} + +pub fn update_swap_data_points(app_data : &data_collection::Data) -> Vec<(f64, f64)> { + convert_mem_data(&app_data.swap) +} + +pub fn convert_mem_data(mem_data : &[data_collection::mem::MemData]) -> Vec<(f64, f64)> { + let mut result : Vec<(f64, f64)> = Vec::new(); + + for data in mem_data { + let current_time = std::time::Instant::now(); + let new_entry = ( + ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), + data.mem_used_in_mb as f64 / data.mem_total_in_mb as f64 * 100_f64, + ); + + // Now, inject our joining points... + if !result.is_empty() { + let previous_element_data = *(result.last().unwrap()); + for idx in 0..50 { + result.push(( + previous_element_data.0 + ((new_entry.0 - previous_element_data.0) / 50.0 * f64::from(idx)), + previous_element_data.1 + ((new_entry.1 - previous_element_data.1) / 50.0 * f64::from(idx)), + )); + } + } + + result.push(new_entry); + //debug!("Pushed: ({}, {})", result.last().unwrap().0, result.last().unwrap().1); + } + + result +} + +pub struct ConvertedNetworkData { + pub rx : Vec<(f64, f64)>, + pub tx : Vec<(f64, f64)>, + pub rx_display : String, + pub tx_display : String, +} + +pub fn update_network_data_points(app_data : &data_collection::Data) -> ConvertedNetworkData { + convert_network_data_points(&app_data.network) +} + +pub fn convert_network_data_points(network_data : &[data_collection::network::NetworkData]) -> ConvertedNetworkData { + let mut rx : Vec<(f64, f64)> = Vec::new(); + let mut tx : Vec<(f64, f64)> = Vec::new(); + + for data in network_data { + let current_time = std::time::Instant::now(); + let rx_data = ( + ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), + data.rx as f64 / 1024.0, + ); + let tx_data = ( + ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), + data.tx as f64 / 1024.0, + ); + + // Now, inject our joining points... + if !rx.is_empty() { + let previous_element_data = *(rx.last().unwrap()); + for idx in 0..50 { + rx.push(( + previous_element_data.0 + ((rx_data.0 - previous_element_data.0) / 50.0 * f64::from(idx)), + previous_element_data.1 + ((rx_data.1 - previous_element_data.1) / 50.0 * f64::from(idx)), + )); + } + } + + // Now, inject our joining points... + if !tx.is_empty() { + let previous_element_data = *(tx.last().unwrap()); + for idx in 0..50 { + tx.push(( + previous_element_data.0 + ((tx_data.0 - previous_element_data.0) / 50.0 * f64::from(idx)), + previous_element_data.1 + ((tx_data.1 - previous_element_data.1) / 50.0 * f64::from(idx)), + )); + } + } + + rx.push(rx_data); + tx.push(tx_data); + + debug!("Pushed rx: ({}, {})", rx.last().unwrap().0, rx.last().unwrap().1); + debug!("Pushed tx: ({}, {})", tx.last().unwrap().0, tx.last().unwrap().1); + } + + let rx_display = if network_data.is_empty() { + "0B".to_string() + } + else { + let num_bytes = network_data.last().unwrap().rx; + if num_bytes < 1024 { + format!("RX: {:4} B", num_bytes).to_string() + } + else if num_bytes < (1024 * 1024) { + format!("RX: {:4}KB", num_bytes / 1024).to_string() + } + else if num_bytes < (1024 * 1024 * 1024) { + format!("RX: {:4}MB", num_bytes / 1024 / 1024).to_string() + } + else { + format!("RX: {:4}GB", num_bytes / 1024 / 1024 / 1024).to_string() + } + }; + let tx_display = if network_data.is_empty() { + "0B".to_string() + } + else { + let num_bytes = network_data.last().unwrap().tx; + if num_bytes < 1024 { + format!("TX: {:4} B", num_bytes).to_string() + } + else if num_bytes < (1024 * 1024) { + format!("TX: {:4}KB", num_bytes / 1024).to_string() + } + else if num_bytes < (1024 * 1024 * 1024) { + format!("TX: {:4}MB", num_bytes / 1024 / 1024).to_string() + } + else { + format!("TX: {:4}GB", num_bytes / 1024 / 1024 / 1024).to_string() + } + }; + + ConvertedNetworkData { rx, tx, rx_display, tx_display } +} diff --git a/src/main.rs b/src/main.rs index 8c56d9da..d1c3c80f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,29 +7,32 @@ extern crate clap; #[macro_use] extern crate failure; -use crossterm::{input, AlternateScreen, InputEvent, KeyEvent}; +use crossterm::{input, AlternateScreen, InputEvent, KeyEvent, MouseButton, MouseEvent}; use std::{sync::mpsc, thread, time::Duration}; use tui::{backend::CrosstermBackend, Terminal}; -mod app; -use app::data_collection; +pub mod app; mod utils { pub mod error; pub mod logging; } -use utils::error::{self, RustopError}; mod canvas; +mod constants; +mod convert_data; + +use app::data_collection; +use constants::{STALE_MAX_MILLISECONDS, TICK_RATE_IN_MILLISECONDS}; +use convert_data::*; +use utils::error::{self, RustopError}; // End imports -enum Event { - Input(I), +enum Event { + KeyInput(I), + MouseInput(J), Update(Box), } -const STALE_MAX_MILLISECONDS : u64 = 60 * 1000; // We wish to store at most 60 seconds worth of data. This may change in the future, or be configurable. -const TICK_RATE_IN_MILLISECONDS : u64 = 200; // We use this as it's a good value to work with. - fn main() -> error::Result<()> { let _log = utils::logging::init_logger(); // TODO: Error handling @@ -79,20 +82,38 @@ fn main() -> error::Result<()> { // Create "app" struct, which will control most of the program and store settings/state let mut app = app::App::new(show_average_cpu, temperature_type, update_rate_in_milliseconds as u64); + // Set up up tui and crossterm + let screen = AlternateScreen::to_alternate(true)?; + let stdout = std::io::stdout(); + let backend = CrosstermBackend::with_alternate_screen(stdout, screen)?; + let mut terminal = Terminal::new(backend)?; + terminal.hide_cursor()?; + terminal.clear()?; + // Set up input handling let (tx, rx) = mpsc::channel(); { let tx = tx.clone(); thread::spawn(move || { let input = input(); + input.enable_mouse_mode().unwrap(); let reader = input.read_sync(); for event in reader { - if let InputEvent::Keyboard(key) = event { - if tx.send(Event::Input(key.clone())).is_err() { - return; + match event { + InputEvent::Keyboard(key) => { + if tx.send(Event::KeyInput(key.clone())).is_err() { + return; + } } + InputEvent::Mouse(mouse) => { + if tx.send(Event::MouseInput(mouse)).is_err() { + return; + } + } + _ => {} } } + input.disable_mouse_mode().unwrap(); }); } @@ -121,30 +142,21 @@ fn main() -> error::Result<()> { }); } - // Set up up tui and crossterm - let screen = AlternateScreen::to_alternate(true)?; - let stdout = std::io::stdout(); - let backend = CrosstermBackend::with_alternate_screen(stdout, screen)?; - let mut terminal = Terminal::new(backend)?; - terminal.hide_cursor()?; - terminal.clear()?; - let mut app_data = data_collection::Data::default(); let mut canvas_data = canvas::CanvasData::default(); loop { if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { match recv { - Event::Input(event) => { - debug!("Input event fired!"); + Event::KeyInput(event) => { + debug!("Keyboard event fired!"); match event { - KeyEvent::Char(c) => app.on_key(c), + KeyEvent::Ctrl('c') | KeyEvent::Esc => break, + KeyEvent::Char(c) => app.on_key(c), // TODO: We can remove the 'q' event and just move it to the quit? KeyEvent::Left => app.on_left(), KeyEvent::Right => app.on_right(), KeyEvent::Up => app.on_up(), KeyEvent::Down => app.on_down(), - KeyEvent::Ctrl('c') => break, - KeyEvent::Esc => break, _ => {} } @@ -155,6 +167,32 @@ fn main() -> error::Result<()> { } debug!("Input event complete."); } + Event::MouseInput(event) => { + debug!("Mouse event fired!"); + match event { + MouseEvent::Press(e, _x, _y) => { + debug!("Mouse press!"); + match e { + MouseButton::WheelUp => { + debug!("Wheel up!"); + } + MouseButton::WheelDown => { + debug!("Wheel down!"); + } + _ => {} + } + } + MouseEvent::Hold(_x, _y) => { + debug!("Mouse hold!"); + } + MouseEvent::Release(_x, _y) => { + debug!("Mouse release!"); + } + _ => { + debug!("Mouse unknown event..."); + } + } + } Event::Update(data) => { debug!("Update event fired!"); app_data = *data; @@ -187,261 +225,3 @@ fn main() -> error::Result<()> { debug!("Terminating."); Ok(()) } - -fn update_temp_row(app_data : &data_collection::Data, temp_type : &data_collection::temperature::TemperatureType) -> Vec> { - let mut sensor_vector : Vec> = Vec::new(); - - for sensor in &app_data.list_of_temperature_sensor { - sensor_vector.push(vec![ - sensor.component_name.to_string(), - (sensor.temperature.ceil() as u64).to_string() - + match temp_type { - data_collection::temperature::TemperatureType::Celsius => "C", - data_collection::temperature::TemperatureType::Kelvin => "K", - data_collection::temperature::TemperatureType::Fahrenheit => "F", - }, - ]); - } - - sensor_vector -} - -// TODO: IO count -fn update_disk_row(app_data : &data_collection::Data) -> Vec> { - let mut disk_vector : Vec> = Vec::new(); - for disk in &app_data.list_of_disks { - disk_vector.push(vec![ - disk.name.to_string(), - disk.mount_point.to_string(), - format!("{:.1}%", disk.used_space as f64 / disk.total_space as f64 * 100_f64), - if disk.free_space < 1024 { - disk.free_space.to_string() + "MB" - } - else { - (disk.free_space / 1024).to_string() + "GB" - }, - if disk.total_space < 1024 { - disk.total_space.to_string() + "MB" - } - else { - (disk.total_space / 1024).to_string() + "GB" - }, - ]); - } - - disk_vector -} - -fn update_process_row(app_data : &data_collection::Data) -> Vec> { - let mut process_vector : Vec> = Vec::new(); - - for process in &app_data.list_of_processes { - process_vector.push(vec![ - process.pid.to_string(), - process.command.to_string(), - 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 - } - ), - ]); - } - - process_vector -} - -fn update_cpu_data_points(show_avg_cpu : bool, app_data : &data_collection::Data) -> Vec<(String, Vec<(f64, f64)>)> { - let mut cpu_data_vector : Vec<(String, Vec<(f64, f64)>)> = Vec::new(); - let mut cpu_collection : Vec> = Vec::new(); - - if !app_data.list_of_cpu_packages.is_empty() { - // I'm sorry for the if statement but I couldn't be bothered here... - for cpu_num in (if show_avg_cpu { 0 } else { 1 })..app_data.list_of_cpu_packages.last().unwrap().cpu_vec.len() { - let mut this_cpu_data : Vec<(f64, f64)> = Vec::new(); - - for data in &app_data.list_of_cpu_packages { - let current_time = std::time::Instant::now(); - let current_cpu_usage = data.cpu_vec[cpu_num].cpu_usage; - - let new_entry = ( - ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), - current_cpu_usage, - ); - - // Now, inject our joining points... - if !this_cpu_data.is_empty() { - let previous_element_data = *(this_cpu_data.last().unwrap()); - for idx in 0..100 { - this_cpu_data.push(( - previous_element_data.0 + ((new_entry.0 - previous_element_data.0) / 100.0 * f64::from(idx)), - previous_element_data.1 + ((new_entry.1 - previous_element_data.1) / 100.0 * f64::from(idx)), - )); - } - } - - this_cpu_data.push(new_entry); - } - - cpu_collection.push(this_cpu_data); - } - - // Finally, add it all onto the end - for (i, data) in cpu_collection.iter().enumerate() { - cpu_data_vector.push(( - // + 1 to skip total CPU if show_avg_cpu is false - format!( - "{:4}: ", - &*(app_data.list_of_cpu_packages.last().unwrap().cpu_vec[i + if show_avg_cpu { 0 } else { 1 }].cpu_name) - ) - .to_uppercase() + &format!("{:3}%", (data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)), - data.clone(), - )) - } - } - - cpu_data_vector -} - -fn update_mem_data_points(app_data : &data_collection::Data) -> Vec<(f64, f64)> { - convert_mem_data(&app_data.memory) -} - -fn update_swap_data_points(app_data : &data_collection::Data) -> Vec<(f64, f64)> { - convert_mem_data(&app_data.swap) -} - -fn convert_mem_data(mem_data : &[data_collection::mem::MemData]) -> Vec<(f64, f64)> { - let mut result : Vec<(f64, f64)> = Vec::new(); - - for data in mem_data { - let current_time = std::time::Instant::now(); - let new_entry = ( - ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), - data.mem_used_in_mb as f64 / data.mem_total_in_mb as f64 * 100_f64, - ); - - // Now, inject our joining points... - if !result.is_empty() { - let previous_element_data = *(result.last().unwrap()); - for idx in 0..100 { - result.push(( - previous_element_data.0 + ((new_entry.0 - previous_element_data.0) / 100.0 * f64::from(idx)), - previous_element_data.1 + ((new_entry.1 - previous_element_data.1) / 100.0 * f64::from(idx)), - )); - } - } - - result.push(new_entry); - //debug!("Pushed: ({}, {})", result.last().unwrap().0, result.last().unwrap().1); - } - - result -} - -struct ConvertedNetworkData { - rx : Vec<(f64, f64)>, - tx : Vec<(f64, f64)>, - rx_display : String, - tx_display : String, -} - -fn update_network_data_points(app_data : &data_collection::Data) -> ConvertedNetworkData { - convert_network_data_points(&app_data.network) -} - -fn convert_network_data_points(network_data : &[data_collection::network::NetworkData]) -> ConvertedNetworkData { - let mut rx : Vec<(f64, f64)> = Vec::new(); - let mut tx : Vec<(f64, f64)> = Vec::new(); - - for data in network_data { - let current_time = std::time::Instant::now(); - let rx_data = ( - ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), - data.rx as f64 / 1024.0, - ); - let tx_data = ( - ((STALE_MAX_MILLISECONDS as f64 - current_time.duration_since(data.instant).as_millis() as f64) * 10_f64).floor(), - data.tx as f64 / 1024.0, - ); - - // Now, inject our joining points... - if !rx.is_empty() { - let previous_element_data = *(rx.last().unwrap()); - for idx in 0..100 { - rx.push(( - previous_element_data.0 + ((rx_data.0 - previous_element_data.0) / 100.0 * f64::from(idx)), - previous_element_data.1 + ((rx_data.1 - previous_element_data.1) / 100.0 * f64::from(idx)), - )); - } - } - - // Now, inject our joining points... - if !tx.is_empty() { - let previous_element_data = *(tx.last().unwrap()); - for idx in 0..100 { - tx.push(( - previous_element_data.0 + ((tx_data.0 - previous_element_data.0) / 100.0 * f64::from(idx)), - previous_element_data.1 + ((tx_data.1 - previous_element_data.1) / 100.0 * f64::from(idx)), - )); - } - } - - rx.push(rx_data); - tx.push(tx_data); - - debug!("Pushed rx: ({}, {})", rx.last().unwrap().0, rx.last().unwrap().1); - debug!("Pushed tx: ({}, {})", tx.last().unwrap().0, tx.last().unwrap().1); - } - - let rx_display = if network_data.is_empty() { - "0B".to_string() - } - else { - let num_bytes = network_data.last().unwrap().rx; - if num_bytes < 1024 { - format!("RX: {:4} B", num_bytes).to_string() - } - else if num_bytes < (1024 * 1024) { - format!("RX: {:4}KB", num_bytes / 1024).to_string() - } - else if num_bytes < (1024 * 1024 * 1024) { - format!("RX: {:4}MB", num_bytes / 1024 / 1024).to_string() - } - else { - format!("RX: {:4}GB", num_bytes / 1024 / 1024 / 1024).to_string() - } - }; - let tx_display = if network_data.is_empty() { - "0B".to_string() - } - else { - let num_bytes = network_data.last().unwrap().tx; - if num_bytes < 1024 { - format!("TX: {:4} B", num_bytes).to_string() - } - else if num_bytes < (1024 * 1024) { - format!("TX: {:4}KB", num_bytes / 1024).to_string() - } - else if num_bytes < (1024 * 1024 * 1024) { - format!("TX: {:4}MB", num_bytes / 1024 / 1024).to_string() - } - else { - format!("TX: {:4}GB", num_bytes / 1024 / 1024 / 1024).to_string() - } - }; - - ConvertedNetworkData { rx, tx, rx_display, tx_display } -}