mirror of https://codeberg.org/topola/topola.git
autorouter: improve error handling by removing some unwraps
This commit is contained in:
parent
f4054783d1
commit
c36ccc287a
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue