Added reads and writes for disk.

This commit is contained in:
ClementTsang 2019-09-16 16:18:42 -04:00
parent 6327ba2c7b
commit 43ac5c3399
9 changed files with 242 additions and 104 deletions

View file

@ -18,6 +18,8 @@
## After making public
* Header should be clear on current sorting direction!
* 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...?
@ -42,4 +44,6 @@
* Help screen
* Modularity
* Potentially process managing? Depends on the libraries...

View file

@ -12,19 +12,29 @@ pub enum ApplicationPosition {
PROCESS,
}
pub enum ScrollDirection {
/// UP means scrolling up --- this usually DECREMENTS
UP,
/// DOWN means scrolling down --- this usually INCREMENTS
DOWN,
}
pub struct App {
pub should_quit : bool,
pub process_sorting_type : processes::ProcessSorting,
pub process_sorting_reverse : bool,
pub to_be_resorted : bool,
pub currently_selected_process_position : u64,
pub currently_selected_disk_position : u64,
pub currently_selected_temperature_position : u64,
pub currently_selected_process_position : i64,
pub currently_selected_disk_position : i64,
pub currently_selected_temperature_position : i64,
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?
pub data : data_collection::Data,
pub scroll_direction : ScrollDirection,
pub previous_process_position : i64,
}
impl App {
@ -42,6 +52,9 @@ impl App {
show_average_cpu,
current_application_position : ApplicationPosition::PROCESS,
current_zoom_level_percent : 100.0,
data : data_collection::Data::default(),
scroll_direction : ScrollDirection::DOWN,
previous_process_position : 0,
}
}
@ -62,6 +75,7 @@ impl App {
}
}
self.to_be_resorted = true;
self.currently_selected_process_position = 0;
}
'm' => {
match self.process_sorting_type {
@ -72,6 +86,7 @@ impl App {
}
}
self.to_be_resorted = true;
self.currently_selected_process_position = 0;
}
'p' => {
match self.process_sorting_type {
@ -82,6 +97,7 @@ impl App {
}
}
self.to_be_resorted = true;
self.currently_selected_process_position = 0;
}
'n' => {
match self.process_sorting_type {
@ -92,6 +108,7 @@ impl App {
}
}
self.to_be_resorted = true;
self.currently_selected_process_position = 0;
}
_ => {}
}
@ -110,40 +127,43 @@ impl App {
}
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),
_ => {}
}
self.scroll_direction = ScrollDirection::UP;
}
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;
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),
_ => {}
}
// else if self.currently_selected_process_position < // TODO: Need to finish this! This should never go PAST the number of elements
self.scroll_direction = ScrollDirection::DOWN;
}
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;
fn change_process_position(&mut self, num_to_change_by : i64) {
if self.currently_selected_process_position + num_to_change_by >= 0
&& self.currently_selected_process_position + num_to_change_by < self.data.list_of_processes.len() as i64
{
self.currently_selected_process_position += num_to_change_by;
}
}
fn change_temp_position(&mut self, num_to_change_by : i64) {
if self.currently_selected_temperature_position + num_to_change_by >= 0 {
self.currently_selected_temperature_position += num_to_change_by;
}
// 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;
fn change_disk_position(&mut self, num_to_change_by : i64) {
if self.currently_selected_disk_position + num_to_change_by >= 0 {
self.currently_selected_disk_position += num_to_change_by;
}
// else if self.currently_selected_disk_position < // TODO: Need to finish this! This should never go PAST the number of elements
}

View file

@ -80,6 +80,11 @@ impl DataState {
self.sys.refresh_system();
self.sys.refresh_network();
if !cfg!(target_os = "linux") {
// For now, might be just windows tbh
self.sys.refresh_processes();
}
// What we want to do: For timed data, if there is an error, just do not add. For other data, just don't update!
push_if_valid(&network::get_network_data(&self.sys), &mut self.data.network);
push_if_valid(&cpu::get_cpu_data_list(&self.sys), &mut self.data.list_of_cpu_packages);
@ -94,7 +99,7 @@ impl DataState {
set_if_valid(&disks::get_disk_usage_list().await, &mut self.data.list_of_disks);
push_if_valid(&disks::get_io_usage_list(false).await, &mut self.data.list_of_io);
push_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io);
//push_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io);
set_if_valid(&temperature::get_temperature_data(&self.temperature_type).await, &mut self.data.list_of_temperature_sensor);
if self.first_run {
@ -144,13 +149,13 @@ impl DataState {
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.list_of_physical_io = self
.data
.list_of_physical_io
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
// self.data.list_of_physical_io = self
// .data
// .list_of_physical_io
// .iter()
// .cloned()
// .filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
// .collect::<Vec<_>>();
debug!("End updating...");
}

View file

@ -10,7 +10,7 @@ pub struct DiskData {
pub total_space : u64,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct IOData {
pub mount_point : Box<str>,
pub read_bytes : u64,
@ -19,37 +19,46 @@ pub struct IOData {
#[derive(Clone)]
pub struct IOPackage {
pub io_list : Vec<IOData>,
pub io_hash : std::collections::HashMap<String, IOData>,
pub instant : Instant,
}
// TODO: This is total --- we have to change the calculation to PER SECOND!
pub async fn get_io_usage_list(get_physical : bool) -> Result<IOPackage, heim::Error> {
let mut io_list : Vec<IOData> = Vec::new();
let mut io_hash : std::collections::HashMap<String, IOData> = std::collections::HashMap::new();
if get_physical {
let mut physical_counter_stream = heim::disk::io_counters_physical();
while let Some(io) = physical_counter_stream.next().await {
let io = io?;
io_list.push(IOData {
mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")),
read_bytes : io.read_bytes().get::<heim_common::units::information::megabyte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::megabyte>(),
})
let mount_point = io.device_name().to_str().unwrap_or("Name Unavailable");
io_hash.insert(
mount_point.to_string(),
IOData {
mount_point : Box::from(mount_point),
read_bytes : io.read_bytes().get::<heim_common::units::information::megabyte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::megabyte>(),
},
);
}
}
else {
let mut counter_stream = heim::disk::io_counters();
while let Some(io) = counter_stream.next().await {
let io = io?;
io_list.push(IOData {
mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")),
read_bytes : io.read_bytes().get::<heim_common::units::information::megabyte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::megabyte>(),
})
let mount_point = io.device_name().to_str().unwrap_or("Name Unavailable");
io_hash.insert(
mount_point.to_string(),
IOData {
mount_point : Box::from(mount_point),
read_bytes : io.read_bytes().get::<heim_common::units::information::byte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::byte>(),
},
);
}
}
Ok(IOPackage {
io_list,
io_hash,
instant : Instant::now(),
})
}

View file

@ -196,6 +196,8 @@ pub async fn get_sorted_processes_list(
}
else if cfg!(target_os = "windows") {
// Windows
// TODO: DO NOT USE HEIM!
let mut process_stream = heim::process::processes().map_ok(non_linux_cpu_usage).try_buffer_unordered(std::usize::MAX);
let mut process_vector : Vec<ProcessData> = Vec::new();

View file

@ -27,7 +27,7 @@ pub struct CanvasData {
pub cpu_data : Vec<(String, Vec<(f64, f64)>)>,
}
pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_data : &app::App, canvas_data : &CanvasData) -> error::Result<()> {
pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_state : &mut app::App, canvas_data : &CanvasData) -> error::Result<()> {
let border_style : Style = Style::default().fg(BORDER_STYLE_COLOUR);
let temperature_rows = canvas_data
@ -35,13 +35,9 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
.iter()
.map(|sensor| Row::StyledData(sensor.iter(), Style::default().fg(TEXT_COLOUR)));
let disk_rows = canvas_data.disk_data.iter().map(|disk| Row::StyledData(disk.iter(), Style::default().fg(TEXT_COLOUR)));
let process_rows = canvas_data
.process_data
.iter()
.map(|process| Row::StyledData(process.iter(), Style::default().fg(TEXT_COLOUR)));
terminal.draw(|mut f| {
debug!("Drawing!");
//debug!("Drawing!");
let vertical_chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
@ -51,7 +47,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
let middle_chunks = Layout::default()
.direction(Direction::Horizontal)
.margin(0)
.constraints([Constraint::Percentage(65), Constraint::Percentage(35)].as_ref())
.constraints([Constraint::Percentage(60), Constraint::Percentage(40)].as_ref())
.split(vertical_chunks[1]);
let middle_divided_chunk_2 = Layout::default()
@ -76,7 +72,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
for (i, cpu) in canvas_data.cpu_data.iter().enumerate() {
let mut avg_cpu_exist_offset = 0;
if app_data.show_average_cpu {
if app_state.show_average_cpu {
if i == 0 {
// Skip, we want to render the average cpu last!
continue;
@ -95,7 +91,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
);
}
if !canvas_data.cpu_data.is_empty() && app_data.show_average_cpu {
if !canvas_data.cpu_data.is_empty() && app_state.show_average_cpu {
dataset_vector.push(
Dataset::default()
.name(&canvas_data.cpu_data[0].0)
@ -154,14 +150,13 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
.block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style))
.header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD))
.widths(&[
// 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,
(width * 0.18) as u16,
(width * 0.14) as u16,
(width * 0.13) as u16,
(width * 0.13) as u16,
(width * 0.13) as u16,
(width * 0.13) as u16,
(width * 0.13) as u16,
])
.render(&mut f, middle_divided_chunk_2[1]);
}
@ -192,6 +187,65 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
// Processes table
{
let width = f64::from(bottom_chunks[1].width);
// Admittedly this is kinda a hack... but we need to:
// * Scroll
// * Show/hide elements based on scroll position
// As such, we use a process_counter to know when we've hit the process we've currently scrolled to. We also need to move the list - we can
// do so by hiding some elements!
let num_rows = i64::from(bottom_chunks[1].height) - 3;
let mut process_counter = 0;
//TODO: Fix this!
let start_position = if num_rows <= app_state.currently_selected_process_position {
match app_state.scroll_direction {
app::ScrollDirection::UP => {
if app_state.previous_process_position - app_state.currently_selected_process_position <= num_rows {
// We don't need to scroll up yet...
debug!("No need to scroll up yet...");
app_state.previous_process_position
}
else {
// We need to scroll up!
debug!("Scroll up! Scroll up!");
app_state.previous_process_position = app_state.currently_selected_process_position;
app_state.currently_selected_process_position
}
}
app::ScrollDirection::DOWN => {
app_state.previous_process_position = app_state.currently_selected_process_position - num_rows + 1;
(app_state.currently_selected_process_position - num_rows + 1)
}
}
}
else {
0
};
debug!(
"START POSN: {}, CURRENT SELECTED POSN: {}, NUM ROWS: {}",
start_position, app_state.currently_selected_process_position, num_rows
);
let sliced_vec : Vec<Vec<String>> = (&canvas_data.process_data[start_position as usize..]).to_vec();
let process_rows = sliced_vec.iter().map(|process| {
Row::StyledData(
process.iter(),
if process_counter == app_state.currently_selected_process_position - start_position {
// TODO: This is what controls the highlighting!
process_counter = -1;
Style::default().fg(Color::Black).bg(Color::Cyan)
}
else {
if process_counter >= 0 {
process_counter += 1;
}
Style::default().fg(TEXT_COLOUR)
},
)
});
Table::new(["PID", "Name", "CPU%", "Mem%"].iter(), process_rows)
.block(Block::default().title("Processes").borders(Borders::ALL).border_style(border_style))
.header_style(Style::default().fg(Color::LightBlue))
@ -200,5 +254,7 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, app_dat
}
})?;
//debug!("Finished drawing.");
Ok(())
}

View file

@ -19,10 +19,51 @@ pub fn update_temp_row(app_data : &data_collection::Data, temp_type : &data_coll
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 {
let io_activity = if app_data.list_of_io.len() > 2 {
let io_package = &app_data.list_of_io.last().unwrap();
let prev_io_package = &app_data.list_of_io[app_data.list_of_io.len() - 2];
let io_hashmap = &io_package.io_hash;
let prev_io_hashmap = &prev_io_package.io_hash;
let trimmed_mount = &disk.name.to_string().split('/').last().unwrap().to_string();
let time_difference = io_package.instant.duration_since(prev_io_package.instant).as_secs_f64();
if io_hashmap.contains_key(trimmed_mount) && prev_io_hashmap.contains_key(trimmed_mount) {
// Ideally change this...
let ele = &io_hashmap[trimmed_mount];
let prev = &prev_io_hashmap[trimmed_mount];
let read_bytes_per_sec = ((ele.read_bytes - prev.read_bytes) as f64 / time_difference) as u64;
let write_bytes_per_sec = ((ele.write_bytes - prev.write_bytes) as f64 / time_difference) as u64;
(
if read_bytes_per_sec < 1024 {
format!("{}B", read_bytes_per_sec)
}
else if read_bytes_per_sec < 1024 * 1024 {
format!("{}KB", read_bytes_per_sec / 1024)
}
else {
format!("{}MB", read_bytes_per_sec / 1024 / 1024)
},
if write_bytes_per_sec < 1024 {
format!("{}B", write_bytes_per_sec)
}
else if write_bytes_per_sec < 1024 * 1024 {
format!("{}KB", write_bytes_per_sec / 1024)
}
else {
format!("{}MB", write_bytes_per_sec / 1024 / 1024)
},
)
}
else {
("0B".to_string(), "0B".to_string())
}
}
else {
("0B".to_string(), "0B".to_string())
};
disk_vector.push(vec![
disk.name.to_string(),
disk.mount_point.to_string(),
@ -39,6 +80,8 @@ pub fn update_disk_row(app_data : &data_collection::Data) -> Vec<Vec<String>> {
else {
(disk.total_space / 1024).to_string() + "GB"
},
io_activity.0,
io_activity.1,
]);
}
@ -215,8 +258,8 @@ pub fn convert_network_data_points(network_data : &[data_collection::network::Ne
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);
//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() {

View file

@ -113,7 +113,6 @@ fn main() -> error::Result<()> {
_ => {}
}
}
input.disable_mouse_mode().unwrap();
});
}
@ -142,14 +141,12 @@ fn main() -> error::Result<()> {
});
}
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::KeyInput(event) => {
debug!("Keyboard event fired!");
// debug!("Keyboard event fired!");
match event {
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?
@ -161,55 +158,46 @@ fn main() -> error::Result<()> {
}
if app.to_be_resorted {
data_collection::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse);
canvas_data.process_data = update_process_row(&app_data);
data_collection::processes::sort_processes(&mut app.data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse);
canvas_data.process_data = update_process_row(&app.data);
app.to_be_resorted = false;
}
debug!("Input event complete.");
// debug!("Input event complete.");
}
Event::MouseInput(event) => {
debug!("Mouse event fired!");
// 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::Press(e, _x, _y) => match e {
MouseButton::WheelUp => {
app.decrement_position_count();
}
}
MouseEvent::Hold(_x, _y) => {
debug!("Mouse hold!");
}
MouseEvent::Release(_x, _y) => {
debug!("Mouse release!");
}
_ => {
debug!("Mouse unknown event...");
}
MouseButton::WheelDown => {
app.increment_position_count();
}
_ => {}
},
MouseEvent::Hold(_x, _y) => {}
MouseEvent::Release(_x, _y) => {}
_ => {}
}
}
Event::Update(data) => {
debug!("Update event fired!");
app_data = *data;
data_collection::processes::sort_processes(&mut app_data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse);
// debug!("Update event fired!");
app.data = *data;
data_collection::processes::sort_processes(&mut app.data.list_of_processes, &app.process_sorting_type, app.process_sorting_reverse);
// Convert all data into tui components
let network_data = update_network_data_points(&app_data);
let network_data = update_network_data_points(&app.data);
canvas_data.network_data_rx = network_data.rx;
canvas_data.network_data_tx = network_data.tx;
canvas_data.rx_display = network_data.rx_display;
canvas_data.tx_display = network_data.tx_display;
canvas_data.disk_data = update_disk_row(&app_data);
canvas_data.temp_sensor_data = update_temp_row(&app_data, &app.temperature_type);
canvas_data.process_data = update_process_row(&app_data);
canvas_data.mem_data = update_mem_data_points(&app_data);
canvas_data.swap_data = update_swap_data_points(&app_data);
canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app_data);
canvas_data.disk_data = update_disk_row(&app.data);
canvas_data.temp_sensor_data = update_temp_row(&app.data, &app.temperature_type);
canvas_data.process_data = update_process_row(&app.data);
canvas_data.mem_data = update_mem_data_points(&app.data);
canvas_data.swap_data = update_swap_data_points(&app.data);
canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app.data);
debug!("Update event complete.");
}
@ -219,9 +207,14 @@ fn main() -> error::Result<()> {
}
}
// Draw!
canvas::draw_data(&mut terminal, &app, &canvas_data)?;
if let Err(err) = canvas::draw_data(&mut terminal, &mut app, &canvas_data) {
input().disable_mouse_mode().unwrap();
error!("{}", err);
return Err(err);
}
}
input().disable_mouse_mode().unwrap();
debug!("Terminating.");
Ok(())
}

View file

@ -10,7 +10,13 @@ pub fn init_logger() -> Result<(), fern::InitError> {
))
})
.level(if cfg!(debug_assertions) { log::LevelFilter::Debug } else { log::LevelFilter::Info })
.chain(fern::log_file("debug.log")?)
.chain(if cfg!(debug_assertions) {
//std::fs::OpenOptions::new().write(true).create(true).append(false).open("debug.log")?
fern::log_file("debug.log")?
}
else {
fern::log_file("logging.log")?
})
.apply()?;
Ok(())