Draw historic CPU usage.

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

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 {