diff options
author | ClementTsang <clementjhtsang@gmail.com> | 2019-09-15 00:06:57 -0400 |
---|---|---|
committer | ClementTsang <clementjhtsang@gmail.com> | 2019-09-15 00:06:57 -0400 |
commit | 282acd1395fb521be3ca94c7731abfdfa2c9ae0c (patch) | |
tree | 1539d67a995ba268ec1d70f3fb615fbe3c7c1d18 /src | |
parent | 2435b9d90ce8029dd731238972f3610a56b6c3f9 (diff) |
Made charting look better, switched back to braille markers (its the only way I could make it look good), and dealt with some issues regarding the display of networking.
Diffstat (limited to 'src')
-rw-r--r-- | src/app/data_collection.rs | 1 | ||||
-rw-r--r-- | src/app/data_collection/disks.rs | 20 | ||||
-rw-r--r-- | src/app/data_collection/processes.rs | 32 | ||||
-rw-r--r-- | src/canvas.rs | 29 | ||||
-rw-r--r-- | src/main.rs | 132 |
5 files changed, 158 insertions, 56 deletions
diff --git a/src/app/data_collection.rs b/src/app/data_collection.rs index 4b7a04e9..4e45b719 100644 --- a/src/app/data_collection.rs +++ b/src/app/data_collection.rs @@ -103,7 +103,6 @@ impl DataState { } // Filter out stale timed entries - // TODO: ideally make this a generic function! let current_instant = std::time::Instant::now(); self.data.list_of_cpu_packages = self .data diff --git a/src/app/data_collection/disks.rs b/src/app/data_collection/disks.rs index ad897aed..1700ddbb 100644 --- a/src/app/data_collection/disks.rs +++ b/src/app/data_collection/disks.rs @@ -56,16 +56,18 @@ pub async fn get_disk_usage_list() -> Result<Vec<DiskData>, heim::Error> { let mut partitions_stream = heim::disk::partitions_physical(); while let Some(part) = partitions_stream.next().await { - let partition = part?; // TODO: Change this? We don't want to error out immediately... - let usage = heim::disk::usage(partition.mount_point().to_path_buf()).await?; + if let Ok(part) = part { + let partition = part; + let usage = heim::disk::usage(partition.mount_point().to_path_buf()).await?; - vec_disks.push(DiskData { - free_space : usage.free().get::<heim_common::units::information::megabyte>(), - used_space : usage.used().get::<heim_common::units::information::megabyte>(), - total_space : usage.total().get::<heim_common::units::information::megabyte>(), - mount_point : Box::from(partition.mount_point().to_str().unwrap_or("Name Unavailable")), - name : Box::from(partition.device().unwrap_or_else(|| std::ffi::OsStr::new("Name Unavailable")).to_str().unwrap_or("Name Unavailable")), - }); + vec_disks.push(DiskData { + free_space : usage.free().get::<heim_common::units::information::megabyte>(), + used_space : usage.used().get::<heim_common::units::information::megabyte>(), + total_space : usage.total().get::<heim_common::units::information::megabyte>(), + mount_point : Box::from(partition.mount_point().to_str().unwrap_or("Name Unavailable")), + name : Box::from(partition.device().unwrap_or_else(|| std::ffi::OsStr::new("Name Unavailable")).to_str().unwrap_or("Name Unavailable")), + }); + } } vec_disks.sort_by(|a, b| { diff --git a/src/app/data_collection/processes.rs b/src/app/data_collection/processes.rs index 2ef587bb..b1d495bf 100644 --- a/src/app/data_collection/processes.rs +++ b/src/app/data_collection/processes.rs @@ -39,15 +39,20 @@ fn vangelis_cpu_usage_calculation(prev_idle : &mut f64, prev_non_idle : &mut f64 let first_line = stat_results.split('\n').collect::<Vec<&str>>()[0]; let val = first_line.split_whitespace().collect::<Vec<&str>>(); - let user : f64 = val[1].parse::<_>().unwrap_or(-1_f64); // TODO: Better checking - let nice : f64 = val[2].parse::<_>().unwrap_or(-1_f64); - let system : f64 = val[3].parse::<_>().unwrap_or(-1_f64); - let idle : f64 = val[4].parse::<_>().unwrap_or(-1_f64); - let iowait : f64 = val[5].parse::<_>().unwrap_or(-1_f64); - let irq : f64 = val[6].parse::<_>().unwrap_or(-1_f64); - let softirq : f64 = val[7].parse::<_>().unwrap_or(-1_f64); - let steal : f64 = val[8].parse::<_>().unwrap_or(-1_f64); - let guest : f64 = val[9].parse::<_>().unwrap_or(-1_f64); + // SC in case that the parsing will fail due to length: + if val.len() <= 10 { + return Ok(1.0); // TODO: This is not the greatest... + } + + let user : f64 = val[1].parse::<_>().unwrap_or(0_f64); + let nice : f64 = val[2].parse::<_>().unwrap_or(0_f64); + let system : f64 = val[3].parse::<_>().unwrap_or(0_f64); + let idle : f64 = val[4].parse::<_>().unwrap_or(0_f64); + let iowait : f64 = val[5].parse::<_>().unwrap_or(0_f64); + let irq : f64 = val[6].parse::<_>().unwrap_or(0_f64); + let softirq : f64 = val[7].parse::<_>().unwrap_or(0_f64); + let steal : f64 = val[8].parse::<_>().unwrap_or(0_f64); + let guest : f64 = val[9].parse::<_>().unwrap_or(0_f64); let idle = idle + iowait; let non_idle = user + nice + system + irq + softirq + steal + guest; @@ -204,12 +209,13 @@ pub async fn get_sorted_processes_list(prev_idle : &mut f64, prev_non_idle : &mu } } else if cfg!(target_os = "macos") { - // macOS - dbg!("Mac"); // TODO: Remove + // TODO: macOS + debug!("Mac"); } else { - dbg!("Else"); // TODO: Remove - // Solaris: https://stackoverflow.com/a/4453581 + // TODO: Others? + debug!("Else"); + // Solaris: https://stackoverflow.com/a/4453581 } Ok(process_vector) diff --git a/src/canvas.rs b/src/canvas.rs index 333e0382..7858c42b 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -7,13 +7,16 @@ use tui::{ use crate::utils::error; -const COLOUR_LIST : [Color; 6] = [Color::LightRed, Color::LightGreen, Color::LightYellow, Color::LightBlue, Color::LightCyan, Color::LightMagenta]; +const COLOUR_LIST : [Color; 6] = [Color::Red, Color::Green, Color::LightYellow, Color::LightBlue, Color::LightCyan, Color::LightMagenta]; const TEXT_COLOUR : Color = Color::Gray; const GRAPH_COLOUR : Color = Color::Gray; const BORDER_STYLE_COLOUR : Color = Color::Gray; +const GRAPH_MARKER : Marker = Marker::Braille; #[derive(Default)] pub struct CanvasData { + pub rx_display : String, + pub tx_display : String, pub network_data_rx : Vec<(f64, f64)>, pub network_data_tx : Vec<(f64, f64)>, pub disk_data : Vec<Vec<String>>, @@ -61,14 +64,14 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_ // CPU usage graph { let x_axis : Axis<String> = 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.0, 100.0]).labels(&["0%", "50%", "100%"]); + let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 100.0]).labels(&["0%", "100%"]); let mut dataset_vector : Vec<Dataset> = Vec::new(); for (i, cpu) in canvas_data.cpu_data.iter().enumerate() { dataset_vector.push( Dataset::default() .name(&cpu.0) - .marker(Marker::Dot) + .marker(GRAPH_MARKER) .style(Style::default().fg(COLOUR_LIST[i % COLOUR_LIST.len()])) .data(&(cpu.1)), ); @@ -85,20 +88,20 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_ //Memory usage graph { let x_axis : Axis<String> = 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.0, 100.0]).labels(&["0%", "50%", "100%"]); + 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... Chart::default() .block(Block::default().title("Memory Usage").borders(Borders::ALL).border_style(border_style)) .x_axis(x_axis) .y_axis(y_axis) .datasets(&[ Dataset::default() - .name(&("MEM :".to_string() + &format!("{:3}%", (canvas_data.mem_data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) - .marker(Marker::Dot) + .name(&("RAM:".to_string() + &format!("{:3}%", (canvas_data.mem_data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) + .marker(GRAPH_MARKER) .style(Style::default().fg(Color::LightBlue)) .data(&canvas_data.mem_data), Dataset::default() - .name(&("SWAP:".to_string() + &format!("{:3}%", (canvas_data.swap_data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) - .marker(Marker::Dot) + .name(&("SWP:".to_string() + &format!("{:3}%", (canvas_data.swap_data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) + .marker(GRAPH_MARKER) .style(Style::default().fg(Color::LightYellow)) .data(&canvas_data.swap_data), ]) @@ -128,20 +131,20 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_ // Network graph { let x_axis : Axis<String> = 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.0, 1000.0]).labels(&["0Kb", "1000Kb"]); + let y_axis = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([-0.5, 1_000_000.0]).labels(&["0GB", "1GB"]); Chart::default() .block(Block::default().title("Network").borders(Borders::ALL).border_style(border_style)) .x_axis(x_axis) .y_axis(y_axis) .datasets(&[ Dataset::default() - .name(&("RX:".to_string() + &format!("{:3}%", (canvas_data.network_data_rx.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) - .marker(Marker::Dot) + .name(&(canvas_data.rx_display)) + .marker(GRAPH_MARKER) .style(Style::default().fg(Color::LightBlue)) .data(&canvas_data.network_data_rx), Dataset::default() - .name(&("TX:".to_string() + &format!("{:3}%", (canvas_data.network_data_tx.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)))) - .marker(Marker::Dot) + .name(&(canvas_data.tx_display)) + .marker(GRAPH_MARKER) .style(Style::default().fg(Color::LightYellow)) .data(&canvas_data.network_data_tx), ]) diff --git a/src/main.rs b/src/main.rs index c8b74cf2..75f66dc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ fn main() -> error::Result<()> { (about: "A graphical top clone.") (@arg THEME: -t --theme +takes_value "Sets a colour theme.") (@arg AVG_CPU: -a --avgcpu "Enables showing the average CPU usage.") - (@arg DEBUG: -d --debug "Enables debug mode.") + (@arg DEBUG: -d --debug "Enables debug mode.") // TODO: This isn't done yet! (@group TEMPERATURE_TYPE => (@arg CELSIUS : -c --celsius "Sets the temperature type to Celsius. This is the default option.") (@arg FAHRENHEIT : -f --fahrenheit "Sets the temperature type to Fahrenheit.") @@ -52,11 +52,16 @@ fn main() -> error::Result<()> { .after_help("Themes:") .get_matches(); - let update_rate_in_milliseconds : u64 = matches.value_of("RATE").unwrap_or("1000").parse::<u64>()?; + let update_rate_in_milliseconds : u128 = matches.value_of("RATE").unwrap_or("1000").parse::<u128>()?; if update_rate_in_milliseconds < 250 { return Err(RustopError::InvalidArg { - message : "Please set your rate to be greater than 250 milliseconds.".to_string(), + message : "Please set your update rate to be greater than 250 milliseconds.".to_string(), + }); + } + else if update_rate_in_milliseconds > u128::from(std::u64::MAX) { + return Err(RustopError::InvalidArg { + message : "Please set your update rate to be less than unsigned INT_MAX.".to_string(), }); } @@ -72,8 +77,7 @@ fn main() -> error::Result<()> { let show_average_cpu = matches.is_present("AVG_CPU"); // Create "app" struct, which will control most of the program and store settings/state - // TODO: Error handling here because users may be stupid and pass INT_MAX. - let mut app = app::App::new(show_average_cpu, temperature_type, update_rate_in_milliseconds); + let mut app = app::App::new(show_average_cpu, temperature_type, update_rate_in_milliseconds as u64); // Set up input handling let (tx, rx) = mpsc::channel(); @@ -104,7 +108,7 @@ fn main() -> error::Result<()> { loop { futures::executor::block_on(data_state.update_data()); tx.send(Event::Update(Box::from(data_state.data.clone()))).unwrap(); - thread::sleep(Duration::from_millis(update_rate_in_milliseconds)); + thread::sleep(Duration::from_millis(update_rate_in_milliseconds as u64)); } }); } @@ -151,6 +155,8 @@ fn main() -> error::Result<()> { let network_data = update_network_data_points(&app_data); canvas_data.network_data_rx = network_data.rx; canvas_data.network_data_tx = network_data.tx; + canvas_data.rx_display = network_data.rx_display; + canvas_data.tx_display = network_data.tx_display; canvas_data.disk_data = update_disk_row(&app_data); canvas_data.temp_sensor_data = update_temp_row(&app_data, &app.temperature_type); canvas_data.process_data = update_process_row(&app_data); @@ -253,8 +259,6 @@ fn update_cpu_data_points(show_avg_cpu : bool, app_data : &data_collection::Data let mut cpu_collection : Vec<Vec<(f64, f64)>> = Vec::new(); if !app_data.list_of_cpu_packages.is_empty() { - // Initially, populate the cpu_collection. We want to inject elements in between if possible. - // 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(); @@ -262,10 +266,24 @@ fn update_cpu_data_points(show_avg_cpu : bool, app_data : &data_collection::Data 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; - this_cpu_data.push(( + + 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); @@ -298,11 +316,23 @@ fn convert_mem_data(mem_data : &[data_collection::mem::MemData]) -> Vec<(f64, f6 for data in mem_data { let current_time = std::time::Instant::now(); - - result.push(( + 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); } @@ -312,6 +342,8 @@ fn convert_mem_data(mem_data : &[data_collection::mem::MemData]) -> Vec<(f64, f6 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 { @@ -324,20 +356,80 @@ fn convert_network_data_points(network_data : &[data_collection::network::Networ for data in network_data { let current_time = std::time::Instant::now(); - - rx.push(( + 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, - )); - - tx.push(( + ); + 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); } - ConvertedNetworkData { rx, tx } + 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 } } |