use crossterm::event::{Event, EventStream, KeyCode};
use futures::StreamExt;
use systemstat::Duration;
use std::error::Error;
use std::sync::{Arc, Mutex};
use crate::display::state::UIState;
use crate::display::ui::{draw, init_ui, shutdown_ui};
use crate::monitoring::polling::{SystemPollResult, SystemPoller, SystemPollerTarget};
use crate::monitoring::system::SystemData;
use crate::ringbuffer::RingBuffer;
enum MFAMessage {
Exit,
}
pub struct MainFrameApp {
refresh_rate: f32,
poll_rate: f32,
}
impl Default for MainFrameApp {
fn default() -> Self {
Self::new()
}
}
impl MainFrameApp {
pub fn with_refresh_rate(mut self, hz: f32) -> Self {
self.refresh_rate = hz;
self
}
pub fn with_poll_rate(mut self, hz: f32) -> Self {
self.poll_rate = hz;
self
}
pub fn new() -> Self {
MainFrameApp {
refresh_rate: 20.0,
poll_rate: 1.0,
}
}
pub async fn run(self) -> Result<(), Box<dyn Error>> {
let mut terminal = init_ui()?;
defer! {
shutdown_ui().unwrap();
}
let app_state = UIState::new_shared();
let app_data = Arc::new(Mutex::new(SystemData::new_from_poll()));
let poll_results = Arc::new(Mutex::new(RingBuffer::<SystemPollResult>::new(1)));
let mut system_poller = SystemPoller::new().with_poll_targets(vec![
SystemPollerTarget::CpuUsage,
SystemPollerTarget::CpuTemperature,
SystemPollerTarget::Gpu,
SystemPollerTarget::Memory,
]);
poll_results.lock().unwrap().add(system_poller.poll());
let _app_state_handle = app_state.clone();
let _app_data_handle = app_data.clone();
let _poll_result_handle_poll_thread = poll_results.clone();
let _poll_result_handle_draw_thread = poll_results.clone();
let (ui_tx, mut ui_rx) = tokio::sync::mpsc::unbounded_channel::<MFAMessage>();
let mut redraw_interval =
tokio::time::interval(Duration::from_secs_f32(1.0 / self.refresh_rate));
let mut polling_interval =
tokio::time::interval(Duration::from_secs_f32(1.0 / self.poll_rate));
let ui_thread = tokio::spawn(async move {
loop {
redraw_interval.tick().await;
let msg = match ui_rx.try_recv() {
Ok(x) => Some(x),
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => None,
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
return;
}
};
match msg {
Some(MFAMessage::Exit) => {
return;
}
_ => (),
};
{
let mut s = _app_state_handle.lock().unwrap();
let d = _app_data_handle.lock().unwrap();
let r = _poll_result_handle_draw_thread.lock().unwrap();
terminal.draw(|f| draw(&mut s, &d, &r, f)).unwrap();
}
}
});
let polling_thread = tokio::spawn(async move {
loop {
polling_interval.tick().await;
let poll_result = system_poller.poll();
{
let mut p = _poll_result_handle_poll_thread.lock().unwrap();
p.add(poll_result);
}
}
});
let mut events = EventStream::new();
'mainloop: loop {
match events.next().await {
Some(Ok(Event::Key(evnt))) => match evnt.code {
KeyCode::Char('q') => {
ui_tx.send(MFAMessage::Exit).unwrap();
break 'mainloop;
}
_ => (),
},
Some(Err(e)) => return Err(Box::new(e)),
None => break 'mainloop,
_ => (),
};
}
ui_thread.abort();
polling_thread.abort();
Ok(())
}
}