mirror of
https://github.com/ClementTsang/bottom.git
synced 2024-11-28 19:12:43 +03:00
More refactoring.
This commit is contained in:
parent
b7081dd0e4
commit
50d3be05dd
@ -8,6 +8,7 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.9"
|
chrono = "0.4.9"
|
||||||
|
clap = "2.33.0"
|
||||||
crossterm = "0.10.2"
|
crossterm = "0.10.2"
|
||||||
futures-preview = "0.3.0-alpha.18"
|
futures-preview = "0.3.0-alpha.18"
|
||||||
fern = "0.5"
|
fern = "0.5"
|
||||||
|
4
TODO.md
4
TODO.md
@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
* Add custom error because it's really messy
|
* Add custom error because it's really messy
|
||||||
|
|
||||||
|
* Scrolling event
|
||||||
|
|
||||||
* Keybindings
|
* Keybindings
|
||||||
|
|
||||||
* FIX PROCESSES AHHHHHH
|
~~* FIX PROCESSES AHHHHHH~~
|
||||||
|
|
||||||
* Refactor everything because it's a mess
|
* Refactor everything because it's a mess
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use sysinfo::{ProcessorExt, System, SystemExt};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CPUData {
|
pub struct CPUData {
|
||||||
pub cpu_name : Box<str>,
|
pub cpu_name : Box<str>,
|
||||||
pub cpu_usage : u32,
|
pub cpu_usage : f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -20,7 +20,7 @@ pub fn get_cpu_data_list(sys : &System) -> Result<CPUPackage, heim::Error> {
|
|||||||
for cpu in cpu_data {
|
for cpu in cpu_data {
|
||||||
cpu_vec.push(CPUData {
|
cpu_vec.push(CPUData {
|
||||||
cpu_name : Box::from(cpu.get_name()),
|
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
6
src/app/data_collection/mod.rs
Normal file
6
src/app/data_collection/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod cpu;
|
||||||
|
pub mod disks;
|
||||||
|
pub mod mem;
|
||||||
|
pub mod network;
|
||||||
|
pub mod processes;
|
||||||
|
pub mod temperature;
|
@ -5,6 +5,8 @@ use sysinfo::{NetworkExt, System, SystemExt};
|
|||||||
pub struct NetworkData {
|
pub struct NetworkData {
|
||||||
pub rx : u64,
|
pub rx : u64,
|
||||||
pub tx : u64,
|
pub tx : u64,
|
||||||
|
pub total_rx : u64,
|
||||||
|
pub total_tx : u64,
|
||||||
pub instant : Instant,
|
pub instant : Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,6 +15,8 @@ pub fn get_network_data(sys : &System) -> Result<NetworkData, heim::Error> {
|
|||||||
Ok(NetworkData {
|
Ok(NetworkData {
|
||||||
rx : network_data.get_income(),
|
rx : network_data.get_income(),
|
||||||
tx : network_data.get_outcome(),
|
tx : network_data.get_outcome(),
|
||||||
|
total_rx : 0,
|
||||||
|
total_tx : 0,
|
||||||
instant : Instant::now(),
|
instant : Instant::now(),
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -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 total_delta : f64 = total - prev_total;
|
||||||
let idle_delta : f64 = idle - *prev_idle;
|
let idle_delta : f64 = idle - *prev_idle;
|
||||||
|
|
||||||
|
debug!("Vangelis function: CPU PERCENT: {}", (total_delta - idle_delta) / total_delta * 100_f64);
|
||||||
|
|
||||||
*prev_idle = idle;
|
*prev_idle = idle;
|
||||||
*prev_non_idle = non_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<T : std::cmp::PartialOrd>(a_val : T, b_val : T, reverse_order : bool) -> std::cmp::Ordering {
|
fn get_ordering<T : std::cmp::PartialOrd>(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<f64> {
|
|||||||
|
|
||||||
let stat_results = std::fs::read_to_string(path)?;
|
let stat_results = std::fs::read_to_string(path)?;
|
||||||
let val = stat_results.split_whitespace().collect::<Vec<&str>>();
|
let val = stat_results.split_whitespace().collect::<Vec<&str>>();
|
||||||
let utime = val[13].parse::<f64>().unwrap_or(-1_f64);
|
let utime = val[13].parse::<f64>().unwrap_or(0_f64);
|
||||||
let stime = val[14].parse::<f64>().unwrap_or(-1_f64);
|
let stime = val[14].parse::<f64>().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<String, f64>) -> std::io::Result<f64> {
|
fn linux_cpu_usage(pid : u32, cpu_usage : f64, previous_pid_stats : &mut HashMap<String, f64>) -> std::io::Result<f64> {
|
||||||
// Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556
|
// 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()) {
|
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 {
|
else {
|
||||||
0_f64
|
0_f64
|
@ -1,9 +1,5 @@
|
|||||||
pub mod cpu;
|
pub mod data_collection;
|
||||||
pub mod disks;
|
use data_collection::{cpu, disks, mem, network, processes, temperature};
|
||||||
pub mod mem;
|
|
||||||
pub mod network;
|
|
||||||
pub mod processes;
|
|
||||||
pub mod temperature;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use sysinfo::{System, SystemExt};
|
use sysinfo::{System, SystemExt};
|
||||||
@ -46,7 +42,7 @@ pub struct DataState {
|
|||||||
pub data : Data,
|
pub data : Data,
|
||||||
sys : System,
|
sys : System,
|
||||||
stale_max_seconds : u64,
|
stale_max_seconds : u64,
|
||||||
prev_pid_stats : HashMap<String, f64>,
|
prev_pid_stats : HashMap<String, f64>, // TODO: Purge list?
|
||||||
prev_idle : f64,
|
prev_idle : f64,
|
||||||
prev_non_idle : f64,
|
prev_non_idle : f64,
|
||||||
}
|
}
|
121
src/main.rs
121
src/main.rs
@ -4,19 +4,20 @@ use std::{io, sync::mpsc, thread, time::Duration};
|
|||||||
use tui::{
|
use tui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Style},
|
style::{Color, Modifier, Style},
|
||||||
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod widgets;
|
mod app;
|
||||||
|
use app::data_collection;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
enum Event<I> {
|
enum Event<I> {
|
||||||
Input(I),
|
Input(I),
|
||||||
Update(Box<widgets::Data>),
|
Update(Box<app::Data>),
|
||||||
}
|
}
|
||||||
|
|
||||||
const STALE_MAX_SECONDS : u64 = 60;
|
const STALE_MAX_SECONDS : u64 = 60;
|
||||||
@ -30,7 +31,7 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
let tick_rate_in_milliseconds : u64 = 250;
|
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 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();
|
let log = init_logger();
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
let mut data_state = widgets::DataState::default();
|
let mut data_state = app::DataState::default();
|
||||||
data_state.init();
|
data_state.init();
|
||||||
data_state.set_stale_max_seconds(STALE_MAX_SECONDS);
|
data_state.set_stale_max_seconds(STALE_MAX_SECONDS);
|
||||||
{
|
{
|
||||||
@ -70,7 +71,10 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
|
|
||||||
terminal.clear()?;
|
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 {
|
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 {
|
||||||
@ -87,16 +91,22 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if app.to_be_resorted {
|
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;
|
app.to_be_resorted = false;
|
||||||
}
|
}
|
||||||
try_debug(&log, "Input event complete.");
|
try_debug(&log, "Input event complete.");
|
||||||
|
|
||||||
|
// Only update processes
|
||||||
}
|
}
|
||||||
Event::Update(data) => {
|
Event::Update(data) => {
|
||||||
try_debug(&log, "Update event fired!");
|
try_debug(&log, "Update event fired!");
|
||||||
app_data = *data;
|
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.");
|
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 {
|
if app.should_quit {
|
||||||
@ -105,14 +115,33 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw!
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data : &widgets::Data) -> Result<(), io::Error> {
|
fn update_temp_row(app_data : &app::Data) {
|
||||||
// Convert data into tui components
|
}
|
||||||
|
|
||||||
|
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<B : tui::backend::Backend>(terminal : &mut Terminal<B>, 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| {
|
let temperature_rows = app_data.list_of_temperature.iter().map(|sensor| {
|
||||||
Row::StyledData(
|
Row::StyledData(
|
||||||
vec![sensor.component_name.to_string(), sensor.temperature.to_string() + "C"].into_iter(), // TODO: Change this based on temperature type
|
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<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data :
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut dataset_vector : Vec<Dataset> = Vec::new();
|
||||||
|
let mut data_vector : Vec<Vec<(f64, f64)>> = 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| {
|
let process_rows = app_data.list_of_processes.iter().map(|process| {
|
||||||
Row::StyledData(
|
Row::StyledData(
|
||||||
vec![
|
vec![
|
||||||
@ -200,24 +255,15 @@ fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data :
|
|||||||
|
|
||||||
// CPU usage graph
|
// CPU usage graph
|
||||||
{
|
{
|
||||||
|
debug!("Drawing CPU...");
|
||||||
let x_axis : Axis<String> = Axis::default().style(Style::default().fg(Color::White)).bounds([0.0, 60.0]);
|
let x_axis : Axis<String> = 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"]);
|
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()
|
Chart::default()
|
||||||
.block(Block::default().title("CPU Usage").borders(Borders::ALL))
|
.block(Block::default().title("CPU Usage").borders(Borders::ALL))
|
||||||
.x_axis(x_axis)
|
.x_axis(x_axis)
|
||||||
.y_axis(y_axis)
|
.y_axis(y_axis)
|
||||||
.datasets(&[
|
.datasets(&dataset_vector)
|
||||||
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)),
|
|
||||||
])
|
|
||||||
.render(&mut f, top_chunks[0]);
|
.render(&mut f, top_chunks[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,16 +276,8 @@ fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data :
|
|||||||
.x_axis(x_axis)
|
.x_axis(x_axis)
|
||||||
.y_axis(y_axis)
|
.y_axis(y_axis)
|
||||||
.datasets(&[
|
.datasets(&[
|
||||||
Dataset::default()
|
Dataset::default().name("MEM").marker(Marker::Braille).style(Style::default().fg(Color::Cyan)).data(&mem_data),
|
||||||
.name("MEM")
|
Dataset::default().name("SWAP").marker(Marker::Braille).style(Style::default().fg(Color::LightGreen)).data(&swap_data),
|
||||||
.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)),
|
|
||||||
])
|
])
|
||||||
.render(&mut f, top_chunks[1]);
|
.render(&mut f, top_chunks[1]);
|
||||||
}
|
}
|
||||||
@ -278,22 +316,7 @@ fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data :
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this count, this is for testing, lol
|
fn convert_mem_data(mem_data : &[app::data_collection::mem::MemData]) -> Vec<(f64, f64)> {
|
||||||
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)> {
|
|
||||||
let mut result : Vec<(f64, f64)> = Vec::new();
|
let mut result : Vec<(f64, f64)> = Vec::new();
|
||||||
let current_time = std::time::Instant::now();
|
let current_time = std::time::Instant::now();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user