mirror of https://codeberg.org/topola/topola.git
feat(stepper): Replace overengineered SMA rate timeout with two accumulators
This commit is contained in:
parent
9345d5de8a
commit
18e8f9812c
|
|
@ -6,7 +6,7 @@ use std::ops::ControlFlow;
|
|||
|
||||
use topola::{
|
||||
interactor::activity::ActivityStepperWithStatus,
|
||||
stepper::{EstimateProgress, GetMaybeReconfigurationTriggerProgress},
|
||||
stepper::{EstimateProgress, GetTimeoutProgress},
|
||||
};
|
||||
|
||||
use crate::{translator::Translator, viewport::Viewport};
|
||||
|
|
@ -45,17 +45,17 @@ impl StatusBar {
|
|||
if let Some(activity) = maybe_activity {
|
||||
let progress = activity.estimate_progress();
|
||||
let value = progress.value();
|
||||
let maximum = progress.reference();
|
||||
let maximum = progress.maximum();
|
||||
let ratio = *value as f32 / *maximum as f32;
|
||||
|
||||
if let Some(trigger_progress) = activity.reconfiguration_trigger_progress() {
|
||||
if let Some(trigger_progress) = activity.timeout_progress() {
|
||||
ui.add(egui::ProgressBar::new(ratio).text(format!(
|
||||
"{:.1}% ({:.1}/{:.1}) (sma: {:.1}, min: {:.1}))",
|
||||
"{:.1}% ({:.1}/{:.1}) (timeout: {:.1}/{:.1}))",
|
||||
ratio * 100.0,
|
||||
value,
|
||||
maximum,
|
||||
trigger_progress.value(),
|
||||
trigger_progress.reference(),
|
||||
trigger_progress.maximum(),
|
||||
)));
|
||||
} else {
|
||||
ui.add(egui::ProgressBar::new(ratio).text(format!(
|
||||
|
|
@ -68,7 +68,7 @@ impl StatusBar {
|
|||
|
||||
let linear_subprogress = progress.subscale();
|
||||
let value = linear_subprogress.value();
|
||||
let maximum = linear_subprogress.reference();
|
||||
let maximum = linear_subprogress.maximum();
|
||||
let ratio = *value as f32 / *maximum as f32;
|
||||
|
||||
ui.add(egui::ProgressBar::new(ratio).text(format!(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
board::{edit::BoardEdit, AccessMesadata},
|
||||
layout::via::ViaWeight,
|
||||
router::ng,
|
||||
stepper::{Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale, Step},
|
||||
stepper::{Abort, EstimateProgress, GetTimeoutProgress, LinearScale, Step},
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -199,14 +199,12 @@ impl<M> EstimateProgress for ExecutionStepper<M> {
|
|||
|
||||
// Since enum_dispatch does not really support generics, we implement this the
|
||||
// long way by using `match`.
|
||||
impl<M> GetMaybeReconfigurationTriggerProgress for ExecutionStepper<M> {
|
||||
impl<M> GetTimeoutProgress for ExecutionStepper<M> {
|
||||
type Subscale = ();
|
||||
|
||||
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
|
||||
fn timeout_progress(&self) -> Option<LinearScale<f64>> {
|
||||
match self {
|
||||
ExecutionStepper::MultilayerAutoroute(autoroute) => {
|
||||
autoroute.reconfiguration_trigger_progress()
|
||||
}
|
||||
ExecutionStepper::MultilayerAutoroute(autoroute) => autoroute.timeout_progress(),
|
||||
ExecutionStepper::PlanarAutoroute(autoroute) => None,
|
||||
ExecutionStepper::TopoAutoroute(..) => None,
|
||||
ExecutionStepper::PlaceVia(..) => None,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ use crate::{
|
|||
IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer,
|
||||
MakeNextMultilayerAutorouteConfiguration, MultilayerAutorouteReconfigurer,
|
||||
},
|
||||
planar_autoroute::PlanarAutorouteConfigurationStatus,
|
||||
planar_preconfigurer::PlanarAutoroutePreconfigurerInput,
|
||||
planar_reconfigurator::PlanarAutorouteReconfiguratorStatus,
|
||||
Autorouter, AutorouterError,
|
||||
|
|
@ -30,8 +29,8 @@ use crate::{
|
|||
geometry::primitive::PrimitiveShape,
|
||||
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
||||
stepper::{
|
||||
Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale,
|
||||
ReconfiguratorStatus, Reconfigure, SmaRateReconfigurationTrigger, Step,
|
||||
Abort, EstimateProgress, GetTimeoutProgress, LinearScale, ReconfiguratorStatus,
|
||||
Reconfigure, Step, TimeVsProgressAccumulatorTimeout,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ pub type MultilayerReconfiguratorStatus =
|
|||
|
||||
pub struct MultilayerAutorouteReconfigurator {
|
||||
stepper: MultilayerAutorouteExecutionStepper,
|
||||
reconfiguration_trigger: SmaRateReconfigurationTrigger,
|
||||
timeout: TimeVsProgressAccumulatorTimeout,
|
||||
reconfigurer: MultilayerAutorouteReconfigurer,
|
||||
options: MultilayerAutorouteOptions,
|
||||
}
|
||||
|
|
@ -74,7 +73,7 @@ impl MultilayerAutorouteReconfigurator {
|
|||
preconfiguration,
|
||||
options,
|
||||
)?,
|
||||
reconfiguration_trigger: SmaRateReconfigurationTrigger::new(20, 0.5, 0.1),
|
||||
timeout: TimeVsProgressAccumulatorTimeout::new(10.0, 5.0),
|
||||
reconfigurer,
|
||||
options,
|
||||
})
|
||||
|
|
@ -86,7 +85,7 @@ impl MultilayerAutorouteReconfigurator {
|
|||
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
|
||||
{
|
||||
// Reset the reconfiguration trigger.
|
||||
self.reconfiguration_trigger = SmaRateReconfigurationTrigger::new(20, 1.0, 0.1);
|
||||
self.timeout = TimeVsProgressAccumulatorTimeout::new(10.0, 5.0);
|
||||
|
||||
loop {
|
||||
let Some(configuration) = self.reconfigurer.next_configuration(autorouter) else {
|
||||
|
|
@ -117,7 +116,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, MultilayerReconfi
|
|||
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
|
||||
{
|
||||
if !self
|
||||
.reconfiguration_trigger
|
||||
.timeout
|
||||
.update(*self.estimate_progress().value() as f64)
|
||||
{
|
||||
return self.reconfigure(autorouter);
|
||||
|
|
@ -161,13 +160,13 @@ impl EstimateProgress for MultilayerAutorouteReconfigurator {
|
|||
}
|
||||
}
|
||||
|
||||
impl GetMaybeReconfigurationTriggerProgress for MultilayerAutorouteReconfigurator {
|
||||
impl GetTimeoutProgress for MultilayerAutorouteReconfigurator {
|
||||
type Subscale = ();
|
||||
|
||||
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
|
||||
fn timeout_progress(&self) -> Option<LinearScale<f64>> {
|
||||
Some(LinearScale::new(
|
||||
(*self.reconfiguration_trigger.maybe_sma_rate_per_sec())?,
|
||||
*self.reconfiguration_trigger.min_sma_rate_per_sec(),
|
||||
self.timeout.start_instant().elapsed().as_secs_f64(),
|
||||
*self.timeout.progress_accumulator(),
|
||||
(),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ impl EstimateProgress for PlanarAutorouteExecutionStepper {
|
|||
.map_or(0.0, |route| *route.estimate_progress().value()),
|
||||
self.route
|
||||
.as_ref()
|
||||
.map_or(0.0, |route| *route.estimate_progress().reference()),
|
||||
.map_or(0.0, |route| *route.estimate_progress().maximum()),
|
||||
(),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ use crate::{
|
|||
geometry::primitive::PrimitiveShape,
|
||||
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
||||
stepper::{
|
||||
Abort, EstimateProgress, LinearScale, ReconfiguratorStatus, Reconfigure,
|
||||
SmaRateReconfigurationTrigger, Step,
|
||||
Abort, EstimateProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
|
||||
TimeVsProgressAccumulatorTimeout,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ pub type PlanarAutorouteReconfiguratorStatus =
|
|||
|
||||
pub struct PlanarAutorouteReconfigurator {
|
||||
stepper: PlanarAutorouteExecutionStepper,
|
||||
reconfiguration_trigger: SmaRateReconfigurationTrigger,
|
||||
timeout: TimeVsProgressAccumulatorTimeout,
|
||||
reconfigurer: PlanarAutorouteReconfigurer,
|
||||
options: PlanarAutorouteOptions,
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ impl PlanarAutorouteReconfigurator {
|
|||
|
||||
Ok(Self {
|
||||
stepper: PlanarAutorouteExecutionStepper::new(autorouter, preconfiguration, options)?,
|
||||
reconfiguration_trigger: SmaRateReconfigurationTrigger::new(5, 0.5, 0.1),
|
||||
timeout: TimeVsProgressAccumulatorTimeout::new(3.0, 1.0),
|
||||
// Note: I assume here that the first permutation is the same as the original order.
|
||||
reconfigurer,
|
||||
options,
|
||||
|
|
@ -77,7 +77,7 @@ impl PlanarAutorouteReconfigurator {
|
|||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||
) -> Result<ControlFlow<Option<BoardEdit>, PlanarAutorouteReconfiguratorStatus>, AutorouterError>
|
||||
{
|
||||
self.reconfiguration_trigger = SmaRateReconfigurationTrigger::new(5, 0.5, 0.1);
|
||||
self.timeout = TimeVsProgressAccumulatorTimeout::new(3.0, 1.0);
|
||||
|
||||
loop {
|
||||
let Some(configuration) = self
|
||||
|
|
@ -111,7 +111,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteRe
|
|||
) -> Result<ControlFlow<Option<BoardEdit>, PlanarAutorouteReconfiguratorStatus>, AutorouterError>
|
||||
{
|
||||
if !self
|
||||
.reconfiguration_trigger
|
||||
.timeout
|
||||
.update(*self.estimate_progress().value() as f64)
|
||||
{
|
||||
return self.reconfigure(autorouter);
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@ use crate::{
|
|||
ng,
|
||||
thetastar::ThetastarStepper,
|
||||
},
|
||||
stepper::{
|
||||
Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale, OnEvent, Step,
|
||||
},
|
||||
stepper::{Abort, EstimateProgress, GetTimeoutProgress, LinearScale, OnEvent, Step},
|
||||
};
|
||||
|
||||
/// Stores the interactive input data from the user.
|
||||
|
|
@ -118,13 +116,13 @@ impl<M> EstimateProgress for ActivityStepper<M> {
|
|||
|
||||
// Since enum_dispatch does not really support generics, we implement this the
|
||||
// long way by using `match`.
|
||||
impl<M> GetMaybeReconfigurationTriggerProgress for ActivityStepper<M> {
|
||||
impl<M> GetTimeoutProgress for ActivityStepper<M> {
|
||||
type Subscale = ();
|
||||
|
||||
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
|
||||
fn timeout_progress(&self) -> Option<LinearScale<f64>> {
|
||||
match self {
|
||||
ActivityStepper::Interaction(..) => None,
|
||||
ActivityStepper::Execution(execution) => execution.reconfiguration_trigger_progress(),
|
||||
ActivityStepper::Execution(execution) => execution.timeout_progress(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -219,11 +217,11 @@ impl<M> EstimateProgress for ActivityStepperWithStatus<M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<M> GetMaybeReconfigurationTriggerProgress for ActivityStepperWithStatus<M> {
|
||||
impl<M> GetTimeoutProgress for ActivityStepperWithStatus<M> {
|
||||
type Subscale = ();
|
||||
|
||||
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
|
||||
self.activity.reconfiguration_trigger_progress()
|
||||
fn timeout_progress(&self) -> Option<LinearScale<f64>> {
|
||||
self.activity.timeout_progress()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
use std::{collections::VecDeque, time::Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
use derive_getters::Getters;
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ pub trait OnEvent<Ctx, Event> {
|
|||
#[derive(Clone, Copy, Debug, Getters)]
|
||||
pub struct LinearScale<V, S = ()> {
|
||||
value: V,
|
||||
reference: V,
|
||||
maximum: V,
|
||||
subscale: S,
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ impl<V, S> LinearScale<V, S> {
|
|||
pub fn new(value: V, reference: V, subscale: S) -> Self {
|
||||
Self {
|
||||
value,
|
||||
reference,
|
||||
maximum: reference,
|
||||
subscale,
|
||||
}
|
||||
}
|
||||
|
|
@ -109,87 +109,38 @@ pub trait EstimateProgress {
|
|||
fn estimate_progress(&self) -> LinearScale<Self::Value, Self::Subscale>;
|
||||
}
|
||||
|
||||
pub trait GetMaybeReconfigurationTriggerProgress {
|
||||
pub trait GetTimeoutProgress {
|
||||
type Subscale;
|
||||
|
||||
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64, Self::Subscale>>;
|
||||
fn timeout_progress(&self) -> Option<LinearScale<f64, Self::Subscale>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Getters)]
|
||||
pub struct SmaRateReconfigurationTrigger {
|
||||
#[getter(skip)]
|
||||
sample_buffer: VecDeque<f64>,
|
||||
#[getter(skip)]
|
||||
last_instant: Instant,
|
||||
pub struct TimeVsProgressAccumulatorTimeout {
|
||||
start_instant: Instant,
|
||||
#[getter(skip)]
|
||||
last_max_value: f64,
|
||||
progress_accumulator: f64,
|
||||
#[getter(skip)]
|
||||
incoming_max_value: f64,
|
||||
maybe_sma_rate_per_sec: Option<f64>,
|
||||
#[getter(skip)]
|
||||
sample_buffer_size: usize,
|
||||
#[getter(skip)]
|
||||
sampling_interval_secs: f64,
|
||||
min_sma_rate_per_sec: f64,
|
||||
progress_bonus_s: f64,
|
||||
}
|
||||
|
||||
impl SmaRateReconfigurationTrigger {
|
||||
pub fn new(
|
||||
sample_buffer_size: usize,
|
||||
sampling_interval_secs: f64,
|
||||
min_sma_rate_per_sec: f64,
|
||||
) -> Self {
|
||||
impl TimeVsProgressAccumulatorTimeout {
|
||||
pub fn new(initial_timeout_s: f64, progress_bonus_s: f64) -> Self {
|
||||
Self {
|
||||
sample_buffer: VecDeque::new(),
|
||||
last_instant: Instant::now(),
|
||||
start_instant: Instant::now(),
|
||||
last_max_value: 0.0,
|
||||
incoming_max_value: 0.0,
|
||||
maybe_sma_rate_per_sec: None,
|
||||
sample_buffer_size,
|
||||
sampling_interval_secs,
|
||||
min_sma_rate_per_sec,
|
||||
progress_accumulator: initial_timeout_s,
|
||||
progress_bonus_s,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, value: f64) -> bool {
|
||||
self.incoming_max_value = self.incoming_max_value.max(value);
|
||||
|
||||
let elapsed = self.last_instant.elapsed();
|
||||
|
||||
if elapsed.as_secs_f64() >= self.sampling_interval_secs {
|
||||
let delta = self.incoming_max_value - self.last_max_value;
|
||||
let count = (elapsed.as_secs_f64() / self.sampling_interval_secs) as usize;
|
||||
let mut total_pushed = 0.0;
|
||||
let mut total_popped = 0.0;
|
||||
|
||||
for _ in 0..count {
|
||||
let pushed = (delta / count as f64) / self.sampling_interval_secs;
|
||||
self.sample_buffer.push_back(delta / count as f64);
|
||||
total_pushed += pushed;
|
||||
|
||||
if self.sample_buffer.len() > self.sample_buffer_size {
|
||||
total_popped += self.sample_buffer.pop_front().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(sma_rate_per_sec) = self.maybe_sma_rate_per_sec {
|
||||
self.maybe_sma_rate_per_sec = Some(
|
||||
sma_rate_per_sec
|
||||
+ (total_pushed - total_popped) / self.sample_buffer_size as f64,
|
||||
)
|
||||
} else if self.sample_buffer.len() >= self.sample_buffer_size {
|
||||
self.maybe_sma_rate_per_sec =
|
||||
Some(self.sample_buffer.iter().sum::<f64>() / self.sample_buffer_size as f64)
|
||||
}
|
||||
|
||||
self.last_instant = Instant::now();
|
||||
self.last_max_value = self.incoming_max_value;
|
||||
if value > self.last_max_value {
|
||||
self.progress_accumulator += (value - self.last_max_value) * self.progress_bonus_s;
|
||||
self.last_max_value = value;
|
||||
}
|
||||
|
||||
if let Some(sma_rate_per_sec) = self.maybe_sma_rate_per_sec {
|
||||
sma_rate_per_sec >= self.min_sma_rate_per_sec
|
||||
} else {
|
||||
true
|
||||
}
|
||||
self.start_instant.elapsed().as_secs_f64() < self.progress_accumulator
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue