use std::marker::PhantomData;
use eframe::{egui, egui::Vec2};
use gmt_dos_clients_transceiver::{CompactRecvr, Monitor, Transceiver, TransceiverError};
use interface::UniqueIdentifier;
use tokio::task::JoinError;
use tracing::debug;
mod signal;
use signal::{Signal, SignalProcessing};
use crate::{GmtScope, ImageScope, PlotScope, ScopeKind};
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
#[error("failed to build transceiver")]
Transceiver(#[from] TransceiverError),
#[error("some task didn't terminate successfully")]
Join(#[from] JoinError),
}
pub type Result<T> = std::result::Result<T, ClientError>;
pub struct XScope<K = PlotScope>
where
K: ScopeKind,
{
server_ip: String,
client_address: String,
monitor: Option<Monitor>,
signals: Vec<Box<dyn SignalProcessing>>,
min_recvr: Option<CompactRecvr>,
kind: PhantomData<K>,
}
impl<K: ScopeKind> XScope<K> {
pub fn new<S: Into<String>>(server_ip: S, client_address: S) -> Self {
Self {
monitor: Some(Monitor::new()),
server_ip: server_ip.into(),
client_address: client_address.into(),
signals: Vec::new(),
min_recvr: None,
kind: PhantomData,
}
}
pub fn signal<U>(mut self, port: u32) -> Result<Self>
where
U: UniqueIdentifier + 'static,
{
let server_address = format!("{}:{}", self.server_ip, port);
let rx = if let Some(min_recvr) = self.min_recvr.as_ref() {
min_recvr.spawn(server_address)?
} else {
let recvr = Transceiver::<crate::payload::ScopeData<U>>::receiver(
server_address,
&self.client_address,
)?;
self.min_recvr = Some(CompactRecvr::from(&recvr));
recvr
}
.run(self.monitor.as_mut().unwrap())
.take_channel_receiver();
self.signals.push(Box::new(Signal::new(rx)));
Ok(self)
}
pub fn run(mut self, ctx: egui::Context) -> Self {
debug!("scope run");
self.signals.iter_mut().for_each(|signal| {
let _ = signal.run(ctx.clone());
});
self
}
pub fn take_monitor(&mut self) -> Monitor {
self.monitor.take().unwrap()
}
}
impl<K> XScope<K>
where
XScope<K>: eframe::App,
K: ScopeKind + 'static,
{
pub fn show(mut self) {
let monitor = self.monitor.take().unwrap();
tokio::spawn(async move {
match monitor.join().await {
Ok(_) => println!("*** data streaming complete ***"),
Err(e) => println!("!!! data streaming error with {:?} !!!", e),
}
});
let native_options = eframe::NativeOptions {
initial_window_size: Some(Vec2::from(<K as ScopeKind>::window_size())),
..Default::default()
};
let _ = eframe::run_native(
"GMT DOS Actors Scope",
native_options,
Box::new(|cc| Box::new(self.run(cc.egui_ctx.clone()))),
);
}
}
pub type Scope = XScope<PlotScope>;
impl eframe::App for Scope {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
let plot = egui::plot::Plot::new("Scope").legend(Default::default());
plot.show(ui, |plot_ui: &mut egui::plot::PlotUi| {
for signal in &mut self.signals {
signal.plot_ui(plot_ui)
}
});
});
}
}
pub type Shot = XScope<ImageScope>;
impl eframe::App for Shot {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
let plot = egui::plot::Plot::new("Scope")
.show_x(false)
.show_y(false)
.allow_scroll(false)
.data_aspect(1f32);
plot.show(ui, |plot_ui: &mut egui::plot::PlotUi| {
for signal in &mut self.signals {
signal.plot_ui(plot_ui)
}
});
});
}
}
pub type GmtShot = XScope<GmtScope>;
impl eframe::App for GmtShot {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
for signal in &mut self.signals {
signal.plot_stats_ui(ctx)
}
egui::CentralPanel::default().show(ctx, |ui| {
let plot = egui::plot::Plot::new("Scope")
.show_x(false)
.show_y(false)
.allow_scroll(false)
.data_aspect(1f32);
plot.show(ui, |plot_ui: &mut egui::plot::PlotUi| {
for signal in &mut self.signals {
signal.plot_ui(plot_ui)
}
});
});
}
}