topola/src/autorouter/execution.rs

229 lines
9.6 KiB
Rust

// 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<ng::PieEdgeIndex>,
active_layer: String,
routed_band_width: f64,
},
PlaceVia(ViaWeight),
RemoveBands(BandSelection),
CompareDetours(Type, PlanarAutorouteOptions),
MeasureLength(BandSelection),
}
#[enum_dispatch(GetDebugOverlayData)]
pub enum ExecutionStepper<M> {
MultilayerAutoroute(MultilayerAutorouteReconfigurator),
PlanarAutoroute(PlanarAutorouteReconfigurator),
TopoAutoroute(ng::AutorouteExecutionStepper<M>),
PlaceVia(PlaceViaExecutionStepper),
RemoveBands(RemoveBandsExecutionStepper),
CompareDetours(CompareDetoursExecutionStepper),
MeasureLength(MeasureLengthExecutionStepper),
}
impl<M: AccessMesadata + Clone> ExecutionStepper<M> {
fn step_catch_err(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<(Option<BoardEdit>, 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<M: AccessMesadata + Clone> Step<Invoker<M>, String> for ExecutionStepper<M> {
type Error = InvokerError;
fn step(&mut self, invoker: &mut Invoker<M>) -> Result<ControlFlow<String>, 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<M: AccessMesadata + Clone> Abort<Invoker<M>> for ExecutionStepper<M> {
fn abort(&mut self, invoker: &mut Invoker<M>) {
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<M> EstimateProgress for ExecutionStepper<M> {
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()
}
}
}
}