autorouter: improve error handling by removing some unwraps

This commit is contained in:
Mikolaj Wielgus 2024-07-08 13:25:58 +02:00
parent f4054783d1
commit c36ccc287a
3 changed files with 124 additions and 115 deletions

View File

@ -15,7 +15,7 @@ use super::{
pub struct Autoroute { pub struct Autoroute {
ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>, ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>,
route: Option<Route>, route: Option<Route>,
cur_ratline: Option<EdgeIndex<usize>>, curr_ratline: Option<EdgeIndex<usize>>,
} }
impl Autoroute { impl Autoroute {
@ -25,16 +25,16 @@ impl Autoroute {
) -> Result<Self, AutorouterError> { ) -> Result<Self, AutorouterError> {
let mut ratlines_iter = Box::new(ratlines.into_iter()); let mut ratlines_iter = Box::new(ratlines.into_iter());
let Some(cur_ratline) = ratlines_iter.next() else { let Some(curr_ratline) = ratlines_iter.next() else {
return Err(AutorouterError::NothingToRoute); return Err(AutorouterError::NothingToRoute);
}; };
let (source, target) = autorouter.ratline_endpoints(cur_ratline); let (source, target) = autorouter.ratline_endpoints(curr_ratline);
let mut router = Router::new(autorouter.board.layout_mut()); let mut router = Router::new(autorouter.board.layout_mut());
let this = Self { let this = Self {
ratlines_iter, ratlines_iter,
cur_ratline: Some(cur_ratline), curr_ratline: Some(curr_ratline),
route: Some(router.route_walk(source, target, 100.0)?), route: Some(router.route_walk(source, target, 100.0)?),
}; };
@ -50,11 +50,11 @@ impl Autoroute {
return Ok(AutorouterStatus::Finished); return Ok(AutorouterStatus::Finished);
}; };
let Some(cur_ratline) = self.cur_ratline else { let Some(curr_ratline) = self.curr_ratline else {
return Ok(AutorouterStatus::Finished); return Ok(AutorouterStatus::Finished);
}; };
let (source, target) = autorouter.ratline_endpoints(cur_ratline); let (source, target) = autorouter.ratline_endpoints(curr_ratline);
let band = { let band = {
let mut router = Router::new(autorouter.board.layout_mut()); let mut router = Router::new(autorouter.board.layout_mut());
@ -67,21 +67,21 @@ impl Autoroute {
autorouter autorouter
.ratsnest .ratsnest
.assign_band_to_ratline(self.cur_ratline.unwrap(), band); .assign_band_to_ratline(self.curr_ratline.unwrap(), band);
autorouter autorouter
.board .board
.try_set_band_between_nodes(source, target, band); .try_set_band_between_nodes(source, target, band);
let Some(new_ratline) = self.ratlines_iter.next() else { let Some(new_ratline) = self.ratlines_iter.next() else {
self.cur_ratline = None; self.curr_ratline = None;
return Ok(AutorouterStatus::Finished); return Ok(AutorouterStatus::Finished);
}; };
let (source, target) = autorouter.ratline_endpoints(new_ratline); let (source, target) = autorouter.ratline_endpoints(new_ratline);
let mut router = Router::new(autorouter.board.layout_mut()); let mut router = Router::new(autorouter.board.layout_mut());
self.cur_ratline = Some(new_ratline); self.curr_ratline = Some(new_ratline);
self.route = Some(router.route_walk(source, target, 100.0)?); self.route = Some(router.route_walk(source, target, 100.0)?);
Ok(AutorouterStatus::Running) Ok(AutorouterStatus::Running)

View File

@ -176,9 +176,9 @@ impl<M: AccessMesadata> Invoker<M> {
(self.autorouter, self.history, self.ongoing_command) (self.autorouter, self.history, self.ongoing_command)
} }
#[debug_requires(self.ongoing_command.is_none())] //#[debug_requires(self.ongoing_command.is_none())]
pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> { pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> {
let mut execute = self.execute_walk(command); let mut execute = self.execute_walk(command)?;
loop { loop {
let status = match execute.step(self) { let status = match execute.step(self) {
@ -194,21 +194,21 @@ impl<M: AccessMesadata> Invoker<M> {
} }
#[debug_requires(self.ongoing_command.is_none())] #[debug_requires(self.ongoing_command.is_none())]
pub fn execute_walk(&mut self, command: Command) -> Execute { pub fn execute_walk(&mut self, command: Command) -> Result<Execute, InvokerError> {
let execute = self.dispatch_command(&command); let execute = self.dispatch_command(&command);
self.ongoing_command = Some(command); self.ongoing_command = Some(command);
execute execute
} }
#[debug_requires(self.ongoing_command.is_none())] #[debug_requires(self.ongoing_command.is_none())]
fn dispatch_command(&mut self, command: &Command) -> Execute { fn dispatch_command(&mut self, command: &Command) -> Result<Execute, InvokerError> {
match command { match command {
Command::Autoroute(selection) => { Command::Autoroute(selection) => Ok::<Execute, InvokerError>(Execute::Autoroute(
Execute::Autoroute(self.autorouter.autoroute_walk(selection).unwrap()) self.autorouter.autoroute_walk(selection)?,
} )),
Command::PlaceVia(weight) => { Command::PlaceVia(weight) => Ok::<Execute, InvokerError>(Execute::PlaceVia(
Execute::PlaceVia(self.autorouter.place_via_walk(*weight).unwrap()) self.autorouter.place_via_walk(*weight)?,
} )),
} }
} }
@ -227,7 +227,7 @@ impl<M: AccessMesadata> Invoker<M> {
//#[debug_requires(self.ongoing.is_none())] //#[debug_requires(self.ongoing.is_none())]
pub fn redo(&mut self) -> Result<(), InvokerError> { pub fn redo(&mut self) -> Result<(), InvokerError> {
let command = self.history.last_undone()?.clone(); let command = self.history.last_undone()?.clone();
let mut execute = self.execute_walk(command); let mut execute = self.execute_walk(command)?;
loop { loop {
let status = match execute.step(self) { let status = match execute.step(self) {

View File

@ -4,7 +4,9 @@ use std::{
}; };
use topola::{ use topola::{
autorouter::invoker::{Command, Execute, ExecuteWithStatus, Invoker, InvokerStatus}, autorouter::invoker::{
Command, Execute, ExecuteWithStatus, Invoker, InvokerError, InvokerStatus,
},
specctra::mesadata::SpecctraMesadata, specctra::mesadata::SpecctraMesadata,
}; };
@ -37,117 +39,124 @@ impl Top {
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_execute: &mut Option<ExecuteWithStatus>, maybe_execute: &mut Option<ExecuteWithStatus>,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
) { ) -> Result<(), InvokerError> {
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { Ok::<(), InvokerError>(
egui::menu::bar(ui, |ui| { egui::TopBottomPanel::top("top_panel")
ui.menu_button("File", |ui| { .show(ctx, |ui| {
if ui.button("Open").clicked() { egui::menu::bar(ui, |ui| {
// `Context` is cheap to clone as it's wrapped in an `Arc`. ui.menu_button("File", |ui| {
let ctx = ui.ctx().clone(); if ui.button("Open").clicked() {
// NOTE: On Linux, this requires Zenity to be installed on your system. // `Context` is cheap to clone as it's wrapped in an `Arc`.
let task = rfd::AsyncFileDialog::new().pick_file(); let ctx = ui.ctx().clone();
// NOTE: On Linux, this requires Zenity to be installed on your system.
let task = rfd::AsyncFileDialog::new().pick_file();
execute(async move { execute(async move {
if let Some(file_handle) = task.await { if let Some(file_handle) = task.await {
let file_sender = FileSender::new(content_sender); let file_sender = FileSender::new(content_sender);
file_sender.send(file_handle).await; file_sender.send(file_handle).await;
ctx.request_repaint(); ctx.request_repaint();
}
});
} }
});
}
ui.separator(); ui.separator();
if ui.button("Load history").clicked() { if ui.button("Load history").clicked() {
let ctx = ui.ctx().clone(); let ctx = ui.ctx().clone();
let task = rfd::AsyncFileDialog::new().pick_file(); let task = rfd::AsyncFileDialog::new().pick_file();
execute(async move { execute(async move {
if let Some(file_handle) = task.await { if let Some(file_handle) = task.await {
let file_sender = FileSender::new(history_sender); let file_sender = FileSender::new(history_sender);
file_sender.send(file_handle).await; file_sender.send(file_handle).await;
ctx.request_repaint(); ctx.request_repaint();
} }
}); });
} else if ui.button("Save history").clicked() { } else if ui.button("Save history").clicked() {
if let Some(invoker) = if let Some(invoker) =
arc_mutex_maybe_invoker.clone().lock().unwrap().as_ref() arc_mutex_maybe_invoker.clone().lock().unwrap().as_ref()
{ {
let ctx = ui.ctx().clone(); let ctx = ui.ctx().clone();
let task = rfd::AsyncFileDialog::new().save_file(); let task = rfd::AsyncFileDialog::new().save_file();
// FIXME: I don't think we should be buffering everything in a `Vec<u8>`. // FIXME: I don't think we should be buffering everything in a `Vec<u8>`.
let mut writebuf = vec![]; let mut writebuf = vec![];
serde_json::to_writer_pretty(&mut writebuf, invoker.history()); serde_json::to_writer_pretty(&mut writebuf, invoker.history());
execute(async move { execute(async move {
if let Some(file_handle) = task.await { if let Some(file_handle) = task.await {
dbg!(file_handle.write(&writebuf).await); dbg!(file_handle.write(&writebuf).await);
ctx.request_repaint(); ctx.request_repaint();
}
});
} }
}); }
ui.separator();
// "Quit" button wouldn't work on a Web page.
if !cfg!(target_arch = "wasm32") {
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
}
});
ui.separator();
if ui.button("Autoroute").clicked() {
if maybe_execute.as_mut().map_or(true, |execute| {
matches!(execute.maybe_status(), Some(InvokerStatus::Finished))
}) {
if let (Some(invoker), Some(ref mut overlay)) = (
arc_mutex_maybe_invoker.lock().unwrap().as_mut(),
maybe_overlay,
) {
let selection = overlay.selection().clone();
overlay.clear_selection();
maybe_execute.insert(ExecuteWithStatus::new(
invoker.execute_walk(Command::Autoroute(selection))?,
));
}
}
} }
}
ui.separator(); ui.toggle_value(&mut self.is_placing_via, "Place Via");
// "Quit" button wouldn't work on a Web page. ui.separator();
if !cfg!(target_arch = "wasm32") {
if ui.button("Quit").clicked() { if ui.button("Undo").clicked()
ctx.send_viewport_cmd(egui::ViewportCommand::Close); || ctx.input_mut(|i| i.consume_key(egui::Modifiers::CTRL, egui::Key::Z))
{
if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut()
{
invoker.undo();
}
} }
}
});
ui.separator(); if ui.button("Redo").clicked()
|| ctx.input_mut(|i| i.consume_key(egui::Modifiers::CTRL, egui::Key::Y))
if ui.button("Autoroute").clicked() { {
if maybe_execute.as_mut().map_or(true, |execute| { if let Some(ref mut invoker) =
matches!(execute.maybe_status(), Some(InvokerStatus::Finished)) arc_mutex_maybe_invoker.lock().unwrap().as_mut()
}) { {
if let (Some(invoker), Some(ref mut overlay)) = ( invoker.redo();
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), }
maybe_overlay,
) {
let selection = overlay.selection().clone();
overlay.clear_selection();
maybe_execute.insert(ExecuteWithStatus::new(
invoker.execute_walk(Command::Autoroute(selection)),
));
} }
}
}
ui.toggle_value(&mut self.is_placing_via, "Place Via"); ui.separator();
ui.separator(); ui.toggle_value(&mut self.show_ratsnest, "Show Ratsnest");
ui.toggle_value(&mut self.show_navmesh, "Show Navmesh");
if ui.button("Undo").clicked() ui.separator();
|| ctx.input_mut(|i| i.consume_key(egui::Modifiers::CTRL, egui::Key::Z))
{
if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() {
invoker.undo();
}
}
if ui.button("Redo").clicked() egui::widgets::global_dark_light_mode_buttons(ui);
|| ctx.input_mut(|i| i.consume_key(egui::Modifiers::CTRL, egui::Key::Y)) Ok::<(), InvokerError>(())
{ });
if let Some(ref mut invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() })
{ .inner,
invoker.redo(); )
}
}
ui.separator();
ui.toggle_value(&mut self.show_ratsnest, "Show Ratsnest");
ui.toggle_value(&mut self.show_navmesh, "Show Navmesh");
ui.separator();
egui::widgets::global_dark_light_mode_buttons(ui);
});
});
} }
} }