Added average cpu option.

This commit is contained in:
ClementTsang 2019-09-14 17:07:18 -04:00
parent 6d9ed34dcb
commit b14432c3df
5 changed files with 186 additions and 172 deletions

View file

@ -1,8 +1,5 @@
pub mod data_collection;
use data_collection::{cpu, disks, mem, network, processes, temperature};
use std::collections::HashMap;
use sysinfo::{System, SystemExt};
use data_collection::{processes, temperature};
#[allow(dead_code)]
pub struct App {
@ -11,167 +8,22 @@ pub struct App {
pub process_sorting_reverse : bool,
pub to_be_resorted : bool,
pub current_selected_process_position : u64,
pub temperature_type : data_collection::temperature::TemperatureType,
pub temperature_type : temperature::TemperatureType,
pub update_rate_in_milliseconds : u64,
}
fn set_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, value_to_set : &mut T) {
if let Ok(result) = result {
*value_to_set = (*result).clone();
}
}
fn push_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, vector_to_push : &mut Vec<T>) {
if let Ok(result) = result {
vector_to_push.push(result.clone());
}
}
#[derive(Default, Clone)]
pub struct Data {
pub list_of_cpu_packages : Vec<cpu::CPUPackage>,
pub list_of_io : Vec<disks::IOPackage>,
pub list_of_physical_io : Vec<disks::IOPackage>,
pub memory : Vec<mem::MemData>,
pub swap : Vec<mem::MemData>,
pub list_of_temperature_sensor : Vec<temperature::TempData>,
pub network : Vec<network::NetworkData>,
pub list_of_processes : Vec<processes::ProcessData>, // Only need to keep a list of processes...
pub list_of_disks : Vec<disks::DiskData>, // Only need to keep a list of disks and their data
}
pub struct DataState {
pub data : Data,
first_run : bool,
sys : System,
stale_max_seconds : u64,
prev_pid_stats : HashMap<String, f64>, // TODO: Purge list?
prev_idle : f64,
prev_non_idle : f64,
temperature_type : data_collection::temperature::TemperatureType,
}
impl Default for DataState {
fn default() -> Self {
DataState {
data : Data::default(),
first_run : true,
sys : System::new(),
stale_max_seconds : 60,
prev_pid_stats : HashMap::new(),
prev_idle : 0_f64,
prev_non_idle : 0_f64,
temperature_type : data_collection::temperature::TemperatureType::Celsius,
}
}
}
impl DataState {
pub fn set_stale_max_seconds(&mut self, stale_max_seconds : u64) {
self.stale_max_seconds = stale_max_seconds;
}
pub fn set_temperature_type(&mut self, temperature_type : data_collection::temperature::TemperatureType) {
self.temperature_type = temperature_type;
}
pub fn init(&mut self) {
self.sys.refresh_system();
self.sys.refresh_network();
}
pub async fn update_data(&mut self) {
debug!("Start updating...");
self.sys.refresh_system();
self.sys.refresh_network();
// 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);
// TODO: We can convert this to a multi-threaded task...
push_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory);
push_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap);
set_if_valid(
&processes::get_sorted_processes_list(&mut self.prev_idle, &mut self.prev_non_idle, &mut self.prev_pid_stats).await,
&mut self.data.list_of_processes,
);
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);
set_if_valid(&temperature::get_temperature_data(&self.temperature_type).await, &mut self.data.list_of_temperature_sensor);
if self.first_run {
self.data = Data::default();
self.first_run = false;
}
// Filter out stale timed entries
// TODO: ideally make this a generic function!
let current_instant = std::time::Instant::now();
self.data.list_of_cpu_packages = self
.data
.list_of_cpu_packages
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.memory = self
.data
.memory
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.swap = self
.data
.swap
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.network = self
.data
.network
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.list_of_io = self
.data
.list_of_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...");
}
pub show_average_cpu : bool,
}
impl App {
pub fn new(temperature_type : data_collection::temperature::TemperatureType, update_rate_in_milliseconds : u64) -> App {
pub fn new(show_average_cpu : bool, temperature_type : temperature::TemperatureType, update_rate_in_milliseconds : u64) -> App {
App {
process_sorting_type : processes::ProcessSorting::CPU, // TODO: Change this based on input args... basically set this on app creation
process_sorting_type : processes::ProcessSorting::CPU,
should_quit : false,
process_sorting_reverse : true,
to_be_resorted : false,
current_selected_process_position : 0,
temperature_type,
update_rate_in_milliseconds,
show_average_cpu,
}
}

View file

@ -1,6 +1,156 @@
use std::collections::HashMap;
use sysinfo::{System, SystemExt};
pub mod cpu;
pub mod disks;
pub mod mem;
pub mod network;
pub mod processes;
pub mod temperature;
fn set_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, value_to_set : &mut T) {
if let Ok(result) = result {
*value_to_set = (*result).clone();
}
}
fn push_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, vector_to_push : &mut Vec<T>) {
if let Ok(result) = result {
vector_to_push.push(result.clone());
}
}
#[derive(Default, Clone)]
pub struct Data {
pub list_of_cpu_packages : Vec<cpu::CPUPackage>,
pub list_of_io : Vec<disks::IOPackage>,
pub list_of_physical_io : Vec<disks::IOPackage>,
pub memory : Vec<mem::MemData>,
pub swap : Vec<mem::MemData>,
pub list_of_temperature_sensor : Vec<temperature::TempData>,
pub network : Vec<network::NetworkData>,
pub list_of_processes : Vec<processes::ProcessData>, // Only need to keep a list of processes...
pub list_of_disks : Vec<disks::DiskData>, // Only need to keep a list of disks and their data
}
pub struct DataState {
pub data : Data,
first_run : bool,
sys : System,
stale_max_seconds : u64,
prev_pid_stats : HashMap<String, f64>, // TODO: Purge list?
prev_idle : f64,
prev_non_idle : f64,
temperature_type : temperature::TemperatureType,
}
impl Default for DataState {
fn default() -> Self {
DataState {
data : Data::default(),
first_run : true,
sys : System::new(),
stale_max_seconds : 60,
prev_pid_stats : HashMap::new(),
prev_idle : 0_f64,
prev_non_idle : 0_f64,
temperature_type : temperature::TemperatureType::Celsius,
}
}
}
impl DataState {
pub fn set_stale_max_seconds(&mut self, stale_max_seconds : u64) {
self.stale_max_seconds = stale_max_seconds;
}
pub fn set_temperature_type(&mut self, temperature_type : temperature::TemperatureType) {
self.temperature_type = temperature_type;
}
pub fn init(&mut self) {
self.sys.refresh_system();
self.sys.refresh_network();
}
pub async fn update_data(&mut self) {
debug!("Start updating...");
self.sys.refresh_system();
self.sys.refresh_network();
// 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);
// TODO: We can convert this to a multi-threaded task...
push_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory);
push_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap);
set_if_valid(
&processes::get_sorted_processes_list(&mut self.prev_idle, &mut self.prev_non_idle, &mut self.prev_pid_stats).await,
&mut self.data.list_of_processes,
);
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);
set_if_valid(&temperature::get_temperature_data(&self.temperature_type).await, &mut self.data.list_of_temperature_sensor);
if self.first_run {
self.data = Data::default();
self.first_run = false;
}
// Filter out stale timed entries
// TODO: ideally make this a generic function!
let current_instant = std::time::Instant::now();
self.data.list_of_cpu_packages = self
.data
.list_of_cpu_packages
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.memory = self
.data
.memory
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.swap = self
.data
.swap
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.network = self
.data
.network
.iter()
.cloned()
.filter(|entry| current_instant.duration_since(entry.instant).as_secs() <= self.stale_max_seconds)
.collect::<Vec<_>>();
self.data.list_of_io = self
.data
.list_of_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

@ -13,6 +13,12 @@ pub enum ProcessSorting {
NAME,
}
impl Default for ProcessSorting {
fn default() -> Self {
ProcessSorting::CPU
}
}
// Possible process info struct?
#[derive(Clone, Default)]
pub struct ProcessData {

View file

@ -13,6 +13,12 @@ pub enum TemperatureType {
Fahrenheit,
}
impl Default for TemperatureType {
fn default() -> Self {
TemperatureType::Celsius
}
}
pub async fn get_temperature_data(temp_type : &TemperatureType) -> Result<Vec<TempData>, heim::Error> {
let mut temperature_vec : Vec<TempData> = Vec::new();

View file

@ -21,7 +21,7 @@ extern crate clap;
enum Event<I> {
Input(I),
Update(Box<app::Data>),
Update(Box<app::data_collection::Data>),
}
const STALE_MAX_MILLISECONDS : u64 = 60 * 1000;
@ -37,11 +37,11 @@ async fn main() -> Result<(), io::Error> {
(author: "Clement Tsang <clementjhtsang@gmail.com>")
(about: "A graphical top clone.")
(@arg THEME: -t --theme +takes_value "Sets a colour theme.")
(@arg AVG_CPU: -a --avgcpu "Enables showing the average CPU usage.")
(@group TEMPERATURE_TYPE =>
(@arg celsius : -c --celsius "Sets the temperature type to Celsius. This is the default option.")
(@arg fahrenheit : -f --fahrenheit "Sets the temperature type to Fahrenheit.")
(@arg kelvin : -k --kelvin "Sets the temperature type to Kelvin.")
)
(@arg RATE: -r --rate +takes_value "Sets a refresh rate in milliseconds, min is 250ms, defaults to 1000ms. Higher values may take more resources.")
)
@ -62,10 +62,9 @@ async fn main() -> Result<(), io::Error> {
else {
app::data_collection::temperature::TemperatureType::Celsius
};
let show_average_cpu = matches.is_present("AVG_CPU");
info!("Temperature type: {:?}", temperature_type);
let mut app = app::App::new(temperature_type, if update_rate_in_milliseconds < 250 { 250 } else { update_rate_in_milliseconds });
let mut app = app::App::new(show_average_cpu, temperature_type, if update_rate_in_milliseconds < 250 { 250 } else { update_rate_in_milliseconds });
terminal.hide_cursor()?;
// Setup input handling
@ -86,7 +85,7 @@ async fn main() -> Result<(), io::Error> {
}
// Event loop
let mut data_state = app::DataState::default();
let mut data_state = app::data_collection::DataState::default();
data_state.init();
data_state.set_stale_max_seconds(STALE_MAX_MILLISECONDS);
data_state.set_temperature_type(app.temperature_type.clone());
@ -104,7 +103,7 @@ async fn main() -> Result<(), io::Error> {
terminal.clear()?;
let mut app_data = app::Data::default();
let mut app_data = app::data_collection::Data::default();
let mut canvas_data = canvas::CanvasData::default();
loop {
@ -140,7 +139,7 @@ async fn main() -> Result<(), io::Error> {
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_data);
canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app_data);
debug!("Update event complete.");
}
@ -156,7 +155,7 @@ async fn main() -> Result<(), io::Error> {
Ok(())
}
fn update_temp_row(app_data : &app::Data, temp_type : &app::data_collection::temperature::TemperatureType) -> Vec<Vec<String>> {
fn update_temp_row(app_data : &app::data_collection::Data, temp_type : &app::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 {
@ -174,7 +173,7 @@ fn update_temp_row(app_data : &app::Data, temp_type : &app::data_collection::tem
sensor_vector
}
fn update_disk_row(app_data : &app::Data) -> Vec<Vec<String>> {
fn update_disk_row(app_data : &app::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![
@ -189,7 +188,7 @@ fn update_disk_row(app_data : &app::Data) -> Vec<Vec<String>> {
disk_vector
}
fn update_process_row(app_data : &app::Data) -> Vec<Vec<String>> {
fn update_process_row(app_data : &app::data_collection::Data) -> Vec<Vec<String>> {
let mut process_vector : Vec<Vec<String>> = Vec::new();
for process in &app_data.list_of_processes {
@ -220,15 +219,15 @@ fn update_process_row(app_data : &app::Data) -> Vec<Vec<String>> {
process_vector
}
fn update_cpu_data_points(app_data : &app::Data) -> Vec<(String, Vec<(f64, f64)>)> {
fn update_cpu_data_points(show_avg_cpu : bool, app_data : &app::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() {
// Initially, populate the cpu_collection. We want to inject elements in between if possible.
for cpu_num in 1..app_data.list_of_cpu_packages.last().unwrap().cpu_vec.len() {
// TODO: 1 to skip total cpu? Or no?
// 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 {
@ -246,8 +245,9 @@ fn update_cpu_data_points(app_data : &app::Data) -> Vec<(String, Vec<(f64, f64)>
// Finally, add it all onto the end
for (i, data) in cpu_collection.iter().enumerate() {
cpu_data_vector.push((
// + 1 to skip total CPU...
(&*(app_data.list_of_cpu_packages.last().unwrap().cpu_vec[i + 1].cpu_name)).to_string() + " " + &format!("{:3}%", (data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)),
// + 1 to skip total CPU if show_avg_cpu is false
(&*(app_data.list_of_cpu_packages.last().unwrap().cpu_vec[i + if show_avg_cpu { 0 } else { 1 }].cpu_name)).to_string()
+ " " + &format!("{:3}%", (data.last().unwrap_or(&(0_f64, 0_f64)).1.round() as u64)),
data.clone(),
))
}
@ -256,11 +256,11 @@ fn update_cpu_data_points(app_data : &app::Data) -> Vec<(String, Vec<(f64, f64)>
cpu_data_vector
}
fn update_mem_data_points(app_data : &app::Data) -> Vec<(f64, f64)> {
fn update_mem_data_points(app_data : &app::data_collection::Data) -> Vec<(f64, f64)> {
convert_mem_data(&app_data.memory)
}
fn update_swap_data_points(app_data : &app::Data) -> Vec<(f64, f64)> {
fn update_swap_data_points(app_data : &app::data_collection::Data) -> Vec<(f64, f64)> {
convert_mem_data(&app_data.swap)
}