Added scrolling event, need to implement across processes now.

This commit is contained in:
ClementTsang 2019-09-15 14:16:18 -04:00
parent 05d4e82153
commit 1a4a261db6
7 changed files with 429 additions and 306 deletions

34
TODO.md
View File

@ -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...

View File

@ -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
}
}

View File

@ -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::<Vec<&str>>()[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::<Vec<&str>>();
// 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<String, f64>) -> Result<Vec<ProcessData>, 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<String, f64>,
) -> Result<Vec<ProcessData>, heim::Error> {
let mut process_vector : Vec<ProcessData> = Vec::new();
if cfg!(target_os = "linux") {

View File

@ -70,7 +70,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
// 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.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<Dataset> = Vec::new();
@ -116,7 +116,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
//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.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<B : tui::backend::Backend>(terminal : &mut Terminal<B>, 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<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
// 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.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)

3
src/constants.rs Normal file
View File

@ -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.

260
src/convert_data.rs Normal file
View File

@ -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<Vec<String>> {
let mut sensor_vector : Vec<Vec<String>> = 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<Vec<String>> {
let mut disk_vector : Vec<Vec<String>> = 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<Vec<String>> {
let mut process_vector : Vec<Vec<String>> = 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<(f64, f64)>> = 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 }
}

View File

@ -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<I> {
Input(I),
enum Event<I, J> {
KeyInput(I),
MouseInput(J),
Update(Box<data_collection::Data>),
}
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<Vec<String>> {
let mut sensor_vector : Vec<Vec<String>> = 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<Vec<String>> {
let mut disk_vector : Vec<Vec<String>> = 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<Vec<String>> {
let mut process_vector : Vec<Vec<String>> = 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<(f64, f64)>> = 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 }
}