mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-22 21:24:49 +02:00
Added scrolling event, need to implement across processes now.
This commit is contained in:
parent
05d4e82153
commit
1a4a261db6
34
TODO.md
34
TODO.md
@ -1,5 +1,7 @@
|
|||||||
# To-Do List
|
# To-Do List
|
||||||
|
|
||||||
|
## Pre-release (bare minimum)
|
||||||
|
|
||||||
* ~~Get each function working as a POC~~
|
* ~~Get each function working as a POC~~
|
||||||
|
|
||||||
* ~~Separate each component for readability, finalize project structure~~
|
* ~~Separate each component for readability, finalize project structure~~
|
||||||
@ -8,24 +10,36 @@
|
|||||||
|
|
||||||
* ~~Write tui display, charting~~
|
* ~~Write tui display, charting~~
|
||||||
|
|
||||||
* Scaling in and out
|
* ~~FIX PROCESSES AHHHHHH~~
|
||||||
|
|
||||||
* Add custom error because it's really messy
|
* Scrolling in at least processes
|
||||||
|
|
||||||
* Remove any ``unwrap()``.
|
|
||||||
|
|
||||||
* Scrolling event
|
|
||||||
|
|
||||||
* Keybindings
|
* 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
|
* 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!!!
|
* 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...
|
||||||
|
62
src/app.rs
62
src/app.rs
@ -1,15 +1,30 @@
|
|||||||
pub mod data_collection;
|
pub mod data_collection;
|
||||||
use data_collection::{processes, temperature};
|
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 struct App {
|
||||||
pub should_quit : bool,
|
pub should_quit : bool,
|
||||||
pub process_sorting_type : processes::ProcessSorting,
|
pub process_sorting_type : processes::ProcessSorting,
|
||||||
pub process_sorting_reverse : bool,
|
pub process_sorting_reverse : bool,
|
||||||
pub to_be_resorted : 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 temperature_type : temperature::TemperatureType,
|
||||||
pub update_rate_in_milliseconds : u64,
|
pub update_rate_in_milliseconds : u64,
|
||||||
pub show_average_cpu : bool,
|
pub show_average_cpu : bool,
|
||||||
|
pub current_application_position : ApplicationPosition,
|
||||||
|
pub current_zoom_level_percent : f64, // Make at most 200, least 50?
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
@ -19,10 +34,14 @@ impl App {
|
|||||||
should_quit : false,
|
should_quit : false,
|
||||||
process_sorting_reverse : true,
|
process_sorting_reverse : true,
|
||||||
to_be_resorted : false,
|
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,
|
temperature_type,
|
||||||
update_rate_in_milliseconds,
|
update_rate_in_milliseconds,
|
||||||
show_average_cpu,
|
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 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ use heim_common::{
|
|||||||
};
|
};
|
||||||
use std::{collections::HashMap, process::Command};
|
use std::{collections::HashMap, process::Command};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ProcessSorting {
|
pub enum ProcessSorting {
|
||||||
CPU,
|
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 stat_results = std::fs::read_to_string(path)?;
|
||||||
let first_line = stat_results.split('\n').collect::<Vec<&str>>()[0];
|
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>>();
|
let val = first_line.split_whitespace().collect::<Vec<&str>>();
|
||||||
|
|
||||||
// SC in case that the parsing will fail due to length:
|
// 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();
|
let mut process_vector : Vec<ProcessData> = Vec::new();
|
||||||
|
|
||||||
if cfg!(target_os = "linux") {
|
if cfg!(target_os = "linux") {
|
||||||
|
@ -70,7 +70,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
|
|||||||
// CPU usage graph
|
// CPU usage graph
|
||||||
{
|
{
|
||||||
let x_axis : Axis<String> = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]);
|
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();
|
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
|
//Memory usage graph
|
||||||
{
|
{
|
||||||
let x_axis : Axis<String> = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]);
|
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()
|
Chart::default()
|
||||||
.block(Block::default().title("Memory Usage").borders(Borders::ALL).border_style(border_style))
|
.block(Block::default().title("Memory Usage").borders(Borders::ALL).border_style(border_style))
|
||||||
.x_axis(x_axis)
|
.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
|
// 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);
|
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))
|
.block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style))
|
||||||
.header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD))
|
.header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD))
|
||||||
.widths(&[
|
.widths(&[
|
||||||
(width * 0.25) as u16,
|
// Must make sure these are NEVER zero! It will fail to display! Seems to only be this...
|
||||||
(width * 0.2) as u16,
|
(width * 0.2) as u16 + 1,
|
||||||
(width * 0.15) as u16,
|
(width * 0.2) as u16 + 1,
|
||||||
(width * 0.15) as u16,
|
(width * 0.1) as u16 + 1,
|
||||||
(width * 0.15) as u16,
|
(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]);
|
.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
|
// Network graph
|
||||||
{
|
{
|
||||||
let x_axis : Axis<String> = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]);
|
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()
|
Chart::default()
|
||||||
.block(Block::default().title("Network").borders(Borders::ALL).border_style(border_style))
|
.block(Block::default().title("Network").borders(Borders::ALL).border_style(border_style))
|
||||||
.x_axis(x_axis)
|
.x_axis(x_axis)
|
||||||
|
3
src/constants.rs
Normal file
3
src/constants.rs
Normal 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
260
src/convert_data.rs
Normal 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 }
|
||||||
|
}
|
346
src/main.rs
346
src/main.rs
@ -7,29 +7,32 @@ extern crate clap;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
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 std::{sync::mpsc, thread, time::Duration};
|
||||||
use tui::{backend::CrosstermBackend, Terminal};
|
use tui::{backend::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
mod app;
|
pub mod app;
|
||||||
use app::data_collection;
|
|
||||||
mod utils {
|
mod utils {
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
}
|
}
|
||||||
use utils::error::{self, RustopError};
|
|
||||||
mod canvas;
|
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
|
// End imports
|
||||||
|
|
||||||
enum Event<I> {
|
enum Event<I, J> {
|
||||||
Input(I),
|
KeyInput(I),
|
||||||
|
MouseInput(J),
|
||||||
Update(Box<data_collection::Data>),
|
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<()> {
|
fn main() -> error::Result<()> {
|
||||||
let _log = utils::logging::init_logger(); // TODO: Error handling
|
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
|
// 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);
|
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
|
// Set up input handling
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
{
|
{
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let input = input();
|
let input = input();
|
||||||
|
input.enable_mouse_mode().unwrap();
|
||||||
let reader = input.read_sync();
|
let reader = input.read_sync();
|
||||||
for event in reader {
|
for event in reader {
|
||||||
if let InputEvent::Keyboard(key) = event {
|
match event {
|
||||||
if tx.send(Event::Input(key.clone())).is_err() {
|
InputEvent::Keyboard(key) => {
|
||||||
return;
|
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 app_data = data_collection::Data::default();
|
||||||
let mut canvas_data = canvas::CanvasData::default();
|
let mut canvas_data = canvas::CanvasData::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {
|
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {
|
||||||
match recv {
|
match recv {
|
||||||
Event::Input(event) => {
|
Event::KeyInput(event) => {
|
||||||
debug!("Input event fired!");
|
debug!("Keyboard event fired!");
|
||||||
match event {
|
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::Left => app.on_left(),
|
||||||
KeyEvent::Right => app.on_right(),
|
KeyEvent::Right => app.on_right(),
|
||||||
KeyEvent::Up => app.on_up(),
|
KeyEvent::Up => app.on_up(),
|
||||||
KeyEvent::Down => app.on_down(),
|
KeyEvent::Down => app.on_down(),
|
||||||
KeyEvent::Ctrl('c') => break,
|
|
||||||
KeyEvent::Esc => break,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +167,32 @@ fn main() -> error::Result<()> {
|
|||||||
}
|
}
|
||||||
debug!("Input event complete.");
|
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) => {
|
Event::Update(data) => {
|
||||||
debug!("Update event fired!");
|
debug!("Update event fired!");
|
||||||
app_data = *data;
|
app_data = *data;
|
||||||
@ -187,261 +225,3 @@ fn main() -> error::Result<()> {
|
|||||||
debug!("Terminating.");
|
debug!("Terminating.");
|
||||||
Ok(())
|
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 }
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user