// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use std::{collections::BTreeSet, ops::ControlFlow}; use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; use crate::{ autorouter::{ multilayer_autoroute::MultilayerAutorouteOptions, multilayer_reconfigurator::MultilayerAutorouteReconfigurator, planar_reconfigurator::PlanarAutorouteReconfigurator, }, board::{edit::BoardEdit, AccessMesadata}, layout::via::ViaWeight, router::ng, stepper::{Abort, EstimateProgress, Step}, }; use super::{ compare_detours::CompareDetoursExecutionStepper, invoker::{GetDebugOverlayData, Invoker, InvokerError}, measure_length::MeasureLengthExecutionStepper, place_via::PlaceViaExecutionStepper, remove_bands::RemoveBandsExecutionStepper, selection::{BandSelection, PinSelection}, Autorouter, PlanarAutorouteOptions, }; type Type = PinSelection; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Command { Autoroute(PinSelection, PlanarAutorouteOptions), // TODO: Rename to PlanarAutoroute. MultilayerAutoroute(PinSelection, MultilayerAutorouteOptions), TopoAutoroute { selection: PinSelection, #[serde(default, skip_serializing_if = "BTreeSet::is_empty")] allowed_edges: BTreeSet, active_layer: String, routed_band_width: f64, }, PlaceVia(ViaWeight), RemoveBands(BandSelection), CompareDetours(Type, PlanarAutorouteOptions), MeasureLength(BandSelection), } #[enum_dispatch(GetDebugOverlayData)] pub enum ExecutionStepper { MultilayerAutoroute(MultilayerAutorouteReconfigurator), PlanarAutoroute(PlanarAutorouteReconfigurator), TopoAutoroute(ng::AutorouteExecutionStepper), PlaceVia(PlaceViaExecutionStepper), RemoveBands(RemoveBandsExecutionStepper), CompareDetours(CompareDetoursExecutionStepper), MeasureLength(MeasureLengthExecutionStepper), } impl ExecutionStepper { fn step_catch_err( &mut self, autorouter: &mut Autorouter, ) -> Result, String)>, InvokerError> { Ok(match self { ExecutionStepper::MultilayerAutoroute(autoroute) => match autoroute.step(autorouter)? { ControlFlow::Continue(..) => ControlFlow::Continue(()), ControlFlow::Break(edit) => { ControlFlow::Break((edit, "finished multilayer autorouting".to_string())) } }, ExecutionStepper::PlanarAutoroute(autoroute) => match autoroute.step(autorouter)? { ControlFlow::Continue(..) => ControlFlow::Continue(()), ControlFlow::Break(edit) => { ControlFlow::Break((edit, "finished planar autorouting".to_string())) } }, ExecutionStepper::TopoAutoroute(autoroute) => { let ret = match autoroute.step() { ControlFlow::Continue(()) => ControlFlow::Continue(()), ControlFlow::Break(false) => { ControlFlow::Break((None, "topo-autorouting failed".to_string())) } ControlFlow::Break(true) => { for (ep, band) in &autoroute.last_bands { let (source, target) = ep.end_points.into(); autorouter.board.try_set_band_between_nodes( &mut autoroute.last_recorder.board_data_edit, source, target, *band, ); } let topo_navmesh = autoroute.maybe_topo_navmesh().unwrap().to_owned(); let mut pretty_config = ron::ser::PrettyConfig::new(); pretty_config.depth_limit = 2; log::debug!( "topo navmesh result: {}", ron::ser::to_string_pretty( &ng::pie::navmesh::NavmeshSer::from(topo_navmesh), pretty_config ) .unwrap() ); ControlFlow::Break(( Some(autoroute.last_recorder.clone()), "finished topo-autorouting".to_string(), )) } }; // TODO: maintain topo-navmesh just like layout *autorouter.board.layout_mut() = autoroute.last_layout.clone(); ret } ExecutionStepper::PlaceVia(place_via) => { let edit = place_via.doit(autorouter)?; ControlFlow::Break((edit, "finished placing via".to_string())) } ExecutionStepper::RemoveBands(remove_bands) => { let edit = remove_bands.doit(autorouter)?; ControlFlow::Break((edit, "finished removing bands".to_string())) } ExecutionStepper::CompareDetours(compare_detours) => { match compare_detours.step(autorouter)? { ControlFlow::Continue(()) => ControlFlow::Continue(()), ControlFlow::Break((total_length1, total_length2)) => ControlFlow::Break(( None, format!( "total detour lengths are {} and {}", total_length1, total_length2 ), )), } } ExecutionStepper::MeasureLength(measure_length) => { let length = measure_length.doit(autorouter)?; ControlFlow::Break((None, format!("Total length of selected bands: {}", length))) } }) } } impl Step, String> for ExecutionStepper { type Error = InvokerError; fn step(&mut self, invoker: &mut Invoker) -> Result, InvokerError> { match self.step_catch_err(&mut invoker.autorouter) { Ok(ControlFlow::Continue(())) => Ok(ControlFlow::Continue(())), Ok(ControlFlow::Break((maybe_edit, msg))) => { if let (Some(command), Some(edit)) = (invoker.ongoing_command.take(), maybe_edit) { invoker.history.do_(command, Some(edit)); } Ok(ControlFlow::Break(msg)) } Err(err) => { invoker.ongoing_command = None; Err(err) } } } } impl Abort> for ExecutionStepper { fn abort(&mut self, invoker: &mut Invoker) { match self { ExecutionStepper::MultilayerAutoroute(autoroute) => { autoroute.abort(&mut invoker.autorouter) } ExecutionStepper::PlanarAutoroute(autoroute) => { autoroute.abort(&mut invoker.autorouter) } ExecutionStepper::TopoAutoroute(autoroute) => { autoroute.abort(&mut ()); // TODO: maintain topo-navmesh just like layout *invoker.autorouter.board.layout_mut() = autoroute.last_layout.clone(); } ExecutionStepper::PlaceVia(_place_via) => (), //place_via.abort(), ExecutionStepper::RemoveBands(_remove_bands) => (), //remove_bands.abort(), ExecutionStepper::CompareDetours(_compare_detours) => (), //compare_detours.abort(), ExecutionStepper::MeasureLength(_measure_length) => (), //measure_length.abort(), } } } // Since enum_dispatch does not really support generics, we implement this the // long way. impl EstimateProgress for ExecutionStepper { type Value = f64; fn estimate_progress_value(&self) -> f64 { match self { ExecutionStepper::MultilayerAutoroute(autoroute) => autoroute.estimate_progress_value(), ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_progress_value(), ExecutionStepper::TopoAutoroute(toporoute) => toporoute.estimate_progress_value(), ExecutionStepper::PlaceVia(place_via) => place_via.estimate_progress_value(), ExecutionStepper::RemoveBands(remove_bands) => remove_bands.estimate_progress_value(), ExecutionStepper::CompareDetours(compare_detours) => { compare_detours.estimate_progress_value() } ExecutionStepper::MeasureLength(measure_length) => { measure_length.estimate_progress_value() } } } fn estimate_progress_maximum(&self) -> f64 { match self { ExecutionStepper::MultilayerAutoroute(autoroute) => { autoroute.estimate_progress_maximum() } ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_progress_maximum(), ExecutionStepper::TopoAutoroute(toporoute) => toporoute.estimate_progress_maximum(), ExecutionStepper::PlaceVia(place_via) => place_via.estimate_progress_maximum(), ExecutionStepper::RemoveBands(remove_bands) => remove_bands.estimate_progress_maximum(), ExecutionStepper::CompareDetours(compare_detours) => { compare_detours.estimate_progress_maximum() } ExecutionStepper::MeasureLength(measure_length) => { measure_length.estimate_progress_maximum() } } } }