mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-22 23:44:55 +01:00
style: rustfmt whole project
This commit is contained in:
parent
32d58e4019
commit
21d664479a
9 changed files with 184 additions and 137 deletions
|
@ -1,12 +1,11 @@
|
|||
pub mod source;
|
||||
pub mod store;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{RwLock, Mutex};
|
||||
use std::num::ParseFloatError;
|
||||
use eframe::epaint::Color32;
|
||||
use self::store::SQLiteDataStore;
|
||||
use self::source::{Panel, Source};
|
||||
use self::store::SQLiteDataStore;
|
||||
use std::num::ParseFloatError;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, RwLock};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FetchError {
|
||||
|
@ -18,20 +17,31 @@ pub enum FetchError {
|
|||
ParseFloatError(ParseFloatError),
|
||||
}
|
||||
|
||||
impl From::<ureq::Error> for FetchError {
|
||||
fn from(e: ureq::Error) -> Self { FetchError::UreqError(e) }
|
||||
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) }
|
||||
impl From<std::io::Error> for FetchError {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
FetchError::IoError(e)
|
||||
}
|
||||
}
|
||||
impl From::<String> for FetchError { // TODO wtf? why does JQL error as a String?
|
||||
fn from(e: String) -> Self { FetchError::JQLError(e) }
|
||||
impl From<String> for FetchError {
|
||||
// TODO wtf? why does JQL error as a String?
|
||||
fn from(e: String) -> Self {
|
||||
FetchError::JQLError(e)
|
||||
}
|
||||
}
|
||||
impl From::<ParseFloatError> for FetchError {
|
||||
fn from(e: ParseFloatError) -> Self { FetchError::ParseFloatError(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) }
|
||||
impl From<rusqlite::Error> for FetchError {
|
||||
fn from(e: rusqlite::Error) -> Self {
|
||||
FetchError::RusqliteError(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApplicationState {
|
||||
|
@ -45,13 +55,13 @@ pub struct ApplicationState {
|
|||
}
|
||||
|
||||
impl ApplicationState {
|
||||
pub fn new(path:PathBuf) -> Result<ApplicationState, FetchError> {
|
||||
pub fn new(path: PathBuf) -> Result<ApplicationState, FetchError> {
|
||||
let storage = SQLiteDataStore::new(path.clone())?;
|
||||
|
||||
let panels = storage.load_panels()?;
|
||||
let sources = storage.load_sources()?;
|
||||
|
||||
return Ok(ApplicationState{
|
||||
return Ok(ApplicationState {
|
||||
run: true,
|
||||
file_size: RwLock::new(std::fs::metadata(path.clone())?.len()),
|
||||
file_path: path,
|
||||
|
@ -63,22 +73,42 @@ impl ApplicationState {
|
|||
}
|
||||
|
||||
pub fn add_panel(&self, panel: &Panel) -> Result<(), FetchError> {
|
||||
let verified_panel = self.storage.lock().expect("Storage Mutex poisoned")
|
||||
let verified_panel = self
|
||||
.storage
|
||||
.lock()
|
||||
.expect("Storage Mutex poisoned")
|
||||
.new_panel(
|
||||
panel.name.as_str(),
|
||||
panel.view_size,
|
||||
panel.width,
|
||||
panel.height,
|
||||
self.panels.read().expect("Panels RwLock poisoned").len() as i32 // todo can this be made more compact and without acquisition?
|
||||
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.write().expect("Panels RwLock poisoned").push(verified_panel);
|
||||
self.panels
|
||||
.write()
|
||||
.expect("Panels RwLock poisoned")
|
||||
.push(verified_panel);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_source(&self, source: &Source) -> Result<(), FetchError> {
|
||||
let verified_source = self.storage.lock().expect("Storage Mutex poisoned")
|
||||
.new_source(source.panel_id, source.name.as_str(), source.url.as_str(), source.query_x.as_str(), source.query_y.as_str(), source.color, source.visible)?;
|
||||
self.sources.write().expect("Sources RwLock poisoned").push(verified_source);
|
||||
let verified_source = self
|
||||
.storage
|
||||
.lock()
|
||||
.expect("Storage Mutex poisoned")
|
||||
.new_source(
|
||||
source.panel_id,
|
||||
source.name.as_str(),
|
||||
source.url.as_str(),
|
||||
source.query_x.as_str(),
|
||||
source.query_y.as_str(),
|
||||
source.color,
|
||||
source.visible,
|
||||
)?;
|
||||
self.sources
|
||||
.write()
|
||||
.expect("Sources RwLock poisoned")
|
||||
.push(verified_source);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::sync::RwLock;
|
||||
use chrono::{DateTime, Utc};
|
||||
use eframe::egui::plot::{Values, Value};
|
||||
use eframe::epaint::Color32;
|
||||
use super::FetchError;
|
||||
use chrono::{DateTime, Utc};
|
||||
use eframe::egui::plot::{Value, Values};
|
||||
use eframe::epaint::Color32;
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub struct Panel {
|
||||
pub(crate) id: i32,
|
||||
|
@ -59,7 +59,7 @@ impl Default for Source {
|
|||
query_x: "".to_string(),
|
||||
query_y: "".to_string(),
|
||||
panel_id: -1,
|
||||
data: RwLock::new(Vec::new())
|
||||
data: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,21 +74,25 @@ impl Source {
|
|||
Values::from_values(self.data.read().expect("Values RwLock poisoned").clone())
|
||||
}
|
||||
|
||||
pub fn values_filter(&self, min_x:f64) -> Values {
|
||||
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<Value, FetchError> {
|
||||
pub fn fetch(url: &str, query_x: &str, query_y: &str) -> Result<Value, FetchError> {
|
||||
let res = ureq::get(url).call()?.into_json()?;
|
||||
let x : f64;
|
||||
let x: f64;
|
||||
if query_x.len() > 0 {
|
||||
x = jql::walker(&res, query_x)?.as_f64().ok_or(FetchError::JQLError("X query is null".to_string()))?; // TODO what if it's given to us as a string?
|
||||
x = jql::walker(&res, query_x)?
|
||||
.as_f64()
|
||||
.ok_or(FetchError::JQLError("X query is null".to_string()))?; // TODO what if it's given to us as a string?
|
||||
} else {
|
||||
x = Utc::now().timestamp() as f64;
|
||||
}
|
||||
let y = jql::walker(&res, query_y)?.as_f64().ok_or(FetchError::JQLError("Y query is null".to_string()))?;
|
||||
return Ok( Value { x, y } );
|
||||
let y = jql::walker(&res, query_y)?
|
||||
.as_f64()
|
||||
.ok_or(FetchError::JQLError("Y query is null".to_string()))?;
|
||||
return Ok(Value { x, y });
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use crate::app::{data::source::{Panel, Source}, util::repack_color};
|
||||
use crate::app::util::unpack_color;
|
||||
use crate::app::{
|
||||
data::source::{Panel, Source},
|
||||
util::repack_color,
|
||||
};
|
||||
use chrono::{TimeZone, Utc};
|
||||
use eframe::egui::{Color32, plot::Value};
|
||||
use eframe::egui::{plot::Value, Color32};
|
||||
use rusqlite::{params, Connection};
|
||||
use std::sync::RwLock;
|
||||
use crate::app::util::unpack_color;
|
||||
|
||||
pub trait DataStorage {
|
||||
fn add_panel(&self, name: &str);
|
||||
|
@ -60,8 +63,6 @@ impl SQLiteDataStore {
|
|||
Ok(SQLiteDataStore { conn })
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn load_values(&self, source_id: i32) -> rusqlite::Result<Vec<Value>> {
|
||||
let mut values: Vec<Value> = Vec::new();
|
||||
let mut statement = self
|
||||
|
@ -90,13 +91,9 @@ impl SQLiteDataStore {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn load_sources(&self) -> rusqlite::Result<Vec<Source>> {
|
||||
let mut sources: Vec<Source> = Vec::new();
|
||||
let mut statement = self
|
||||
.conn
|
||||
.prepare("SELECT * FROM sources")?;
|
||||
let mut statement = self.conn.prepare("SELECT * FROM sources")?;
|
||||
let sources_iter = statement.query_map([], |row| {
|
||||
Ok(Source {
|
||||
id: row.get(0)?,
|
||||
|
@ -134,7 +131,11 @@ impl SQLiteDataStore {
|
|||
color: Color32,
|
||||
visible: bool,
|
||||
) -> rusqlite::Result<Source> {
|
||||
let color_u32 : Option<u32> = if color == Color32::TRANSPARENT { None } else { Some(repack_color(color)) };
|
||||
let color_u32: Option<u32> = if color == Color32::TRANSPARENT {
|
||||
None
|
||||
} else {
|
||||
Some(repack_color(color))
|
||||
};
|
||||
self.conn.execute(
|
||||
"INSERT INTO sources(name, url, interval, query_x, query_y, panel_id, color, visible) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
params![name, url, 60i32, query_x, query_y, panel_id, color_u32, visible],
|
||||
|
@ -177,7 +178,11 @@ impl SQLiteDataStore {
|
|||
color: Color32,
|
||||
visible: bool,
|
||||
) -> rusqlite::Result<usize> {
|
||||
let color_u32 : Option<u32> = if color == Color32::TRANSPARENT { None } else { Some(repack_color(color)) };
|
||||
let color_u32: Option<u32> = if color == Color32::TRANSPARENT {
|
||||
None
|
||||
} else {
|
||||
Some(repack_color(color))
|
||||
};
|
||||
self.conn.execute(
|
||||
"UPDATE sources SET name = ?, url = ?, interval = ?, query_x = ?, query_y = ?, panel_id = ?, color = ?, visible = ? WHERE id = ?",
|
||||
params![name, url, interval, query_x, query_y, panel_id, color_u32, visible, source_id],
|
||||
|
@ -190,7 +195,9 @@ impl SQLiteDataStore {
|
|||
|
||||
pub fn load_panels(&self) -> rusqlite::Result<Vec<Panel>> {
|
||||
let mut panels: Vec<Panel> = Vec::new();
|
||||
let mut statement = self.conn.prepare("SELECT * FROM panels ORDER BY position")?;
|
||||
let mut statement = self
|
||||
.conn
|
||||
.prepare("SELECT * FROM panels ORDER BY position")?;
|
||||
let panels_iter = statement.query_map([], |row| {
|
||||
Ok(Panel {
|
||||
id: row.get(0)?,
|
||||
|
@ -214,7 +221,14 @@ impl SQLiteDataStore {
|
|||
}
|
||||
|
||||
// jank! TODO make it not jank!
|
||||
pub fn new_panel(&self, name: &str, view_size:i32, width: i32, height: i32, position: i32) -> rusqlite::Result<Panel> {
|
||||
pub fn new_panel(
|
||||
&self,
|
||||
name: &str,
|
||||
view_size: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
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]
|
||||
|
@ -260,7 +274,4 @@ impl SQLiteDataStore {
|
|||
// pub fn delete_panel(&self, id:i32) -> rusqlite::Result<usize> {
|
||||
// self.conn.execute("DELETE FROM panels WHERE id = ?", params![id])
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
pub mod source;
|
||||
pub mod panel;
|
||||
pub mod source;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use chrono::{Utc, Local};
|
||||
use eframe::egui::{Ui, Layout, Align, plot::{Plot, Legend, Corner, Line, GridMark}, Slider, DragValue};
|
||||
use chrono::{Local, Utc};
|
||||
use eframe::egui::{
|
||||
plot::{Corner, GridMark, Legend, Line, Plot},
|
||||
DragValue, Layout, Slider, Ui,
|
||||
};
|
||||
|
||||
use crate::app::{data::source::{Panel, Source}, util::timestamp_to_str};
|
||||
use crate::app::{
|
||||
data::source::{Panel, Source},
|
||||
util::timestamp_to_str,
|
||||
};
|
||||
|
||||
pub fn panel_edit_inline_ui(ui: &mut Ui, panel: &mut Panel) {
|
||||
eframe::egui::TextEdit::singleline(&mut panel.name)
|
||||
|
@ -27,16 +33,13 @@ pub fn panel_title_ui(ui: &mut Ui, panel: &mut Panel) {
|
|||
ui.separator();
|
||||
ui.checkbox(&mut panel.timeserie, "timeserie");
|
||||
ui.separator();
|
||||
ui.add(
|
||||
Slider::new(&mut panel.height, 0..=500).text("height"),
|
||||
);
|
||||
ui.add(Slider::new(&mut panel.height, 0..=500).text("height"));
|
||||
ui.separator();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
|
||||
let mut p = Plot::new(format!("plot-{}", panel.name))
|
||||
.height(panel.height as f32)
|
||||
|
@ -44,26 +47,20 @@ pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
|
|||
.legend(Legend::default().position(Corner::LeftTop));
|
||||
|
||||
if panel.view_scroll {
|
||||
p = p
|
||||
.include_x(Utc::now().timestamp() as f64);
|
||||
p = p.include_x(Utc::now().timestamp() as f64);
|
||||
if panel.limit {
|
||||
p = p
|
||||
.set_margin_fraction(eframe::emath::Vec2{x:0.0, y:0.1})
|
||||
.include_x((Utc::now().timestamp() + ( panel.view_size as i64 * 3)) as f64);
|
||||
.set_margin_fraction(eframe::emath::Vec2 { x: 0.0, y: 0.1 })
|
||||
.include_x((Utc::now().timestamp() + (panel.view_size as i64 * 3)) as f64);
|
||||
}
|
||||
if panel.limit {
|
||||
p = p.include_x(
|
||||
(Utc::now().timestamp() - (panel.view_size as i64 * 60))
|
||||
as f64,
|
||||
);
|
||||
p = p.include_x((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64);
|
||||
}
|
||||
}
|
||||
|
||||
if panel.timeserie {
|
||||
p = p
|
||||
.x_axis_formatter(|x, _range| {
|
||||
timestamp_to_str(x as i64, true, false)
|
||||
})
|
||||
.x_axis_formatter(|x, _range| timestamp_to_str(x as i64, true, false))
|
||||
.label_formatter(|name, value| {
|
||||
if !name.is_empty() {
|
||||
return format!(
|
||||
|
@ -111,8 +108,7 @@ pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
|
|||
if source.visible && 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,
|
||||
(Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64,
|
||||
))
|
||||
.name(source.name.as_str())
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use eframe::egui;
|
||||
use eframe::egui::Ui;
|
||||
|
||||
use crate::app::data::source::{Source, Panel};
|
||||
use crate::app::data::source::{Panel, Source};
|
||||
|
||||
pub fn source_edit_inline_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Panel>) {
|
||||
eframe::egui::TextEdit::singleline(&mut source.name)
|
||||
|
@ -25,11 +25,7 @@ pub fn source_edit_inline_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Pane
|
|||
.width(70.0)
|
||||
.show_ui(ui, |ui| {
|
||||
for p in panels {
|
||||
ui.selectable_value(
|
||||
&mut source.panel_id,
|
||||
p.id,
|
||||
p.name.as_str(),
|
||||
);
|
||||
ui.selectable_value(&mut source.panel_id, p.id, p.name.as_str());
|
||||
}
|
||||
});
|
||||
ui.checkbox(&mut source.visible, "visible");
|
||||
|
@ -37,7 +33,6 @@ pub fn source_edit_inline_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Pane
|
|||
ui.color_edit_button_srgba(&mut source.color);
|
||||
}
|
||||
|
||||
|
||||
pub fn source_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Panel>) {
|
||||
ui.group(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
|
@ -66,11 +61,7 @@ pub fn source_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Panel>) {
|
|||
.width(70.0)
|
||||
.show_ui(ui, |ui| {
|
||||
for p in panels {
|
||||
ui.selectable_value(
|
||||
&mut source.panel_id,
|
||||
p.id,
|
||||
p.name.as_str(),
|
||||
);
|
||||
ui.selectable_value(&mut source.panel_id, p.id, p.name.as_str());
|
||||
}
|
||||
});
|
||||
ui.color_edit_button_srgba(&mut source.color);
|
||||
|
|
|
@ -3,21 +3,15 @@ pub mod gui;
|
|||
pub mod util;
|
||||
pub mod worker;
|
||||
|
||||
use chrono::{Local, Utc};
|
||||
use eframe::egui;
|
||||
use eframe::egui::plot::GridMark;
|
||||
use eframe::egui::{
|
||||
plot::{Line, Plot},
|
||||
Color32,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing::error;
|
||||
|
||||
use self::data::source::{Panel, Source};
|
||||
use self::data::ApplicationState;
|
||||
use self::data::source::{Panel,Source};
|
||||
use self::gui::panel::{panel_edit_inline_ui, panel_title_ui, panel_body_ui};
|
||||
use self::gui::source::{source_ui, source_edit_inline_ui};
|
||||
use self::util::{human_size, timestamp_to_str};
|
||||
use self::gui::panel::{panel_body_ui, panel_edit_inline_ui, panel_title_ui};
|
||||
use self::gui::source::{source_edit_inline_ui, source_ui};
|
||||
use self::util::human_size;
|
||||
use self::worker::native_save;
|
||||
|
||||
pub struct App {
|
||||
|
@ -61,7 +55,11 @@ impl eframe::App for App {
|
|||
}
|
||||
ui.separator();
|
||||
ui.label("+ source");
|
||||
source_edit_inline_ui(ui, &mut self.input_source, &self.data.panels.read().expect("Panels RwLock poisoned"));
|
||||
source_edit_inline_ui(
|
||||
ui,
|
||||
&mut self.input_source,
|
||||
&self.data.panels.read().expect("Panels RwLock poisoned"),
|
||||
);
|
||||
if ui.button("add").clicked() {
|
||||
if let Err(e) = self.data.add_source(&self.input_source) {
|
||||
error!(target: "ui", "Error adding souce : {:?}", e);
|
||||
|
@ -154,22 +152,22 @@ impl eframe::App for App {
|
|||
ui.make_persistent_id(format!("panel-{}-compressable", panel.id)),
|
||||
true,
|
||||
)
|
||||
.show_header(ui, |ui| {
|
||||
if self.edit {
|
||||
if ui.small_button(" + ").clicked() {
|
||||
if index > 0 {
|
||||
to_swap.push(index); // TODO kinda jank but is there a better way?
|
||||
}
|
||||
}
|
||||
if ui.small_button(" - ").clicked() {
|
||||
if index < panels_count - 1 {
|
||||
to_swap.push(index + 1); // TODO kinda jank but is there a better way?
|
||||
}
|
||||
.show_header(ui, |ui| {
|
||||
if self.edit {
|
||||
if ui.small_button(" + ").clicked() {
|
||||
if index > 0 {
|
||||
to_swap.push(index); // TODO kinda jank but is there a better way?
|
||||
}
|
||||
}
|
||||
panel_title_ui(ui, panel);
|
||||
})
|
||||
.body(|ui| panel_body_ui(ui, panel, &sources));
|
||||
if ui.small_button(" - ").clicked() {
|
||||
if index < panels_count - 1 {
|
||||
to_swap.push(index + 1); // TODO kinda jank but is there a better way?
|
||||
}
|
||||
}
|
||||
}
|
||||
panel_title_ui(ui, panel);
|
||||
})
|
||||
.body(|ui| panel_body_ui(ui, panel, &sources));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::sync::Arc;
|
||||
use tracing::warn;
|
||||
use crate::app::data::{source::fetch, ApplicationState};
|
||||
use chrono::Utc;
|
||||
use eframe::egui::Context;
|
||||
use crate::app::data::{ApplicationState, source::fetch};
|
||||
use std::sync::Arc;
|
||||
use tracing::warn;
|
||||
|
||||
pub fn native_save(state:Arc<ApplicationState>) {
|
||||
pub fn native_save(state: Arc<ApplicationState>) {
|
||||
std::thread::spawn(move || {
|
||||
let storage = state.storage.lock().expect("Storage Mutex poisoned");
|
||||
let panels = state.panels.read().expect("Panels RwLock poisoned");
|
||||
|
@ -43,16 +43,16 @@ pub fn native_save(state:Arc<ApplicationState>) {
|
|||
}
|
||||
|
||||
pub(crate) trait BackgroundWorker {
|
||||
fn start(state:Arc<ApplicationState>, ctx:Context) -> Self; // TODO make it return an error? Can we even do anything without a background worker
|
||||
fn stop(self); // TODO make it return an error? Can we even do anything without a background worker
|
||||
fn start(state: Arc<ApplicationState>, ctx: Context) -> Self; // TODO make it return an error? Can we even do anything without a background worker
|
||||
fn stop(self); // TODO make it return an error? Can we even do anything without a background worker
|
||||
}
|
||||
|
||||
pub(crate) struct NativeBackgroundWorker {
|
||||
worker : std::thread::JoinHandle<()>,
|
||||
worker: std::thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl BackgroundWorker for NativeBackgroundWorker {
|
||||
fn start(state:Arc<ApplicationState>, ctx:Context) -> Self {
|
||||
fn start(state: Arc<ApplicationState>, ctx: Context) -> Self {
|
||||
let worker = std::thread::spawn(move || {
|
||||
let mut last_check = 0;
|
||||
while state.run {
|
||||
|
@ -66,25 +66,38 @@ impl BackgroundWorker for NativeBackgroundWorker {
|
|||
for j in 0..sources.len() {
|
||||
let s_id = sources[j].id;
|
||||
if !sources[j].valid() {
|
||||
let mut last_update = sources[j].last_fetch.write().expect("Sources RwLock poisoned");
|
||||
let mut last_update = sources[j]
|
||||
.last_fetch
|
||||
.write()
|
||||
.expect("Sources RwLock poisoned");
|
||||
*last_update = Utc::now();
|
||||
let state2 = state.clone();
|
||||
let url = sources[j].url.clone();
|
||||
let query_x = sources[j].query_x.clone();
|
||||
let query_y = sources[j].query_y.clone();
|
||||
std::thread::spawn(move || { // TODO this can overspawn if a request takes longer than the refresh interval!
|
||||
std::thread::spawn(move || {
|
||||
// TODO this can overspawn if a request takes longer than the refresh interval!
|
||||
match fetch(url.as_str(), query_x.as_str(), query_y.as_str()) {
|
||||
Ok(v) => {
|
||||
let store = state2.storage.lock().expect("Storage mutex poisoned");
|
||||
let store =
|
||||
state2.storage.lock().expect("Storage mutex poisoned");
|
||||
if let Err(e) = store.put_value(s_id, v) {
|
||||
warn!(target:"background-worker", "Could not put sample for source #{} in db: {:?}", s_id, e);
|
||||
} else {
|
||||
let sources = state2.sources.read().expect("Sources RwLock poisoned");
|
||||
sources[j].data.write().expect("Source data RwLock poisoned").push(v);
|
||||
let mut last_update = sources[j].last_fetch.write().expect("Source last update RwLock poisoned");
|
||||
let sources =
|
||||
state2.sources.read().expect("Sources RwLock poisoned");
|
||||
sources[j]
|
||||
.data
|
||||
.write()
|
||||
.expect("Source data RwLock poisoned")
|
||||
.push(v);
|
||||
let mut last_update = sources[j]
|
||||
.last_fetch
|
||||
.write()
|
||||
.expect("Source last update RwLock poisoned");
|
||||
*last_update = Utc::now(); // overwrite it so fetches comply with API slowdowns and get desynched among them
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(target:"background-worker", "Could not fetch value from {} : {:?}", url, e);
|
||||
}
|
||||
|
@ -102,12 +115,12 @@ impl BackgroundWorker for NativeBackgroundWorker {
|
|||
}
|
||||
});
|
||||
|
||||
return NativeBackgroundWorker {
|
||||
worker
|
||||
};
|
||||
return NativeBackgroundWorker { worker };
|
||||
}
|
||||
|
||||
fn stop(self) {
|
||||
self.worker.join().expect("Failed joining main worker thread");
|
||||
self.worker
|
||||
.join()
|
||||
.expect("Failed joining main worker thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -1,23 +1,26 @@
|
|||
mod app;
|
||||
|
||||
use crate::app::{
|
||||
data::ApplicationState,
|
||||
util::InternalLogger,
|
||||
worker::{BackgroundWorker, NativeBackgroundWorker},
|
||||
App,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use crate::app::{App, util::InternalLogger, data::ApplicationState, worker::{BackgroundWorker, NativeBackgroundWorker}};
|
||||
|
||||
// When compiling natively:
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() -> ! {
|
||||
use tracing::metadata::LevelFilter;
|
||||
|
||||
use tracing::metadata::LevelFilter;
|
||||
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
|
||||
|
||||
let mut store_path = dirs::data_dir().unwrap_or(std::path::PathBuf::from(".")); // TODO get cwd more consistently?
|
||||
store_path.push("dashboard.db");
|
||||
|
||||
let store = Arc::new(
|
||||
ApplicationState::new(store_path).expect("Failed creating application state")
|
||||
);
|
||||
let store =
|
||||
Arc::new(ApplicationState::new(store_path).expect("Failed creating application state"));
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(LevelFilter::INFO)
|
||||
|
@ -25,7 +28,8 @@ fn main() -> ! {
|
|||
.with(InternalLogger::new(store.clone()))
|
||||
.init();
|
||||
|
||||
eframe::run_native( // TODO replace this with a loop that ends so we can cleanly exit the background worker
|
||||
eframe::run_native(
|
||||
// TODO replace this with a loop that ends so we can cleanly exit the background worker
|
||||
"dashboard",
|
||||
native_options,
|
||||
Box::new(move |cc| {
|
||||
|
|
Loading…
Reference in a new issue