1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use super::{ClientError, Scope};
use eframe::egui;
use interface::UniqueIdentifier;

const PLOT_SIZE: (f32, f32) = (600f32, 500f32);
const MAX_WINDOW_SIZE: (f32, f32) = (1200f32, 1000f32);

#[derive(Debug, thiserror::Error)]
pub enum GridScopeError {
    #[error("failed to create the scope within the grid")]
    Pin(#[from] ClientError),
}
pub type Result<T> = std::result::Result<T, GridScopeError>;

struct NodeScope {
    indices: (usize, usize),
    scope: Scope,
}

/// Display [Scope]s in a grid like pattern
pub struct GridScope {
    size: (usize, usize),
    scopes: Vec<NodeScope>,
    plot_size: (f32, f32),
    server_ip: String,
    client_address: String,
}
impl GridScope {
    /// Creates a new grid layout for [Scope]s
    ///
    /// `size` sets the number of rows and columns
    pub fn new(size: (usize, usize), server_ip: impl Into<String>) -> Self {
        let (rows, cols) = size;
        let width = MAX_WINDOW_SIZE.0.min(PLOT_SIZE.0 * cols as f32) / cols as f32;
        let height = MAX_WINDOW_SIZE.1.min(PLOT_SIZE.1 * rows as f32) / rows as f32;
        Self {
            size,
            scopes: vec![],
            plot_size: (width, height),
            server_ip: server_ip.into(),
            client_address: "0.0.0.0:0".into(),
        }
    }
    /// Sets the client address
    pub fn client_address(mut self, client_address: impl Into<String>) -> Self {
        self.client_address = client_address.into();
        self
    }
    fn window_size(&self) -> (f32, f32) {
        let (rows, cols) = self.size;
        let (width, height) = self.plot_size;
        (width * cols as f32, height * rows as f32)
    }
    /// Sets a [Scope] at position `(row,column)` in the grid layout
    pub fn pin<U>(mut self, indices: (usize, usize), port: u32) -> Result<Self>
    where
        U: UniqueIdentifier + 'static,
    {
        let (rows, cols) = self.size;
        let (row, col) = indices;
        assert!(
            row < rows,
            "The row index in the scopes grid must be less than {}",
            rows
        );
        assert!(
            col < cols,
            "The columm index in the scopes grid must be less than {}",
            cols
        );
        self.scopes.push(NodeScope {
            indices,
            scope: Scope::new(&self.server_ip, &self.client_address).signal::<U>(port)?,
        });
        Ok(self)
    }
    /// Display the scope
    pub fn show(mut self) {
        for node in self.scopes.iter_mut() {
            let monitor = node.scope.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(egui::Vec2::from(self.window_size())),
            ..Default::default()
        };
        let _ = eframe::run_native(
            "GMT DOS Actors Scope",
            native_options,
            Box::new(|cc| {
                for node in self.scopes.iter_mut() {
                    let scope = &mut node.scope;
                    scope.run(cc.egui_ctx.clone());
                }
                Box::new(self)
            }),
        );
    }
}

impl eframe::App for GridScope {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            let (rows, cols) = self.size;
            let style = ui.style_mut();
            style.spacing.item_spacing = egui::vec2(0.0, 0.0);
            for row in 0..rows {
                ui.horizontal(|ui| {
                    for col in 0..cols {
                        self.scopes
                            .iter_mut()
                            .find(|node| node.indices == (row, col))
                            .map(|node| {
                                let plot = egui::plot::Plot::new("Scope")
                                    .legend(Default::default())
                                    .width(self.plot_size.0)
                                    .height(self.plot_size.1)
                                    .set_margin_fraction(egui::Vec2::from((0.05, 0.05)));
                                plot.show(ui, |plot_ui: &mut egui::plot::PlotUi| {
                                    for signal in &mut node.scope.signals {
                                        signal.plot_ui(plot_ui)
                                    }
                                });
                            });
                    }
                });
            }
        });
    }
}