diff --git a/Cargo.lock b/Cargo.lock index dee2df1..28b312c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1393,6 +1393,7 @@ dependencies = [ "draw", "imageproc", "protocol", + "regex", ] [[package]] diff --git a/draw/src/stacked.rs b/draw/src/stacked.rs index aa55412..5540599 100644 --- a/draw/src/stacked.rs +++ b/draw/src/stacked.rs @@ -17,7 +17,7 @@ pub struct StackedSeriesConfig { pub struct StackedSeries<'a> { pub config: &'a StackedConfig, - pub data: Vec<&'a Vec>, + pub data: &'a Vec>, } pub fn stacked(graph: StackedSeries) -> RgbImage { @@ -31,19 +31,18 @@ pub fn stacked(graph: StackedSeries) -> RgbImage { } /// return scale factor to scale stacked series sum to range 0..1 -fn get_scale(series: &Vec<&Vec>) -> f32 { - let len = series.iter().map(|s| s.len()).max().unwrap_or(0); - - let max = (0..len) - .map(|i| series.iter().map(|s| s[i]).sum::()) - .max_by(|f1, f2| f1.total_cmp(f2)) +fn get_scale(series: &Vec>) -> f32 { + let max = series + .iter() + .map(|s| s.iter().sum()) + .reduce(f32::max) .unwrap_or(1f32); - 1f32 / max + if max > 1f32 { 1f32 / max } else { 1f32 } } fn draw_scaled_graph(image: &mut RgbImage, graph: &StackedSeries) { - let scale = get_scale(&graph.data); + let scale = get_scale(graph.data); let colors = graph .config .series @@ -52,33 +51,23 @@ fn draw_scaled_graph(image: &mut RgbImage, graph: &StackedSeries) { .collect::>>(); let mut x_offset = 0f32; - let mut series_idx = 0usize; - let rows = graph.data.len(); - let cols = graph.data[0].len(); - let transposed_series: Vec> = (0..cols) - .map(|col| (0..rows).map(|row| graph.data[row][col]).collect()) - .collect(); - - for timeseries in transposed_series { + for point_in_time in graph.data { let mut y_offset = graph.config.height_px as f32; - for sample in timeseries.iter() { - println!("VAL {}", sample); + let mut series_idx = 0usize; + for sample in point_in_time.iter() { let color = colors[series_idx]; let height_px = (graph.config.height_px as f32) * sample * scale; - let rect = Rect::at(x_offset as i32, (y_offset - height_px) as i32) - .of_size(graph.config.width_px_per_sample, height_px as u32); - draw_filled_rect_mut(image, rect, color); - x_offset += graph.config.width_px_per_sample as f32; - y_offset -= height_px; - println!("rect: {:?}, height {}, yoff {}", rect, height_px, y_offset); + if height_px > 1f32 { + let rect = Rect::at(x_offset as i32, (y_offset - height_px) as i32) + .of_size(graph.config.width_px_per_sample, height_px as u32); + draw_filled_rect_mut(image, rect, color); + y_offset -= height_px; + } + series_idx += 1; } - - println!("BRK"); - - // FIXME: - // series_idx += 1; + x_offset += graph.config.width_px_per_sample as f32; } } @@ -93,7 +82,6 @@ mod test { vec![0., 1., 0., 0., 0.], vec![0., 0., 0., 0., 5.], ]; - assert_eq!(0, 1); // FIX - // assert_eq!(get_scale(&conf), 0.1); + assert_eq!(get_scale(&conf), 0.1); } } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index afd8956..94c1ffc 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -2,4 +2,5 @@ mod err; mod message; mod protocol; +pub use err::ProtocolError; pub use protocol::Protocol; diff --git a/protocol/src/protocol.rs b/protocol/src/protocol.rs index 4c21d71..22eea4c 100644 --- a/protocol/src/protocol.rs +++ b/protocol/src/protocol.rs @@ -1,6 +1,8 @@ use std::io::{Read, Write}; use std::os::unix::net::UnixStream; +use regex::Regex; + use crate::err::ProtocolError; use crate::message::{ create_list_message, create_request_message, parse_list_response, parse_response_header, diff --git a/waybar-collectd/Cargo.toml b/waybar-collectd/Cargo.toml index 99a4f68..76575d2 100644 --- a/waybar-collectd/Cargo.toml +++ b/waybar-collectd/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" draw = { version = "0.1.0", path = "../draw" } imageproc = { version = "0.26.1", features = ["display-window"] } protocol = { path = "../protocol" } +regex = "1.12.3" diff --git a/waybar-collectd/src/main.rs b/waybar-collectd/src/main.rs index fa131b0..bc7ee81 100644 --- a/waybar-collectd/src/main.rs +++ b/waybar-collectd/src/main.rs @@ -1,36 +1,36 @@ +mod observables; + use draw::{StackedConfig, StackedSeries, StackedSeriesConfig, stacked}; use imageproc::window::display_image; +use observables::CpuObservable; use protocol::Protocol; fn main() { let cfg = StackedConfig { width_px_per_sample: 4, height_px: 100, - series: vec![StackedSeriesConfig { color: (255, 0, 0) }], + series: vec![ + StackedSeriesConfig { color: (255, 0, 0) }, + StackedSeriesConfig { color: (0, 255, 0) }, + StackedSeriesConfig { color: (0, 0, 255) }, + ], max_samples: 10, }; if let Ok(mut proto) = Protocol::new("/var/run/collectd-unixsock") { - let mut data: Vec = vec![]; + let mut data: Vec> = vec![]; // loop { + let cpu = CpuObservable::discover(&mut proto).unwrap(); for _ in 1..10 { - let rec = proto.get( - "beerpowered/cpu-0/cpu-idle".to_string(), - "value".to_string(), - ); - if let Ok(val) = rec { - data.push(val); - } else { - println!("{}", rec.unwrap_err()); - } - if data.len() > 10 { data.pop(); } + let sample = cpu.sample(&mut proto).unwrap(); + data.push(sample); let img = stacked(StackedSeries { config: &cfg, - data: vec![&data], + data: &data, }); display_image("bzn", &img, 500, 500); } diff --git a/waybar-collectd/src/observables.rs b/waybar-collectd/src/observables.rs new file mode 100644 index 0000000..4ae9941 --- /dev/null +++ b/waybar-collectd/src/observables.rs @@ -0,0 +1,55 @@ +use protocol::{Protocol, ProtocolError}; +use regex::Regex; + +const CPU_NORM: f32 = 100.; + +pub struct CpuObservable { + pub name: String, + num_cpus: u8, + wait_cpus: Vec, + interrupt_cpus: Vec, + load_cpus: Vec, + idle_cpus: Vec, +} + +impl CpuObservable { + pub fn discover(proto: &mut Protocol) -> Result { + let wait_cpus = proto.list(&Regex::new(".*\\/cpu-[0-9]+\\/cpu-wait").unwrap())?; + let interrupt_cpus = + proto.list(&Regex::new(".*\\/cpu-[0-9]+\\/cpu-(softirq|interrupt)").unwrap())?; + let load_cpus = proto.list(&Regex::new(".*\\/cpu-[0-9]+\\/cpu-.+").unwrap())?; + let idle_cpus = proto.list(&Regex::new(".*\\/cpu-[0-9]+\\/cpu-idle").unwrap())?; + + let num_cpus = wait_cpus.iter().len(); + + Ok(CpuObservable { + name: "cpu".into(), + num_cpus: num_cpus as u8, + wait_cpus: wait_cpus, + interrupt_cpus: interrupt_cpus, + load_cpus: load_cpus, + idle_cpus: idle_cpus, + }) + } + + pub fn sample(&self, proto: &mut Protocol) -> Result, ProtocolError> { + let load = self.sample_fields(&self.load_cpus, proto)?; + let iowait = self.sample_fields(&self.wait_cpus, proto)?; + let interrupts = self.sample_fields(&self.interrupt_cpus, proto)?; + let idle = self.sample_fields(&self.idle_cpus, proto)?; + + Ok(vec![load - iowait - interrupts - idle, iowait, interrupts]) + } + + fn sample_fields( + &self, + fields: &Vec, + proto: &mut Protocol, + ) -> Result { + let results = fields + .iter() + .map(|f| proto.get(f.into(), "value".to_string())) + .collect::, ProtocolError>>()?; + Ok(results.into_iter().sum::() / self.num_cpus as f32 / CPU_NORM) + } +}