Draw historic CPU usage.
Version spark library and extend.
This commit is contained in:
@@ -1 +1,2 @@
|
||||
pub mod spark;
|
||||
pub mod widgets;
|
||||
|
||||
65
src/spark.rs
65
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<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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user