feat!: added view offset and reduce, ui tweaks

view reduce averages chunks of X values, view offset shifts the "now"
back. Improved panel options UI
This commit is contained in:
əlemi 2022-06-18 19:37:56 +02:00
parent e8df50f010
commit 7c6f765455
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 121 additions and 34 deletions

View file

@ -79,9 +79,16 @@ impl ApplicationState {
.expect("Storage Mutex poisoned")
.new_panel(
panel.name.as_str(),
false,
panel.view_size,
5,
0,
true,
panel.width,
panel.height,
false,
false,
false,
self.panels.read().expect("Panels RwLock poisoned").len() as i32, // todo can this be made more compact and without acquisition?
)?; // TODO make values customizable and useful
self.panels

View file

@ -4,15 +4,20 @@ use eframe::egui::plot::{Value, Values};
use eframe::epaint::Color32;
use std::sync::RwLock;
#[derive(Debug)]
pub struct Panel {
pub(crate) id: i32,
pub name: String,
pub view_scroll: bool,
pub view_size: i32,
pub view_size: u32,
pub view_chunks: u32,
pub view_offset: u32,
pub timeserie: bool,
pub(crate) width: i32,
pub(crate) height: i32,
pub limit: bool,
pub reduce: bool,
pub shift: bool,
}
impl Default for Panel {
@ -22,14 +27,19 @@ impl Default for Panel {
name: "".to_string(),
view_scroll: true,
view_size: 300,
view_chunks: 5,
view_offset: 0,
timeserie: true,
width: 100,
height: 200,
limit: false,
reduce: false,
shift: false,
}
}
}
#[derive(Debug)]
pub struct Source {
pub(crate) id: i32,
pub name: String,
@ -64,19 +74,37 @@ impl Default for Source {
}
}
fn avg_value(values: &[Value]) -> Value {
let mut x = 0.0;
let mut y = 0.0;
for v in values {
x += v.x;
y += v.y;
}
return Value { x: x / values.len() as f64, y: y / values.len() as f64 };
}
impl Source {
pub fn valid(&self) -> bool {
let last_fetch = self.last_fetch.read().expect("LastFetch RwLock poisoned");
return (Utc::now() - *last_fetch).num_seconds() < self.interval as i64;
}
pub fn values(&self) -> Values {
Values::from_values(self.data.read().expect("Values RwLock poisoned").clone())
}
pub fn values_filter(&self, min_x: f64) -> Values {
// TODO optimize this with caching!
pub fn values(&self, min_x: Option<f64>, max_x: Option<f64>, chunk_size: Option<u32>) -> Values {
let mut values = self.data.read().expect("Values RwLock poisoned").clone();
values.retain(|x| x.x > min_x);
if let Some(min_x) = min_x {
values.retain(|x| x.x > min_x);
}
if let Some(max_x) = max_x {
values.retain(|x| x.x < max_x);
}
if let Some(chunk_size) = chunk_size {
if chunk_size > 0 { // TODO make this nested if prettier
let iter = values.chunks(chunk_size as usize);
values = iter.map(|x| avg_value(x) ).collect();
}
}
Values::from_values(values)
}
}

View file

@ -30,7 +30,11 @@ impl SQLiteDataStore {
width INT NOT NULL,
height INT NOT NULL,
limit_view BOOL NOT NULL,
position INT NOT NULL
position INT NOT NULL,
reduce_view BOOL NOT NULL,
view_chunks INT NOT NULL,
shift_view BOOL NOT NULL,
view_offset INT NOT NULL
);",
[],
)?;
@ -212,6 +216,11 @@ impl SQLiteDataStore {
width: row.get(5)?,
height: row.get(6)?,
limit: row.get(7)?,
// position: row.get(8)?,
reduce: row.get(9)?,
view_chunks: row.get(10)?,
shift: row.get(11)?,
view_offset: row.get(12)?,
})
})?;
@ -228,14 +237,21 @@ impl SQLiteDataStore {
pub fn new_panel(
&self,
name: &str,
view_size: i32,
view_scroll: bool,
view_size: u32,
view_chunks: u32,
view_offset: u32,
timeserie: bool,
width: i32,
height: i32,
limit: bool,
reduce: bool,
shift: bool,
position: i32,
) -> rusqlite::Result<Panel> {
self.conn.execute(
"INSERT INTO panels (name, view_scroll, view_size, timeserie, width, height, limit_view, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
params![name, true, view_size, true, width, height, false, position]
"INSERT INTO panels (name, view_scroll, view_size, timeserie, width, height, limit_view, position, reduce_view, view_chunks, shift_view, view_offset) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
params![name, view_scroll, view_size, timeserie, width, height, limit, position, reduce, view_chunks, shift, view_offset]
)?;
let mut statement = self.conn.prepare("SELECT * FROM panels WHERE name = ?")?;
for panel in statement.query_map(params![name], |row| {
@ -248,6 +264,11 @@ impl SQLiteDataStore {
width: row.get(5)?,
height: row.get(6)?,
limit: row.get(7)?,
// position: row.get(8)?,
reduce: row.get(9)?,
view_chunks: row.get(10)?,
shift: row.get(11)?,
view_offset: row.get(12)?,
})
})? {
if let Ok(p) = panel {
@ -262,16 +283,20 @@ impl SQLiteDataStore {
id: i32,
name: &str,
view_scroll: bool,
view_size: i32,
view_size: u32,
view_chunks: u32,
view_offset: u32,
timeserie: bool,
width: i32,
height: i32,
limit: bool,
reduce: bool,
shift: bool,
position: i32,
) -> rusqlite::Result<usize> {
self.conn.execute(
"UPDATE panels SET name = ?, view_scroll = ?, view_size = ?, timeserie = ?, width = ?, height = ?, limit_view = ?, position = ? WHERE id = ?",
params![name, view_scroll, view_size, timeserie, width, height, limit, position, id],
"UPDATE panels SET name = ?, view_scroll = ?, view_size = ?, timeserie = ?, width = ?, height = ?, limit_view = ?, position = ?, reduce_view = ?, view_chunks = ?, shift_view = ?, view_offset = ? WHERE id = ?",
params![name, view_scroll, view_size, timeserie, width, height, limit, position, reduce, view_chunks, shift, view_offset, id],
)
}

View file

@ -21,15 +21,37 @@ pub fn panel_title_ui(ui: &mut Ui, panel: &mut Panel, extra: bool) {
ui.heading(panel.name.as_str());
ui.with_layout(Layout::right_to_left(), |ui| {
ui.horizontal(|ui| {
ui.toggle_value(&mut panel.view_scroll, "");
ui.toggle_value(&mut panel.view_scroll, "🔒");
ui.separator();
ui.label("m");
ui.add(
DragValue::new(&mut panel.view_size)
.speed(10)
.clamp_range(0..=2147483647i32),
);
ui.checkbox(&mut panel.limit, "limit");
if panel.limit {
ui.label("min"); // TODO makes no sense if it's not a timeserie
ui.add(
DragValue::new(&mut panel.view_size)
.speed(10)
.clamp_range(0..=2147483647i32),
);
}
ui.toggle_value(&mut panel.limit, "limit");
ui.separator();
if panel.shift {
ui.label("min");
ui.add(
DragValue::new(&mut panel.view_offset)
.speed(10)
.clamp_range(0..=2147483647i32),
);
}
ui.toggle_value(&mut panel.shift, "offset");
ui.separator();
if panel.reduce {
ui.label("x");
ui.add(
DragValue::new(&mut panel.view_chunks)
.speed(1)
.clamp_range(1..=1000), // TODO allow to average larger spans maybe?
);
}
ui.toggle_value(&mut panel.reduce, "reduce");
if extra {
ui.separator();
ui.checkbox(&mut panel.timeserie, "timeserie");
@ -52,11 +74,12 @@ pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
}
if panel.view_scroll {
p = p.include_x(Utc::now().timestamp() as f64);
let _now = (Utc::now().timestamp() as f64) - (60.0 * panel.view_offset as f64);
p = p.include_x(_now);
if panel.limit {
p = p
.include_x((Utc::now().timestamp() + (panel.view_size as i64 * 3)) as f64)
.include_x((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64); // ??? TODO
.include_x(_now + (panel.view_size as f64 * 3.0))
.include_x(_now - (panel.view_size as f64 * 60.0)); // ??? TODO
}
}
@ -106,16 +129,16 @@ pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
}
p.show(ui, |plot_ui| {
for source in &*sources {
for source in sources {
if source.panel_id == panel.id {
let line = if panel.limit {
Line::new(source.values_filter(
(Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64,
))
.name(source.name.as_str())
} else {
Line::new(source.values()).name(source.name.as_str())
};
let _now = Utc::now().timestamp() as f64;
let _off = (panel.view_offset as f64) * 60.0; // TODO multiplying x60 makes sense only for timeseries
let _size = (panel.view_size as f64) * 60.0; // TODO multiplying x60 makes sense only for timeseries
let min_x = if panel.limit { Some(_now - _size - _off) } else { None };
let max_x = if panel.shift { Some(_now - _off) } else { None };
let chunk_size = if panel.reduce { Some(panel.view_chunks) } else { None };
// let chunks = None;
let line = Line::new(source.values(min_x, max_x, chunk_size)).name(source.name.as_str());
plot_ui.line(line.color(source.color));
}
}

View file

@ -14,10 +14,14 @@ pub fn native_save(state: Arc<ApplicationState>) {
panel.name.as_str(),
panel.view_scroll,
panel.view_size,
panel.view_chunks,
panel.view_offset,
panel.timeserie,
panel.width,
panel.height,
panel.limit,
panel.reduce,
panel.shift,
index as i32,
) {
warn!(target: "native-save", "Could not update panel #{} : {:?}", panel.id, e);