Skip to content

Commit c754025

Browse files
committed
Add SpeedTestApp functionality: Implement lifecycle management for speed tests, including start and stop operations, state management, and error handling. Introduce UI components for configuration and results display, enhancing user interaction and experience.
1 parent 3e6a30e commit c754025

File tree

5 files changed

+451
-0
lines changed

5 files changed

+451
-0
lines changed

src/ui/app/lifecycle.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use super::super::test_management::start_test;
2+
3+
use super::state::SpeedTestApp;
4+
use crate::ui::console::log_to_console;
5+
6+
impl SpeedTestApp {
7+
pub fn start_test_impl(&mut self) {
8+
self.is_running = true;
9+
self.error_message = None;
10+
self.show_error_modal = false;
11+
self.error_modal_message.clear();
12+
self.results.lock().unwrap().clear();
13+
self.current_throughput = 0.0;
14+
self.current_reads = 0;
15+
self.current_latency = 0.0;
16+
self.max_throughput = 0.0;
17+
self.test_start_time = Some(std::time::Instant::now());
18+
self.overall_test_start_time = Some(std::time::Instant::now());
19+
self.test_end_time = None;
20+
self.current_test_size = None;
21+
self.completed_chunks.clear();
22+
23+
let (modal_tx, modal_rx) = std::sync::mpsc::channel::<String>();
24+
let (stats_tx, stats_rx) = tokio::sync::mpsc::channel(100);
25+
self.stats_rx = Some(stats_rx);
26+
self.modal_rx = Some(modal_rx);
27+
28+
match start_test(
29+
self.connector,
30+
self.pcileech_device.clone(),
31+
self.duration,
32+
&self.test_sizes,
33+
&self.console,
34+
modal_tx,
35+
stats_tx,
36+
) {
37+
Ok(test) => {
38+
self.test = Some(test);
39+
}
40+
Err(error_msg) => {
41+
log_to_console(&self.console, &error_msg);
42+
self.show_error_modal = true;
43+
self.error_modal_message = error_msg;
44+
self.is_running = false;
45+
self.show_config = true;
46+
self.modal_rx = None;
47+
}
48+
}
49+
}
50+
51+
pub fn stop_test_impl(&mut self) {
52+
self.is_running = false;
53+
self.test = None;
54+
self.stats_rx = None;
55+
56+
if let Some(overall_start_time) = self.overall_test_start_time {
57+
let final_time = overall_start_time.elapsed().as_secs_f64();
58+
self.test_end_time = Some(final_time);
59+
60+
if let Some(current_size) = self.current_test_size
61+
&& !self
62+
.completed_chunks
63+
.iter()
64+
.any(|(s, _)| *s == current_size)
65+
{
66+
self.completed_chunks.push((current_size, final_time));
67+
}
68+
}
69+
self.test_start_time = None;
70+
self.overall_test_start_time = None;
71+
self.current_test_size = None;
72+
self.modal_rx = None;
73+
log_to_console(&self.console, "Test stopped");
74+
}
75+
}

src/ui/app/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod lifecycle;
2+
mod state;
3+
mod update;
4+
5+
pub use state::SpeedTestApp;

src/ui/app/state.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use std::sync::{Arc, Mutex};
2+
use std::time::Instant;
3+
4+
use tokio::sync::mpsc;
5+
6+
use crate::speedtest::{Connector, SpeedTest};
7+
use crate::ui::console::ConsoleWindow;
8+
9+
use super::super::constants::DEFAULT_PLOT_HEIGHT;
10+
use super::super::constants::DEFAULT_PLOT_WIDTH;
11+
use super::super::types::PlotResizeDirection;
12+
use super::super::types::TestResults;
13+
14+
pub struct SpeedTestApp {
15+
pub connector: Connector,
16+
pub pcileech_device: String,
17+
pub duration: u64,
18+
pub test: Option<SpeedTest>,
19+
pub results: TestResults,
20+
pub is_running: bool,
21+
pub was_running: bool,
22+
pub error_message: Option<String>,
23+
pub current_throughput: f64,
24+
pub current_reads: u64,
25+
pub current_latency: f64,
26+
pub show_config: bool,
27+
#[allow(clippy::type_complexity)]
28+
pub stats_rx: Option<mpsc::Receiver<(f64, u64, f64, usize, f64)>>, // (throughput, reads_per_sec, elapsed_secs, read_size, latency_us)
29+
pub test_start_time: Option<Instant>,
30+
pub overall_test_start_time: Option<Instant>,
31+
pub test_end_time: Option<f64>,
32+
pub current_test_size: Option<usize>,
33+
pub completed_chunks: Vec<(usize, f64)>,
34+
pub max_throughput: f64,
35+
pub console: ConsoleWindow,
36+
pub ui_scale: f32,
37+
pub ui_scale_text: String,
38+
pub test_sizes: Vec<(usize, bool)>,
39+
pub show_error_modal: bool,
40+
pub error_modal_message: String,
41+
pub modal_rx: Option<std::sync::mpsc::Receiver<String>>,
42+
pub custom_plot_width: f32,
43+
pub custom_plot_height: f32,
44+
pub plot_resize_start_time: Option<std::time::Instant>,
45+
pub plot_resize_direction: PlotResizeDirection,
46+
pub plot_resize_last_repeat: Option<std::time::Instant>,
47+
}
48+
49+
impl Default for SpeedTestApp {
50+
fn default() -> Self {
51+
Self::new()
52+
}
53+
}
54+
55+
impl SpeedTestApp {
56+
pub fn new() -> Self {
57+
Self {
58+
connector: Connector::default(),
59+
pcileech_device: String::new(),
60+
duration: 10,
61+
test: None,
62+
results: Arc::new(Mutex::new(Vec::new())), // (read_size, (throughput_points, reads_points, latency_points))
63+
is_running: false,
64+
was_running: false,
65+
error_message: None,
66+
current_throughput: 0.0,
67+
current_reads: 0,
68+
current_latency: 0.0,
69+
show_config: true,
70+
stats_rx: None,
71+
test_start_time: None,
72+
overall_test_start_time: None,
73+
test_end_time: None,
74+
current_test_size: None,
75+
completed_chunks: Vec::new(),
76+
max_throughput: 0.0,
77+
console: ConsoleWindow::new(),
78+
ui_scale: 1.0,
79+
ui_scale_text: "1.0".to_string(),
80+
show_error_modal: false,
81+
error_modal_message: String::new(),
82+
modal_rx: None,
83+
custom_plot_width: DEFAULT_PLOT_WIDTH,
84+
custom_plot_height: DEFAULT_PLOT_HEIGHT,
85+
plot_resize_start_time: None,
86+
plot_resize_direction: PlotResizeDirection::None,
87+
plot_resize_last_repeat: None,
88+
test_sizes: vec![
89+
(512, false), // 512 bytes
90+
(1024, false), // 1KB
91+
(2048, false), // 2KB
92+
(4096, true), // 4KB - selected by default
93+
(8192, true), // 8KB - selected by default
94+
(16384, true), // 16KB - selected by default
95+
(32768, true), // 32KB - selected by default
96+
(65536, false), // 64KB
97+
(131072, false), // 128KB
98+
],
99+
}
100+
}
101+
}

src/ui/app/update.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use anyhow::Result;
2+
use eframe::egui;
3+
4+
use crate::ui::config_panel::render_config_panel;
5+
use crate::ui::modal::show_modal;
6+
use crate::ui::results_panel::render_results_panel;
7+
use crate::ui::test_management::handle_stats_update;
8+
use crate::ui::types::{
9+
ConfigParams, PlotControls, ResultsPanelParams, StatsUpdateParams, TestState,
10+
};
11+
12+
use super::state::SpeedTestApp;
13+
14+
impl SpeedTestApp {
15+
pub fn run(self) -> Result<()> {
16+
let options = eframe::NativeOptions {
17+
window_builder: Some(Box::new(|builder| {
18+
builder
19+
.with_inner_size(egui::vec2(
20+
super::super::constants::CONFIG_WINDOW_MIN_WIDTH,
21+
super::super::constants::CONFIG_WINDOW_MIN_HEIGHT,
22+
))
23+
.with_min_inner_size(egui::vec2(
24+
super::super::constants::CONFIG_WINDOW_MIN_WIDTH,
25+
super::super::constants::CONFIG_WINDOW_MIN_HEIGHT,
26+
))
27+
.with_max_inner_size(egui::vec2(
28+
super::super::constants::CONFIG_WINDOW_MAX_WIDTH,
29+
super::super::constants::CONFIG_WINDOW_MAX_HEIGHT,
30+
))
31+
.with_resizable(true)
32+
.with_decorations(true)
33+
.with_title("DMA Speed Test")
34+
})),
35+
..Default::default()
36+
};
37+
38+
eframe::run_native(
39+
"DMA Speed Test",
40+
options,
41+
Box::new(|cc| {
42+
let mut fonts = egui::FontDefinitions::default();
43+
egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular);
44+
cc.egui_ctx.set_fonts(fonts);
45+
46+
Ok::<Box<dyn eframe::App>, Box<dyn std::error::Error + Send + Sync>>(Box::new(self))
47+
}),
48+
)
49+
.map_err(|e| anyhow::anyhow!("Failed to run GUI: {e}"))?;
50+
51+
Ok(())
52+
}
53+
}
54+
55+
impl eframe::App for SpeedTestApp {
56+
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
57+
ctx.set_pixels_per_point(self.ui_scale * 1.3);
58+
59+
if self.is_running && !self.was_running {
60+
ctx.send_viewport_cmd(egui::ViewportCommand::MinInnerSize(egui::vec2(
61+
super::super::constants::TEST_WINDOW_MIN_WIDTH,
62+
super::super::constants::TEST_WINDOW_MIN_HEIGHT,
63+
)));
64+
ctx.send_viewport_cmd(egui::ViewportCommand::MaxInnerSize(egui::vec2(
65+
super::super::constants::TEST_WINDOW_MAX_WIDTH,
66+
super::super::constants::TEST_WINDOW_MAX_HEIGHT,
67+
)));
68+
ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(egui::vec2(
69+
super::super::constants::TEST_WINDOW_MIN_WIDTH,
70+
super::super::constants::TEST_WINDOW_MIN_HEIGHT,
71+
)));
72+
}
73+
self.was_running = self.is_running;
74+
75+
self.console.show(ctx);
76+
77+
if self.show_error_modal {
78+
let message = self.error_modal_message.clone();
79+
let mut on_close = || {
80+
self.show_error_modal = false;
81+
};
82+
show_modal(ctx, &message, &mut on_close);
83+
if !self.show_error_modal {
84+
self.error_modal_message.clear();
85+
}
86+
return;
87+
}
88+
89+
if self.is_running
90+
&& let Some(rx) = &mut self.stats_rx
91+
{
92+
let mut stats_params = StatsUpdateParams {
93+
current_throughput: &mut self.current_throughput,
94+
current_reads: &mut self.current_reads,
95+
current_latency: &mut self.current_latency,
96+
current_test_size: &mut self.current_test_size,
97+
test_start_time: &mut self.test_start_time,
98+
max_throughput: &mut self.max_throughput,
99+
completed_chunks: &mut self.completed_chunks,
100+
};
101+
102+
let stats_closed =
103+
handle_stats_update(rx, &mut stats_params, &self.results, &self.console);
104+
105+
if stats_closed {
106+
self.stop_test_impl();
107+
}
108+
}
109+
110+
if let Some(rx) = &self.modal_rx
111+
&& let Ok(error_msg) = rx.try_recv()
112+
{
113+
self.show_error_modal = true;
114+
self.error_modal_message = error_msg;
115+
self.stop_test_impl();
116+
self.show_config = true;
117+
}
118+
119+
if self.show_config {
120+
egui::CentralPanel::default().show(ctx, |ui| {
121+
let mut should_start_test = false;
122+
let mut config_params = ConfigParams {
123+
connector: &mut self.connector,
124+
pcileech_device: &mut self.pcileech_device,
125+
duration: &mut self.duration,
126+
ui_scale: &mut self.ui_scale,
127+
ui_scale_text: &mut self.ui_scale_text,
128+
test_sizes: &mut self.test_sizes,
129+
show_error_modal: &mut self.show_error_modal,
130+
error_modal_message: &mut self.error_modal_message,
131+
show_config: &mut self.show_config,
132+
};
133+
render_config_panel(ui, &mut config_params, || should_start_test = true);
134+
if should_start_test {
135+
self.start_test_impl();
136+
}
137+
});
138+
} else {
139+
egui::CentralPanel::default().show(ctx, |ui| {
140+
let mut should_stop_test = false;
141+
let mut should_start_test = false;
142+
let mut should_toggle_console = false;
143+
144+
let plot_controls = PlotControls {
145+
custom_plot_width: &mut self.custom_plot_width,
146+
custom_plot_height: &mut self.custom_plot_height,
147+
plot_resize_start_time: &mut self.plot_resize_start_time,
148+
plot_resize_direction: &mut self.plot_resize_direction,
149+
plot_resize_last_repeat: &mut self.plot_resize_last_repeat,
150+
};
151+
152+
let test_state = TestState {
153+
is_running: self.is_running,
154+
current_throughput: self.current_throughput,
155+
current_reads: self.current_reads,
156+
current_latency: self.current_latency,
157+
current_test_size: self.current_test_size,
158+
test_start_time: self.test_start_time,
159+
test_end_time: self.test_end_time,
160+
completed_chunks: &self.completed_chunks,
161+
};
162+
163+
let mut results_params = ResultsPanelParams {
164+
results: &self.results,
165+
duration: self.duration,
166+
plot_controls,
167+
console: &self.console,
168+
ui_scale: &mut self.ui_scale,
169+
ui_scale_text: &mut self.ui_scale_text,
170+
test_state,
171+
test_sizes: &self.test_sizes,
172+
show_config: &mut self.show_config,
173+
};
174+
175+
render_results_panel(
176+
ui,
177+
&mut results_params,
178+
|| should_stop_test = true,
179+
|| should_start_test = true,
180+
&mut should_toggle_console,
181+
);
182+
183+
if should_stop_test {
184+
self.stop_test_impl();
185+
}
186+
if should_start_test {
187+
self.start_test_impl();
188+
}
189+
if should_toggle_console {
190+
self.console.toggle();
191+
}
192+
});
193+
}
194+
195+
if self.is_running {
196+
ctx.request_repaint();
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)