From 6b2200d64045461edee3d2bed7a86f0a6d1b65fb Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 22 Sep 2019 09:57:32 -0700 Subject: [PATCH] Draw historic CPU usage. Version spark library and extend. --- Cargo.lock | 7 ----- Cargo.toml | 1 - src/lib.rs | 1 + src/spark.rs | 65 +++++++++++++++++++++++------------------- src/widgets/cpu.rs | 65 +++++++++++++++++++++++++++++++++++------- src/widgets/network.rs | 12 +++++--- 6 files changed, 100 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd37ee5..2fe1834 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,6 @@ dependencies = [ "chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "i3monkit 0.1.2 (git+https://github.com/38/i3monkit)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "spark 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -364,11 +363,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "spark" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "structopt" version = "0.2.18" @@ -523,7 +517,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" -"checksum spark 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdfdbc4ce5316afaa9023d28ed77019bc0860e68e6e5857643a14071520347cb" "checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" "checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" diff --git a/Cargo.toml b/Cargo.toml index ac2b41c..74a88bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,3 @@ num_cpus = "1.0" structopt = { version = "0.2", default-features = false } chrono = "0.4" chrono-tz = "0.5" -spark = "0.4.0" diff --git a/src/lib.rs b/src/lib.rs index 42e1b6d..90f89c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ +pub mod spark; pub mod widgets; diff --git a/src/spark.rs b/src/spark.rs index 1392628..771658d 100644 --- a/src/spark.rs +++ b/src/spark.rs @@ -12,33 +12,40 @@ use std::f32::MAX as f32_max; /// let sparkline = spark::graph(&[1.0, 5.0, 22.0, 13.0, 53.0]); /// assert_eq!(sparkline, "▁▁▃▂█"); /// ``` -pub fn graph(values: &[f32]) -> String { - let ticks = "▁▂▃▄▅▆▇█"; - - /* XXX: This doesn't feel like idiomatic Rust */ - let mut min: f32 = f32_max; - let mut max: f32 = 0.0; - - for &i in values.iter() { - if i > max { - max = i; - } - if i < min { - min = i; - } - } - - let ratio = if max == min { - 1.0 - } else { - (ticks.chars().count() - 1) as f32 / (max - min) - }; - - values - .iter() - .cloned() - .map(|n| (n - min) * ratio) - .map(|n| n.floor() as usize) - .filter_map(|n| ticks.chars().nth(n)) - .collect() +pub struct Graph { + pub min: Option, + pub max: Option, +} + +impl Graph { + pub fn render(&self, values: &[f32]) -> String { + let ticks = "▁▂▃▄▅▆▇█"; + + /* XXX: This doesn't feel like idiomatic Rust */ + let mut min: f32 = self.min.unwrap_or(f32_max); + let mut max: f32 = self.max.unwrap_or(0.); + + for &i in values.iter() { + if i > max { + max = i; + } + if i < min { + min = i; + } + } + + let ratio = if max == min { + 1.0 + } else { + (ticks.chars().count() - 1) as f32 / (max - min) + }; + + values + .iter() + .cloned() + .map(|n| (n - min) * ratio) + .map(|n| n.floor() as usize) + .filter_map(|n| ticks.chars().nth(n)) + .collect() + } } diff --git a/src/widgets/cpu.rs b/src/widgets/cpu.rs index 32f5a34..8851644 100644 --- a/src/widgets/cpu.rs +++ b/src/widgets/cpu.rs @@ -1,8 +1,11 @@ +use std::collections::vec_deque::VecDeque; +use std::fs::File; +use std::io::{BufRead, BufReader, Result}; + use i3monkit::Block; use i3monkit::{Widget, WidgetUpdate}; -use std::fs::File; -use std::io::{BufRead, BufReader, Result}; +use crate::spark; /// The CPU usage widget /// @@ -17,6 +20,7 @@ pub struct CpuWidget { user_color: String, nice_color: String, system_color: String, + history: VecDeque, } impl CpuWidget { @@ -47,6 +51,7 @@ impl CpuWidget { /// **id** The core id pub fn new(id: u32) -> Self { let (user, nice, system, idel) = Self::read_status(id).unwrap(); + let num_samples = 6; let ret = Self { id, user, @@ -57,11 +62,37 @@ impl CpuWidget { user_color: "#00ff00".to_string(), nice_color: "#0000ff".to_string(), system_color: "#ff0000".to_string(), + history: (0..num_samples).map(|_| 0.).collect(), }; return ret; } + fn draw_history(&mut self) -> Option { + let (user, nice, system, idel) = Self::read_status(self.id).ok()?; + let used_diff = (user + nice + system) - (self.user + self.nice + self.system) + 1; + let total_diff = + (user + nice + system + idel) - (self.user + self.nice + self.system + self.idel) + 1; + + self.nice = nice; + self.user = user; + self.idel = idel; + self.system = system; + + let percent = used_diff as f32 / total_diff as f32; + self.history.push_back(percent); + self.history.pop_front(); + if self.history.len() > 0 { + let g = spark::Graph { + min: Some(0.), + max: Some(1.), + }; + Some(g.render(&self.history.iter().cloned().collect::>())) + } else { + Some("N/A".to_string()) + } + } + fn draw_bar(&mut self) -> Option { let mut ret = Vec::new(); for _ in 0..self.width { @@ -102,16 +133,30 @@ impl CpuWidget { impl Widget for CpuWidget { fn update(&mut self) -> Option { - if let Some(bar) = self.draw_bar() { - let mut data = Block::new(); + if self.history.len() > 0 { + if let Some(history) = self.draw_history() { + let mut data = Block::new(); - data.use_pango(); - data.append_full_text(&format!("{}[{}]", self.id + 1, bar)); + data.use_pango(); + data.append_full_text(&format!("{}: {}", self.id + 1, history)); - return Some(WidgetUpdate { - refresh_interval: std::time::Duration::new(1, 0), - data: Some(data), - }); + return Some(WidgetUpdate { + refresh_interval: std::time::Duration::new(1, 0), + data: Some(data), + }); + } + } else { + if let Some(bar) = self.draw_bar() { + let mut data = Block::new(); + + data.use_pango(); + data.append_full_text(&format!("{}[{}]", self.id + 1, bar)); + + return Some(WidgetUpdate { + refresh_interval: std::time::Duration::new(1, 0), + data: Some(data), + }); + } } None diff --git a/src/widgets/network.rs b/src/widgets/network.rs index 73638f6..a6339aa 100644 --- a/src/widgets/network.rs +++ b/src/widgets/network.rs @@ -7,7 +7,7 @@ use std::io::{BufRead, BufReader, Error, ErrorKind, Result}; use std::path::PathBuf; use std::time::SystemTime; -use spark; +use crate::spark; const NETWORK_PATH_PREFIX: &'static str = "/sys/class/net"; const NETWORK_STAT_SUFFIX: &'static str = "statistics/dummy"; @@ -139,16 +139,20 @@ impl Widget for NetworkSpeedWidget { if let Ok((rx, tx)) = self.get_human_readable_stat() { let mut data = Block::new(); let (rx_history, tx_history) = if self.rx_history.len() > 1 { + let g = spark::Graph { + min: None, + max: None, + }; ( - spark::graph(&self.rx_history.iter().cloned().collect::>()), - spark::graph(&self.tx_history.iter().cloned().collect::>()), + g.render(&self.rx_history.iter().cloned().collect::>()), + g.render(&self.tx_history.iter().cloned().collect::>()), ) } else { ("".to_string(), "".to_string()) }; data.use_pango(); data.append_full_text(&format!( - "Rx:{}{} Tx:{}{}", + "Rx:{} {} Tx:{} {}", rx, rx_history, tx, tx_history )); return Some(WidgetUpdate {