// pub mod source; pub mod store; use std::path::PathBuf; use std::sync::{RwLock, Mutex}; use std::num::ParseFloatError; use chrono::{DateTime, Utc}; use eframe::egui::plot::{Values, Value}; use eframe::epaint::Color32; use self::store::SQLiteDataStore; #[derive(Debug)] pub enum FetchError { UreqError(ureq::Error), IoError(std::io::Error), JqError(jq_rs::Error), RusqliteError(rusqlite::Error), ParseFloatError(ParseFloatError), NoPanelWithThatIdError, } impl From:: for FetchError { fn from(e: ureq::Error) -> Self { FetchError::UreqError(e) } } impl From:: for FetchError { fn from(e: std::io::Error) -> Self { FetchError::IoError(e) } } impl From:: for FetchError { fn from(e: jq_rs::Error) -> Self { FetchError::JqError(e) } } impl From:: for FetchError { fn from(e: ParseFloatError) -> Self { FetchError::ParseFloatError(e) } } impl From:: for FetchError { fn from(e: rusqlite::Error) -> Self { FetchError::RusqliteError(e) } } pub struct ApplicationState { pub run: bool, pub file_path: PathBuf, pub file_size: RwLock, pub panels: RwLock>, pub storage: Mutex, } impl ApplicationState { pub fn new(path:PathBuf) -> Self { let storage = SQLiteDataStore::new(path.clone()).unwrap(); let panels = storage.load_panels().unwrap(); return ApplicationState{ run: true, file_size: RwLock::new(std::fs::metadata(path.clone()).unwrap().len()), file_path: path, panels: RwLock::new(panels), storage: Mutex::new(storage), }; } pub fn add_panel(&self, name:&str) -> Result<(), FetchError> { let panel = self.storage.lock().expect("Storage Mutex poisoned").new_panel(name, 100, 200, 280)?; // TODO make values customizable and useful self.panels.write().expect("Panels RwLock poisoned").push(panel); Ok(()) } pub fn add_source(&self, panel_id:i32, name:&str, url:&str, query_x:&str, query_y:&str, color:Color32, visible:bool) -> Result<(), FetchError> { let source = self.storage.lock().expect("Storage Mutex poisoned") .new_source(panel_id, name, url, query_x, query_y, color, visible)?; let panels = self.panels.read().expect("Panels RwLock poisoned"); for panel in &*panels { if panel.id == panel_id { panel.sources.write().expect("Sources RwLock poisoned").push(source); return Ok(()); } } Err(FetchError::NoPanelWithThatIdError) } } pub struct Panel { pub(crate) id: i32, pub name: String, pub view_scroll: bool, pub view_size: i32, pub timeserie: bool, pub(crate) width: i32, pub(crate) height: i32, pub(crate) sources: RwLock>, } impl Panel { } pub struct Source { pub(crate) id: i32, pub name: String, pub url: String, pub interval: i32, pub color: Color32, pub visible: bool, pub(crate) last_fetch: RwLock>, pub query_x: String, // pub(crate) compiled_query_x: Arc>, pub query_y: String, // pub(crate) compiled_query_y: Arc>, // pub(crate) panel_id: i32, pub(crate) data: RwLock>, } 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 { let mut values = self.data.read().expect("Values RwLock poisoned").clone(); values.retain(|x| x.x > min_x); Values::from_values(values) } } pub fn fetch(url:&str, query_x:&str, query_y:&str) -> Result { 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::()?; // TODO precompile and guard with a mutex } else { x = Utc::now().timestamp() as f64; } let y = jq_rs::compile(query_y)?.run(&body)?.trim().parse::()?; return Ok( Value { x, y } ); }