mirror of
https://github.com/hubaldv/bioz-host-rs.git
synced 2026-07-23 16:47:43 +00:00
895
Cargo.lock
generated
895
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -5,14 +5,14 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "1.0.1" }
|
defmt = { version = "1.0.1" }
|
||||||
eframe = { version = "0.33.3"}
|
eframe = { version = "0.34.1"}
|
||||||
egui_plot = "0.34.0"
|
egui_plot = "0.35.0"
|
||||||
egui_dock = "0.18.0"
|
egui_dock = "0.19.1"
|
||||||
egui_extras = "0.33.3"
|
egui_extras = "0.34.1"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
simple_logger = "5.0.0"
|
simple_logger = "5.2.0"
|
||||||
atomic_float = "1.1.0"
|
atomic_float = "1.1.0"
|
||||||
chrono = { version = "0.4.43" }
|
chrono = { version = "0.4.44" }
|
||||||
rfd = { version = "0.17.2" }
|
rfd = { version = "0.17.2" }
|
||||||
|
|
||||||
[dependencies.bioz-icd-rs]
|
[dependencies.bioz-icd-rs]
|
||||||
@@ -32,7 +32,7 @@ version = "0.2.5"
|
|||||||
features = ["derive", "heapless_v0_8"]
|
features = ["derive", "heapless_v0_8"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.49.0"
|
version = "1.52.1"
|
||||||
features = [
|
features = [
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"macros",
|
"macros",
|
||||||
|
|||||||
157
src/app.rs
157
src/app.rs
@@ -716,9 +716,9 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for App {
|
impl eframe::App for App {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
||||||
// Egui add a top bar
|
// Egui add a top bar
|
||||||
egui::TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
egui::Panel::top("top_bar").show_inside(ui, |ui| {
|
||||||
egui::MenuBar::new().ui(ui, |ui| {
|
egui::MenuBar::new().ui(ui, |ui| {
|
||||||
let is_connected = self.hardware_state_rx.borrow().hardware_connected != HardwareConnected::None;
|
let is_connected = self.hardware_state_rx.borrow().hardware_connected != HardwareConnected::None;
|
||||||
|
|
||||||
@@ -782,9 +782,69 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.add_enabled_ui(gui_logging_state == LoggingState::Idle, |ui| {
|
||||||
|
ui.label("Log filename:");
|
||||||
|
TextEdit::singleline(&mut *self.log_filename.lock().unwrap()).desired_width(150.0).id(Id::new("file_name_field")).ui(ui);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spacer to push the LED to the right
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
ui.scope(|ui| {
|
||||||
|
let (color, tooltip) = match self.hardware_state_rx.borrow().hardware_connected {
|
||||||
|
HardwareConnected::None => {
|
||||||
|
(Color32::DARK_RED, "Disconnected")
|
||||||
|
},
|
||||||
|
HardwareConnected::WithoutMultiplexer => {
|
||||||
|
(Color32::DARK_GREEN, "Connected without multiplexer")
|
||||||
|
},
|
||||||
|
HardwareConnected::WithMultiplexer => {
|
||||||
|
(Color32::DARK_GREEN, "Connected with multiplexer")
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate a fixed-size rectangle for the LED
|
||||||
|
let led_size = egui::Vec2::splat(12.0);
|
||||||
|
let (rect, response) = ui.allocate_exact_size(led_size, egui::Sense::hover());
|
||||||
|
|
||||||
|
// Draw the circle
|
||||||
|
let center = rect.center();
|
||||||
|
let radius = 5.0;
|
||||||
|
ui.painter().circle_filled(center, radius, color);
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
if response.hovered() {
|
||||||
|
response.on_hover_text(tooltip);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show connection status in the bottom panel, green if connected
|
||||||
|
if self.app_state_rx.borrow().tcp_connected {
|
||||||
|
let frame = egui::Frame::default().fill(Color32::DARK_GREEN);
|
||||||
|
|
||||||
|
egui::Panel::bottom("bottom_panel")
|
||||||
|
.frame(frame)
|
||||||
|
.show_inside(ui, |ui| {
|
||||||
|
ui.with_layout(
|
||||||
|
egui::Layout::centered_and_justified(egui::Direction::LeftToRight),
|
||||||
|
|ui| {
|
||||||
|
ui.label(
|
||||||
|
RichText::new("TCP interface active")
|
||||||
|
.color(Color32::WHITE),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show log marker input field (or marker modal) if log_marker_modal is true
|
||||||
if self.log_marker_modal {
|
if self.log_marker_modal {
|
||||||
if Modal::new(Id::new("modal_marker"))
|
if Modal::new(Id::new("modal_marker"))
|
||||||
.show(ctx, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Add marker");
|
ui.heading("Add marker");
|
||||||
|
|
||||||
@@ -828,68 +888,9 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||||
|
|
||||||
ui.add_enabled_ui(gui_logging_state == LoggingState::Idle, |ui| {
|
|
||||||
ui.label("Log filename:");
|
|
||||||
TextEdit::singleline(&mut *self.log_filename.lock().unwrap()).desired_width(150.0).id(Id::new("file_name_field")).ui(ui);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Spacer to push the LED to the right
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
ui.scope(|ui| {
|
|
||||||
let (color, tooltip) = match self.hardware_state_rx.borrow().hardware_connected {
|
|
||||||
HardwareConnected::None => {
|
|
||||||
(Color32::DARK_RED, "Disconnected")
|
|
||||||
},
|
|
||||||
HardwareConnected::WithoutMultiplexer => {
|
|
||||||
(Color32::DARK_GREEN, "Connected without multiplexer")
|
|
||||||
},
|
|
||||||
HardwareConnected::WithMultiplexer => {
|
|
||||||
(Color32::DARK_GREEN, "Connected with multiplexer")
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate a fixed-size rectangle for the LED
|
|
||||||
let led_size = egui::Vec2::splat(12.0);
|
|
||||||
let (rect, response) = ui.allocate_exact_size(led_size, egui::Sense::hover());
|
|
||||||
|
|
||||||
// Draw the circle
|
|
||||||
let center = rect.center();
|
|
||||||
let radius = 5.0;
|
|
||||||
ui.painter().circle_filled(center, radius, color);
|
|
||||||
|
|
||||||
// Tooltip
|
|
||||||
if response.hovered() {
|
|
||||||
response.on_hover_text(tooltip);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show connection status in the bottom panel, green if connected
|
|
||||||
if self.app_state_rx.borrow().tcp_connected {
|
|
||||||
let frame = egui::Frame::default().fill(Color32::DARK_GREEN);
|
|
||||||
|
|
||||||
egui::TopBottomPanel::bottom("bottom_panel")
|
|
||||||
.frame(frame)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.with_layout(
|
|
||||||
egui::Layout::centered_and_justified(egui::Direction::LeftToRight),
|
|
||||||
|ui| {
|
|
||||||
ui.label(
|
|
||||||
RichText::new("TCP interface active")
|
|
||||||
.color(Color32::WHITE),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
DockArea::new(&mut self.tree)
|
DockArea::new(&mut self.tree)
|
||||||
.style(Style::from_egui(ctx.style().as_ref()))
|
.style(Style::from_egui(ui.style().as_ref()))
|
||||||
.show_leaf_close_all_buttons(false)
|
.show_leaf_close_all_buttons(false)
|
||||||
.show_leaf_collapse_buttons(false)
|
.show_leaf_collapse_buttons(false)
|
||||||
.show_close_buttons(false)
|
.show_close_buttons(false)
|
||||||
@@ -900,49 +901,49 @@ impl eframe::App for App {
|
|||||||
if let Some((_, name)) = self.tree.find_active_focused() {
|
if let Some((_, name)) = self.tree.find_active_focused() {
|
||||||
if self.app_state_rx.borrow().tab_active.to_string() != *name {
|
if self.app_state_rx.borrow().tab_active.to_string() != *name {
|
||||||
let active_tab = self.app_state_rx.borrow().tab_active.to_string();
|
let active_tab = self.app_state_rx.borrow().tab_active.to_string();
|
||||||
self.tree.set_active_tab(self.tree.find_tab(&active_tab).unwrap());
|
self.tree.set_active_tab(self.tree.find_tab(&active_tab).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let active_tab = self.app_state_rx.borrow().tab_active.to_string();
|
let active_tab = self.app_state_rx.borrow().tab_active.to_string();
|
||||||
self.tree.set_active_tab(self.tree.find_tab(&active_tab).unwrap());
|
self.tree.set_active_tab(self.tree.find_tab(&active_tab).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CMD- or control-W to close window
|
// CMD- or control-W to close window
|
||||||
if ctx.input(|i| i.modifiers.cmd_ctrl_matches(Modifiers::COMMAND))
|
if ui.input(|i| i.modifiers.cmd_ctrl_matches(Modifiers::COMMAND))
|
||||||
&& ctx.input(|i| i.key_pressed(Key::W))
|
&& ui.input(|i| i.key_pressed(Key::W))
|
||||||
{
|
{
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
ui.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the file name field is focused
|
// Check if the file name field is focused
|
||||||
// If it is, we don't want to trigger shortcuts
|
// If it is, we don't want to trigger shortcuts
|
||||||
let file_name_field_is_focused = ctx.memory(|memory| memory.has_focus(Id::new("file_name_field")));
|
let file_name_field_is_focused = ui.memory(|memory| memory.has_focus(Id::new("file_name_field")));
|
||||||
let marker_field_is_focused = ctx.memory(|memory| memory.has_focus(Id::new("marker_field")));
|
let marker_field_is_focused = ui.memory(|memory| memory.has_focus(Id::new("marker_field")));
|
||||||
if !file_name_field_is_focused && !marker_field_is_focused {
|
if !file_name_field_is_focused && !marker_field_is_focused {
|
||||||
|
|
||||||
// Space to start/stop measurement
|
// Space to start/stop measurement
|
||||||
if ctx.input(|i| i.key_pressed(Key::Space))
|
if ui.input(|i| i.key_pressed(Key::Space))
|
||||||
{
|
{
|
||||||
self.toggle_start_stop();
|
self.toggle_start_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send stop command when the window is closed
|
// Send stop command when the window is closed
|
||||||
if ctx.input(|i| i.viewport().close_requested()) {
|
if ui.input(|i| i.viewport().close_requested()) {
|
||||||
self.toggle_start_stop();
|
self.toggle_start_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset view
|
// Reset view
|
||||||
if ctx.input(|i| i.key_pressed(Key::R)) {
|
if ui.input(|i| i.key_pressed(Key::R)) {
|
||||||
self.reset_view();
|
self.reset_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle marker modal
|
// Toggle marker modal
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::A)) && *self.logging_state_rx.borrow() == LoggingState::Logging {
|
if ui.input(|i| i.key_pressed(egui::Key::A)) && *self.logging_state_rx.borrow() == LoggingState::Logging {
|
||||||
self.log_marker_modal = !self.log_marker_modal;
|
self.log_marker_modal = !self.log_marker_modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable/disable GUI logging
|
// Enable/disable GUI logging
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::L)) {
|
if ui.input(|i| i.key_pressed(egui::Key::L)) {
|
||||||
let gui_logging_enabled = *self.logging_state_rx.borrow();
|
let gui_logging_enabled = *self.logging_state_rx.borrow();
|
||||||
match gui_logging_enabled {
|
match gui_logging_enabled {
|
||||||
LoggingState::Starting => {
|
LoggingState::Starting => {
|
||||||
@@ -963,7 +964,7 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Toggle setttings view
|
// Toggle setttings view
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::S)) {
|
if ui.input(|i| i.key_pressed(egui::Key::S)) {
|
||||||
self.tab_viewer.show_settings = !self.tab_viewer.show_settings;
|
self.tab_viewer.show_settings = !self.tab_viewer.show_settings;
|
||||||
if self.tab_viewer.show_settings {
|
if self.tab_viewer.show_settings {
|
||||||
self.tab_viewer.show_settings_toggle = Some(true);
|
self.tab_viewer.show_settings_toggle = Some(true);
|
||||||
@@ -973,7 +974,7 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.request_repaint();
|
ui.request_repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/plot.rs
10
src/plot.rs
@@ -31,16 +31,16 @@ impl TimeSeriesPlot {
|
|||||||
self.values.push_back(PlotPoint::new(last_x + 1.0, val));
|
self.values.push_back(PlotPoint::new(last_x + 1.0, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plot_values(&self) -> PlotPoints {
|
pub fn plot_values(&self) -> PlotPoints<'_> {
|
||||||
PlotPoints::Owned(Vec::from_iter(self.values.iter().copied()))
|
PlotPoints::Owned(Vec::from_iter(self.values.iter().copied()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plot_values_negative(&self) -> PlotPoints {
|
pub fn plot_values_negative(&self) -> PlotPoints<'_> {
|
||||||
let mut values = Vec::from_iter(self.values.iter().copied());
|
let mut values = Vec::from_iter(self.values.iter().copied());
|
||||||
for point in &mut values {
|
for point in &mut values {
|
||||||
point.y = -point.y;
|
point.y = -point.y;
|
||||||
}
|
}
|
||||||
PlotPoints::Owned(Vec::from_iter(values))
|
PlotPoints::Owned(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
@@ -74,11 +74,11 @@ impl BodePlot {
|
|||||||
self.phases = freqs.into_iter().zip(phases.into_iter()).map(|(f, p)| PlotPoint::new((f as f32).log10(), p)).collect(); // Convert to f32 first due to rouding errors
|
self.phases = freqs.into_iter().zip(phases.into_iter()).map(|(f, p)| PlotPoint::new((f as f32).log10(), p)).collect(); // Convert to f32 first due to rouding errors
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plot_magnitudes(&self) -> PlotPoints {
|
pub fn plot_magnitudes(&self) -> PlotPoints<'_> {
|
||||||
PlotPoints::Owned(self.magnitudes.clone())
|
PlotPoints::Owned(self.magnitudes.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plot_phases(&self) -> PlotPoints {
|
pub fn plot_phases(&self) -> PlotPoints<'_> {
|
||||||
PlotPoints::Owned(self.phases.clone())
|
PlotPoints::Owned(self.phases.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user