diff --git a/Cargo.toml b/Cargo.toml index ba5fbff1..d357f593 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] chrono = "0.4.9" +clap = "2.33.0" crossterm = "0.10.2" futures-preview = "0.3.0-alpha.18" fern = "0.5" diff --git a/TODO.md b/TODO.md index 65388e30..b97e0a71 100644 --- a/TODO.md +++ b/TODO.md @@ -10,9 +10,11 @@ * Add custom error because it's really messy +* Scrolling event + * Keybindings -* FIX PROCESSES AHHHHHH +~~* FIX PROCESSES AHHHHHH~~ * Refactor everything because it's a mess diff --git a/src/widgets/cpu.rs b/src/app/data_collection/cpu.rs similarity index 86% rename from src/widgets/cpu.rs rename to src/app/data_collection/cpu.rs index f6c7f31d..e2a00272 100644 --- a/src/widgets/cpu.rs +++ b/src/app/data_collection/cpu.rs @@ -4,7 +4,7 @@ use sysinfo::{ProcessorExt, System, SystemExt}; #[derive(Clone)] pub struct CPUData { pub cpu_name : Box, - pub cpu_usage : u32, + pub cpu_usage : f64, } #[derive(Clone)] @@ -20,7 +20,7 @@ pub fn get_cpu_data_list(sys : &System) -> Result { for cpu in cpu_data { cpu_vec.push(CPUData { cpu_name : Box::from(cpu.get_name()), - cpu_usage : (cpu.get_cpu_usage() * 100_f32).ceil() as u32, + cpu_usage : f64::from(cpu.get_cpu_usage()) * 100_f64, }) } diff --git a/src/widgets/disks.rs b/src/app/data_collection/disks.rs similarity index 100% rename from src/widgets/disks.rs rename to src/app/data_collection/disks.rs diff --git a/src/widgets/mem.rs b/src/app/data_collection/mem.rs similarity index 100% rename from src/widgets/mem.rs rename to src/app/data_collection/mem.rs diff --git a/src/app/data_collection/mod.rs b/src/app/data_collection/mod.rs new file mode 100644 index 00000000..fdbd4bcd --- /dev/null +++ b/src/app/data_collection/mod.rs @@ -0,0 +1,6 @@ +pub mod cpu; +pub mod disks; +pub mod mem; +pub mod network; +pub mod processes; +pub mod temperature; diff --git a/src/widgets/network.rs b/src/app/data_collection/network.rs similarity index 84% rename from src/widgets/network.rs rename to src/app/data_collection/network.rs index 24a89678..b54e93dd 100644 --- a/src/widgets/network.rs +++ b/src/app/data_collection/network.rs @@ -5,6 +5,8 @@ use sysinfo::{NetworkExt, System, SystemExt}; pub struct NetworkData { pub rx : u64, pub tx : u64, + pub total_rx : u64, + pub total_tx : u64, pub instant : Instant, } @@ -13,6 +15,8 @@ pub fn get_network_data(sys : &System) -> Result { Ok(NetworkData { rx : network_data.get_income(), tx : network_data.get_outcome(), + total_rx : 0, + total_tx : 0, instant : Instant::now(), }) } diff --git a/src/widgets/processes.rs b/src/app/data_collection/processes.rs similarity index 92% rename from src/widgets/processes.rs rename to src/app/data_collection/processes.rs index e0d43dcd..e4bb0bd4 100644 --- a/src/widgets/processes.rs +++ b/src/app/data_collection/processes.rs @@ -52,10 +52,14 @@ fn vangelis_cpu_usage_calculation(prev_idle : &mut f64, prev_non_idle : &mut f64 let total_delta : f64 = total - prev_total; let idle_delta : f64 = idle - *prev_idle; + debug!("Vangelis function: CPU PERCENT: {}", (total_delta - idle_delta) / total_delta * 100_f64); + *prev_idle = idle; *prev_non_idle = non_idle; - Ok(total_delta - idle_delta) + let result = if total_delta - idle_delta != 0_f64 { total_delta - idle_delta } else { 1_f64 }; + + Ok(result) // This works, REALLY damn well. The percentage check is within like 2% of the sysinfo one. } fn get_ordering(a_val : T, b_val : T, reverse_order : bool) -> std::cmp::Ordering { @@ -96,16 +100,18 @@ fn get_process_cpu_stats(pid : u32) -> std::io::Result { let stat_results = std::fs::read_to_string(path)?; let val = stat_results.split_whitespace().collect::>(); - let utime = val[13].parse::().unwrap_or(-1_f64); - let stime = val[14].parse::().unwrap_or(-1_f64); + let utime = val[13].parse::().unwrap_or(0_f64); + let stime = val[14].parse::().unwrap_or(0_f64); - Ok(utime + stime) + debug!("PID: {}, utime: {}, stime: {}", pid, utime, stime); + + Ok(utime + stime) // This seems to match top... } fn linux_cpu_usage(pid : u32, cpu_usage : f64, previous_pid_stats : &mut HashMap) -> std::io::Result { // Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556 let before_proc_val : f64 = if previous_pid_stats.contains_key(&pid.to_string()) { - *previous_pid_stats.get(&pid.to_string()).unwrap_or(&-1_f64) + *previous_pid_stats.get(&pid.to_string()).unwrap_or(&0_f64) } else { 0_f64 diff --git a/src/widgets/temperature.rs b/src/app/data_collection/temperature.rs similarity index 100% rename from src/widgets/temperature.rs rename to src/app/data_collection/temperature.rs diff --git a/src/widgets/mod.rs b/src/app/mod.rs similarity index 97% rename from src/widgets/mod.rs rename to src/app/mod.rs index a2fc252a..c07b3884 100644 --- a/src/widgets/mod.rs +++ b/src/app/mod.rs @@ -1,9 +1,5 @@ -pub mod cpu; -pub mod disks; -pub mod mem; -pub mod network; -pub mod processes; -pub mod temperature; +pub mod data_collection; +use data_collection::{cpu, disks, mem, network, processes, temperature}; use std::collections::HashMap; use sysinfo::{System, SystemExt}; @@ -46,7 +42,7 @@ pub struct DataState { pub data : Data, sys : System, stale_max_seconds : u64, - prev_pid_stats : HashMap, + prev_pid_stats : HashMap, // TODO: Purge list? prev_idle : f64, prev_non_idle : f64, } diff --git a/src/main.rs b/src/main.rs index 1d8207f8..9a578c10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,19 +4,20 @@ use std::{io, sync::mpsc, thread, time::Duration}; use tui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, - style::{Color, Style}, + style::{Color, Modifier, Style}, widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget}, Terminal, }; -mod widgets; +mod app; +use app::data_collection; #[macro_use] extern crate log; enum Event { Input(I), - Update(Box), + Update(Box), } const STALE_MAX_SECONDS : u64 = 60; @@ -30,7 +31,7 @@ async fn main() -> Result<(), io::Error> { let tick_rate_in_milliseconds : u64 = 250; let update_rate_in_milliseconds : u64 = 1000; // TODO: Must set a check to prevent this from going into negatives! - let mut app = widgets::App::new("rustop"); + let mut app = app::App::new("rustop"); let log = init_logger(); @@ -53,7 +54,7 @@ async fn main() -> Result<(), io::Error> { } // Event loop - let mut data_state = widgets::DataState::default(); + let mut data_state = app::DataState::default(); data_state.init(); data_state.set_stale_max_seconds(STALE_MAX_SECONDS); { @@ -70,7 +71,10 @@ async fn main() -> Result<(), io::Error> { terminal.clear()?; - let mut app_data = widgets::Data::default(); + let mut app_data = app::Data::default(); + let mut swap_data : Vec<(f64, f64)> = Vec::new(); + let mut mem_data : Vec<(f64, f64)> = Vec::new(); + loop { if let Ok(recv) = rx.recv_timeout(Duration::from_millis(tick_rate_in_milliseconds)) { match recv { @@ -87,16 +91,22 @@ async fn main() -> Result<(), io::Error> { } if app.to_be_resorted { - widgets::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse); + data_collection::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse); app.to_be_resorted = false; } try_debug(&log, "Input event complete."); + + // Only update processes } Event::Update(data) => { try_debug(&log, "Update event fired!"); app_data = *data; - widgets::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse); + data_collection::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse); try_debug(&log, "Update event complete."); + + // Convert all data into tui components + mem_data = update_mem_data_points(&app_data); + swap_data = update_swap_data_points(&app_data); } } if app.should_quit { @@ -105,14 +115,33 @@ async fn main() -> Result<(), io::Error> { } // Draw! - draw_data(&mut terminal, &app_data)?; + // TODO: We should change this btw! It should not redraw everything on every tick! + draw_data(&mut terminal, &app_data, &mem_data, &swap_data)?; } Ok(()) } -fn draw_data(terminal : &mut Terminal, app_data : &widgets::Data) -> Result<(), io::Error> { - // Convert data into tui components +fn update_temp_row(app_data : &app::Data) { +} + +fn update_process_row(app_data : &app::Data) { +} + +fn update_cpu_data_points(app_data : &app::Data) { +} + +fn update_mem_data_points(app_data : &app::Data) -> Vec<(f64, f64)> { + convert_mem_data(&app_data.memory) +} + +fn update_swap_data_points(app_data : &app::Data) -> Vec<(f64, f64)> { + convert_mem_data(&app_data.swap) +} + +fn draw_data(terminal : &mut Terminal, app_data : &app::Data, mem_data : &[(f64, f64)], swap_data : &[(f64, f64)]) -> Result<(), io::Error> { + const COLOUR_LIST : [Color; 6] = [Color::LightCyan, Color::LightMagenta, Color::LightRed, Color::LightGreen, Color::LightYellow, Color::LightBlue]; + let temperature_rows = app_data.list_of_temperature.iter().map(|sensor| { Row::StyledData( vec![sensor.component_name.to_string(), sensor.temperature.to_string() + "C"].into_iter(), // TODO: Change this based on temperature type @@ -134,6 +163,32 @@ fn draw_data(terminal : &mut Terminal, app_data : ) }); + let mut dataset_vector : Vec = Vec::new(); + let mut data_vector : Vec> = Vec::new(); + + if !app_data.list_of_cpu_packages.is_empty() { + for cpu_num in 0..app_data.list_of_cpu_packages.last().unwrap().cpu_vec.len() { + let mut this_cpu_data : Vec<(f64, f64)> = Vec::new(); + let current_time = std::time::Instant::now(); + + for cpu in &app_data.list_of_cpu_packages { + this_cpu_data.push((STALE_MAX_SECONDS as f64 - current_time.duration_since(cpu.instant).as_secs_f64(), cpu.cpu_vec[cpu_num].cpu_usage)); + } + + data_vector.push(this_cpu_data); + } + + for (i, data) in data_vector.iter().enumerate() { + dataset_vector.push( + Dataset::default() + .name(&*(app_data.list_of_cpu_packages.last().unwrap().cpu_vec[i].cpu_name)) + .marker(Marker::Braille) + .style(Style::default().fg(COLOUR_LIST[i % COLOUR_LIST.len()])) + .data(&data), + ) + } + } + let process_rows = app_data.list_of_processes.iter().map(|process| { Row::StyledData( vec![ @@ -200,24 +255,15 @@ fn draw_data(terminal : &mut Terminal, app_data : // CPU usage graph { + debug!("Drawing CPU..."); let x_axis : Axis = Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 60.0]); let y_axis = Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 100.0]).labels(&["0.0", "50.0", "100.0"]); + Chart::default() .block(Block::default().title("CPU Usage").borders(Borders::ALL)) .x_axis(x_axis) .y_axis(y_axis) - .datasets(&[ - Dataset::default() - .name("CPU0") - .marker(Marker::Braille) - .style(Style::default().fg(Color::Cyan)) - .data(&convert_cpu_data(0, &app_data.list_of_cpu_packages)), - Dataset::default() - .name("CPU1") - .marker(Marker::Braille) - .style(Style::default().fg(Color::LightMagenta)) - .data(&convert_cpu_data(1, &app_data.list_of_cpu_packages)), - ]) + .datasets(&dataset_vector) .render(&mut f, top_chunks[0]); } @@ -230,16 +276,8 @@ fn draw_data(terminal : &mut Terminal, app_data : .x_axis(x_axis) .y_axis(y_axis) .datasets(&[ - Dataset::default() - .name("MEM") - .marker(Marker::Braille) - .style(Style::default().fg(Color::Cyan)) - .data(&convert_mem_data(&app_data.memory)), - Dataset::default() - .name("SWAP") - .marker(Marker::Braille) - .style(Style::default().fg(Color::LightGreen)) - .data(&convert_mem_data(&app_data.swap)), + Dataset::default().name("MEM").marker(Marker::Braille).style(Style::default().fg(Color::Cyan)).data(&mem_data), + Dataset::default().name("SWAP").marker(Marker::Braille).style(Style::default().fg(Color::LightGreen)).data(&swap_data), ]) .render(&mut f, top_chunks[1]); } @@ -278,22 +316,7 @@ fn draw_data(terminal : &mut Terminal, app_data : Ok(()) } -// TODO: Remove this count, this is for testing, lol -fn convert_cpu_data(count : usize, cpu_data : &[widgets::cpu::CPUPackage]) -> Vec<(f64, f64)> { - let mut result : Vec<(f64, f64)> = Vec::new(); - let current_time = std::time::Instant::now(); - - for data in cpu_data { - result.push(( - STALE_MAX_SECONDS as f64 - current_time.duration_since(data.instant).as_secs() as f64, - f64::from(data.cpu_vec[count + 1].cpu_usage), - )); - } - - result -} - -fn convert_mem_data(mem_data : &[widgets::mem::MemData]) -> Vec<(f64, f64)> { +fn convert_mem_data(mem_data : &[app::data_collection::mem::MemData]) -> Vec<(f64, f64)> { let mut result : Vec<(f64, f64)> = Vec::new(); let current_time = std::time::Instant::now();