add cpu observable
This commit is contained in:
parent
8e42d763fe
commit
825e4327c4
|
|
@ -1393,6 +1393,7 @@ dependencies = [
|
||||||
"draw",
|
"draw",
|
||||||
"imageproc",
|
"imageproc",
|
||||||
"protocol",
|
"protocol",
|
||||||
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ pub struct StackedSeriesConfig {
|
||||||
|
|
||||||
pub struct StackedSeries<'a> {
|
pub struct StackedSeries<'a> {
|
||||||
pub config: &'a StackedConfig,
|
pub config: &'a StackedConfig,
|
||||||
pub data: Vec<&'a Vec<f32>>,
|
pub data: &'a Vec<Vec<f32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stacked(graph: StackedSeries) -> RgbImage {
|
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
|
/// return scale factor to scale stacked series sum to range 0..1
|
||||||
fn get_scale(series: &Vec<&Vec<f32>>) -> f32 {
|
fn get_scale(series: &Vec<Vec<f32>>) -> f32 {
|
||||||
let len = series.iter().map(|s| s.len()).max().unwrap_or(0);
|
let max = series
|
||||||
|
.iter()
|
||||||
let max = (0..len)
|
.map(|s| s.iter().sum())
|
||||||
.map(|i| series.iter().map(|s| s[i]).sum::<f32>())
|
.reduce(f32::max)
|
||||||
.max_by(|f1, f2| f1.total_cmp(f2))
|
|
||||||
.unwrap_or(1f32);
|
.unwrap_or(1f32);
|
||||||
|
|
||||||
1f32 / max
|
if max > 1f32 { 1f32 / max } else { 1f32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_scaled_graph(image: &mut RgbImage, graph: &StackedSeries) {
|
fn draw_scaled_graph(image: &mut RgbImage, graph: &StackedSeries) {
|
||||||
let scale = get_scale(&graph.data);
|
let scale = get_scale(graph.data);
|
||||||
let colors = graph
|
let colors = graph
|
||||||
.config
|
.config
|
||||||
.series
|
.series
|
||||||
|
|
@ -52,33 +51,23 @@ fn draw_scaled_graph(image: &mut RgbImage, graph: &StackedSeries) {
|
||||||
.collect::<Vec<Rgb<u8>>>();
|
.collect::<Vec<Rgb<u8>>>();
|
||||||
|
|
||||||
let mut x_offset = 0f32;
|
let mut x_offset = 0f32;
|
||||||
let mut series_idx = 0usize;
|
|
||||||
|
|
||||||
let rows = graph.data.len();
|
for point_in_time in graph.data {
|
||||||
let cols = graph.data[0].len();
|
|
||||||
let transposed_series: Vec<Vec<f32>> = (0..cols)
|
|
||||||
.map(|col| (0..rows).map(|row| graph.data[row][col]).collect())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for timeseries in transposed_series {
|
|
||||||
let mut y_offset = graph.config.height_px as f32;
|
let mut y_offset = graph.config.height_px as f32;
|
||||||
|
|
||||||
for sample in timeseries.iter() {
|
let mut series_idx = 0usize;
|
||||||
println!("VAL {}", sample);
|
for sample in point_in_time.iter() {
|
||||||
let color = colors[series_idx];
|
let color = colors[series_idx];
|
||||||
let height_px = (graph.config.height_px as f32) * sample * scale;
|
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)
|
if height_px > 1f32 {
|
||||||
.of_size(graph.config.width_px_per_sample, height_px as u32);
|
let rect = Rect::at(x_offset as i32, (y_offset - height_px) as i32)
|
||||||
draw_filled_rect_mut(image, rect, color);
|
.of_size(graph.config.width_px_per_sample, height_px as u32);
|
||||||
x_offset += graph.config.width_px_per_sample as f32;
|
draw_filled_rect_mut(image, rect, color);
|
||||||
y_offset -= height_px;
|
y_offset -= height_px;
|
||||||
println!("rect: {:?}, height {}, yoff {}", rect, height_px, y_offset);
|
}
|
||||||
|
series_idx += 1;
|
||||||
}
|
}
|
||||||
|
x_offset += graph.config.width_px_per_sample as f32;
|
||||||
println!("BRK");
|
|
||||||
|
|
||||||
// FIXME:
|
|
||||||
// series_idx += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +82,6 @@ mod test {
|
||||||
vec![0., 1., 0., 0., 0.],
|
vec![0., 1., 0., 0., 0.],
|
||||||
vec![0., 0., 0., 0., 5.],
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ mod err;
|
||||||
mod message;
|
mod message;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
|
|
||||||
|
pub use err::ProtocolError;
|
||||||
pub use protocol::Protocol;
|
pub use protocol::Protocol;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::err::ProtocolError;
|
use crate::err::ProtocolError;
|
||||||
use crate::message::{
|
use crate::message::{
|
||||||
create_list_message, create_request_message, parse_list_response, parse_response_header,
|
create_list_message, create_request_message, parse_list_response, parse_response_header,
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ edition = "2024"
|
||||||
draw = { version = "0.1.0", path = "../draw" }
|
draw = { version = "0.1.0", path = "../draw" }
|
||||||
imageproc = { version = "0.26.1", features = ["display-window"] }
|
imageproc = { version = "0.26.1", features = ["display-window"] }
|
||||||
protocol = { path = "../protocol" }
|
protocol = { path = "../protocol" }
|
||||||
|
regex = "1.12.3"
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,36 @@
|
||||||
|
mod observables;
|
||||||
|
|
||||||
use draw::{StackedConfig, StackedSeries, StackedSeriesConfig, stacked};
|
use draw::{StackedConfig, StackedSeries, StackedSeriesConfig, stacked};
|
||||||
use imageproc::window::display_image;
|
use imageproc::window::display_image;
|
||||||
|
use observables::CpuObservable;
|
||||||
use protocol::Protocol;
|
use protocol::Protocol;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cfg = StackedConfig {
|
let cfg = StackedConfig {
|
||||||
width_px_per_sample: 4,
|
width_px_per_sample: 4,
|
||||||
height_px: 100,
|
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,
|
max_samples: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(mut proto) = Protocol::new("/var/run/collectd-unixsock") {
|
if let Ok(mut proto) = Protocol::new("/var/run/collectd-unixsock") {
|
||||||
let mut data: Vec<f32> = vec![];
|
let mut data: Vec<Vec<f32>> = vec![];
|
||||||
// loop {
|
// loop {
|
||||||
|
let cpu = CpuObservable::discover(&mut proto).unwrap();
|
||||||
for _ in 1..10 {
|
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 {
|
if data.len() > 10 {
|
||||||
data.pop();
|
data.pop();
|
||||||
}
|
}
|
||||||
|
let sample = cpu.sample(&mut proto).unwrap();
|
||||||
|
data.push(sample);
|
||||||
|
|
||||||
let img = stacked(StackedSeries {
|
let img = stacked(StackedSeries {
|
||||||
config: &cfg,
|
config: &cfg,
|
||||||
data: vec![&data],
|
data: &data,
|
||||||
});
|
});
|
||||||
display_image("bzn", &img, 500, 500);
|
display_image("bzn", &img, 500, 500);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<String>,
|
||||||
|
interrupt_cpus: Vec<String>,
|
||||||
|
load_cpus: Vec<String>,
|
||||||
|
idle_cpus: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuObservable {
|
||||||
|
pub fn discover(proto: &mut Protocol) -> Result<CpuObservable, ProtocolError> {
|
||||||
|
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<Vec<f32>, 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<String>,
|
||||||
|
proto: &mut Protocol,
|
||||||
|
) -> Result<f32, ProtocolError> {
|
||||||
|
let results = fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| proto.get(f.into(), "value".to_string()))
|
||||||
|
.collect::<Result<Vec<f32>, ProtocolError>>()?;
|
||||||
|
Ok(results.into_iter().sum::<f32>() / self.num_cpus as f32 / CPU_NORM)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue