Draw historic CPU usage.

Version spark library and extend.
This commit is contained in:
Bill Thiede 2019-09-22 09:57:32 -07:00
parent dcc1fdcd83
commit 6b2200d640
6 changed files with 100 additions and 51 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -1 +1,2 @@
pub mod spark;
pub mod widgets;

View File

@ -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<f32>,
pub max: Option<f32>,
}
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()
}
}

View File

@ -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<f32>,
}
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<String> {
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::<Vec<f32>>()))
} else {
Some("N/A".to_string())
}
}
fn draw_bar(&mut self) -> Option<String> {
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<WidgetUpdate> {
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

View File

@ -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::<Vec<f32>>()),
spark::graph(&self.tx_history.iter().cloned().collect::<Vec<f32>>()),
g.render(&self.rx_history.iter().cloned().collect::<Vec<f32>>()),
g.render(&self.tx_history.iter().cloned().collect::<Vec<f32>>()),
)
} else {
("".to_string(), "".to_string())
};
data.use_pango();
data.append_full_text(&format!(
"Rx:<tt>{}</tt>{} Tx:<tt>{}</tt>{}",
"Rx:<tt>{} {}</tt> Tx:<tt>{} {}</tt>",
rx, rx_history, tx, tx_history
));
return Some(WidgetUpdate {