2022-06-07 00:05:45 +02:00
|
|
|
// pub mod source;
|
2022-06-06 04:41:34 +02:00
|
|
|
pub mod store;
|
|
|
|
|
2022-06-07 00:05:45 +02:00
|
|
|
use std::path::PathBuf;
|
2022-06-07 01:28:17 +02:00
|
|
|
use std::sync::{RwLock, Mutex};
|
2022-06-06 04:41:34 +02:00
|
|
|
use std::num::ParseFloatError;
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
use eframe::egui::plot::{Values, Value};
|
|
|
|
|
2022-06-07 00:05:45 +02:00
|
|
|
use self::store::SQLiteDataStore;
|
|
|
|
|
2022-06-06 04:41:34 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum FetchError {
|
2022-06-07 00:05:45 +02:00
|
|
|
UreqError(ureq::Error),
|
|
|
|
IoError(std::io::Error),
|
2022-06-06 04:41:34 +02:00
|
|
|
JqError(jq_rs::Error),
|
|
|
|
RusqliteError(rusqlite::Error),
|
|
|
|
ParseFloatError(ParseFloatError),
|
2022-06-07 00:05:45 +02:00
|
|
|
NoPanelWithThatIdError,
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
2022-06-07 00:05:45 +02:00
|
|
|
impl From::<ureq::Error> for FetchError {
|
|
|
|
fn from(e: ureq::Error) -> Self { FetchError::UreqError(e) }
|
|
|
|
}
|
|
|
|
impl From::<std::io::Error> for FetchError {
|
|
|
|
fn from(e: std::io::Error) -> Self { FetchError::IoError(e) }
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
impl From::<jq_rs::Error> for FetchError {
|
|
|
|
fn from(e: jq_rs::Error) -> Self { FetchError::JqError(e) }
|
|
|
|
}
|
|
|
|
impl From::<ParseFloatError> for FetchError {
|
|
|
|
fn from(e: ParseFloatError) -> Self { FetchError::ParseFloatError(e) }
|
|
|
|
}
|
|
|
|
impl From::<rusqlite::Error> for FetchError {
|
|
|
|
fn from(e: rusqlite::Error) -> Self { FetchError::RusqliteError(e) }
|
|
|
|
}
|
|
|
|
|
2022-06-07 00:05:45 +02:00
|
|
|
pub struct ApplicationState {
|
|
|
|
pub run: bool,
|
|
|
|
pub panels: RwLock<Vec<Panel>>,
|
|
|
|
pub storage: Mutex<SQLiteDataStore>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ApplicationState {
|
|
|
|
pub fn new(path:PathBuf) -> Self {
|
|
|
|
let storage = SQLiteDataStore::new(path).unwrap();
|
|
|
|
|
|
|
|
let panels = storage.load_panels().unwrap();
|
|
|
|
|
|
|
|
return ApplicationState{
|
|
|
|
run: true,
|
|
|
|
panels: RwLock::new(panels),
|
|
|
|
storage: Mutex::new(storage),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_panel(&self, name:&str) -> Result<(), FetchError> {
|
|
|
|
let panel = self.storage.lock().unwrap().new_panel(name, 100, 200, 280)?; // TODO make values customizable and useful
|
|
|
|
self.panels.write().unwrap().push(panel);
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-06-06 04:41:34 +02:00
|
|
|
|
2022-06-07 00:05:45 +02:00
|
|
|
pub fn add_source(&self, panel_id:i32, name:&str, url:&str, query_x:&str, query_y:&str) -> Result<(), FetchError> {
|
|
|
|
let source = self.storage.lock().unwrap().new_source(panel_id, name, url, query_x, query_y)?;
|
|
|
|
let panels = self.panels.read().unwrap();
|
|
|
|
for panel in &*panels {
|
|
|
|
if panel.id == panel_id {
|
|
|
|
panel.sources.write().unwrap().push(source);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(FetchError::NoPanelWithThatIdError)
|
|
|
|
}
|
|
|
|
}
|
2022-06-06 04:41:34 +02:00
|
|
|
|
|
|
|
pub struct Panel {
|
|
|
|
pub(crate) id: i32,
|
|
|
|
pub name: String,
|
|
|
|
pub view_scroll: bool,
|
|
|
|
pub view_size: i32,
|
2022-06-07 00:05:45 +02:00
|
|
|
pub timeserie: bool,
|
2022-06-06 04:41:34 +02:00
|
|
|
pub(crate) width: i32,
|
|
|
|
pub(crate) height: i32,
|
2022-06-07 00:05:45 +02:00
|
|
|
pub(crate) sources: RwLock<Vec<Source>>,
|
|
|
|
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Panel {
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Source {
|
|
|
|
pub(crate) id: i32,
|
|
|
|
pub name: String,
|
|
|
|
pub url: String,
|
|
|
|
pub interval: i32,
|
2022-06-07 00:05:45 +02:00
|
|
|
pub(crate) last_fetch: RwLock<DateTime<Utc>>,
|
2022-06-06 04:41:34 +02:00
|
|
|
pub query_x: String,
|
|
|
|
// pub(crate) compiled_query_x: Arc<Mutex<jq_rs::JqProgram>>,
|
|
|
|
pub query_y: String,
|
|
|
|
// pub(crate) compiled_query_y: Arc<Mutex<jq_rs::JqProgram>>,
|
2022-06-07 01:28:17 +02:00
|
|
|
// pub(crate) panel_id: i32,
|
2022-06-07 00:05:45 +02:00
|
|
|
pub(crate) data: RwLock<Vec<Value>>,
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Source {
|
|
|
|
pub fn valid(&self) -> bool {
|
2022-06-07 00:05:45 +02:00
|
|
|
let last_fetch = self.last_fetch.read().unwrap();
|
|
|
|
return (Utc::now() - *last_fetch).num_seconds() < self.interval as i64;
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn values(&self) -> Values {
|
2022-06-07 00:05:45 +02:00
|
|
|
Values::from_values(self.data.read().unwrap().clone())
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
2022-06-07 01:28:17 +02:00
|
|
|
pub fn values_filter(&self, min_x:f64) -> Values {
|
|
|
|
let mut values = self.data.read().unwrap().clone();
|
|
|
|
values.retain(|x| x.x > min_x);
|
|
|
|
Values::from_values(values)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not really useful since different data has different fetch rates
|
|
|
|
// pub fn values_limit(&self, size:usize) -> Values {
|
|
|
|
// let values = self.data.read().unwrap().clone();
|
|
|
|
// let min = if values.len() < size { 0 } else { values.len() - size };
|
|
|
|
// Values::from_values(values[min..values.len()].to_vec())
|
|
|
|
// }
|
2022-06-07 00:05:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fetch(url:&str, query_x:&str, query_y:&str) -> Result<Value, FetchError> {
|
|
|
|
let res = ureq::get(url).call()?;
|
|
|
|
let body = res.into_string()?;
|
|
|
|
let x : f64;
|
|
|
|
if query_x.len() > 0 {
|
|
|
|
x = jq_rs::compile(query_x)?.run(&body)?.trim().parse::<f64>()?; // TODO precompile and guard with a mutex
|
|
|
|
} else {
|
|
|
|
x = Utc::now().timestamp() as f64;
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|
2022-06-07 00:05:45 +02:00
|
|
|
let y = jq_rs::compile(query_y)?.run(&body)?.trim().parse::<f64>()?;
|
|
|
|
return Ok( Value { x, y } );
|
2022-06-06 04:41:34 +02:00
|
|
|
}
|