Skip to main content

csi_webclient/ui/
dashboard.rs

1use crate::state::AppState;
2
3/// Render the dashboard view.
4pub fn render(ui: &mut egui::Ui, state: &mut AppState) {
5    ui.heading("Dashboard");
6    ui.separator();
7
8    ui.horizontal_wrapped(|ui| {
9        ui.strong("Serial:");
10        ui.label(format_tristate(state.runtime.serial_connected, "connected", "disconnected"));
11        ui.separator();
12
13        ui.strong("Firmware:");
14        ui.label(format_tristate(state.runtime.firmware_verified, "verified", "unverified"));
15        ui.separator();
16
17        ui.strong("Collection:");
18        ui.label(format_tristate(state.runtime.collection_running, "running", "idle"));
19        ui.separator();
20
21        ui.strong("Port:");
22        ui.label(state.runtime.port_path.clone().unwrap_or_else(|| "?".to_owned()));
23    });
24
25    ui.separator();
26
27    if let Some(info) = &state.runtime.latest_info {
28        ui.horizontal_wrapped(|ui| {
29            ui.strong("Name:");
30            ui.label(info.name.clone().unwrap_or_default());
31            ui.separator();
32
33            ui.strong("Version:");
34            ui.label(info.version.clone().unwrap_or_default());
35            ui.separator();
36
37            ui.strong("Chip:");
38            ui.label(info.chip.clone().unwrap_or_default());
39            ui.separator();
40
41            ui.strong("Protocol:");
42            ui.label(
43                info.protocol
44                    .map(|v| v.to_string())
45                    .unwrap_or_else(|| "?".to_owned()),
46            );
47            ui.separator();
48
49            ui.strong("Features:");
50            ui.label(info.features.join(", "));
51        });
52        ui.separator();
53    }
54
55    ui.horizontal_wrapped(|ui| {
56        ui.strong("Collection role:");
57        ui.label(state.persistent.collection_mode.as_api_value());
58        ui.separator();
59
60        ui.strong("Output mode:");
61        ui.label(state.persistent.output_mode.as_api_value());
62        ui.separator();
63
64        ui.strong("Log mode:");
65        ui.label(state.persistent.log_mode.as_api_value());
66    });
67
68    ui.separator();
69
70    ui.horizontal_wrapped(|ui| {
71        ui.label(format!("HTTP Base: {}", state.base_http_url()));
72        ui.separator();
73        ui.label(format!(
74            "WebSocket: {}",
75            if state.runtime.ws_connected {
76                "Connected"
77            } else {
78                "Disconnected"
79            }
80        ));
81        ui.separator();
82        ui.label(format!("Frames: {}", state.runtime.frames_received));
83        ui.separator();
84        ui.label(format!("Bytes: {}", state.runtime.bytes_received));
85    });
86
87    ui.separator();
88    ui.label("Recent events");
89    egui::ScrollArea::vertical()
90        .auto_shrink([false, false])
91        .show(ui, |ui| {
92            for line in state.runtime.events.iter().rev().take(80) {
93                ui.add(egui::Label::new(line).wrap());
94            }
95        });
96}
97
98fn format_tristate(value: Option<bool>, true_label: &str, false_label: &str) -> String {
99    match value {
100        Some(true) => true_label.to_owned(),
101        Some(false) => false_label.to_owned(),
102        None => "?".to_owned(),
103    }
104}