pub trait DispatchStrategy: Send + Sync {
// Required method
fn rank(&mut self, ctx: &RankContext<'_>) -> Option<f64>;
// Provided methods
fn pre_dispatch(
&mut self,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &mut World,
) { ... }
fn prepare_car(
&mut self,
_car: EntityId,
_car_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
) { ... }
fn fallback(
&mut self,
_car: EntityId,
_car_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
) -> DispatchDecision { ... }
fn notify_removed(&mut self, _elevator: EntityId) { ... }
fn builtin_id(&self) -> Option<BuiltinStrategy> { ... }
fn snapshot_config(&self) -> Option<String> { ... }
fn restore_config(&mut self, _serialized: &str) -> Result<(), String> { ... }
}Expand description
Pluggable dispatch algorithm.
Strategies implement rank to score each (car, stop)
pair; the dispatch system then performs an optimal assignment across
the whole group, guaranteeing that no two cars are sent to the same
hall call.
Returning None from rank excludes a pair from assignment — useful
for capacity limits, direction preferences, restricted stops, or
sticky commitments.
Cars that receive no stop fall through to fallback,
which returns the policy for that car (idle, park, etc.).
Required Methods§
Sourcefn rank(&mut self, ctx: &RankContext<'_>) -> Option<f64>
fn rank(&mut self, ctx: &RankContext<'_>) -> Option<f64>
Score the cost of sending car to stop. Lower is better.
Returning None marks this (car, stop) pair as unavailable;
the assignment algorithm will never pair them. Use this for
capacity limits, wrong-direction stops, stops outside the line’s
topology, or pairs already committed via a sticky assignment.
Must return a finite, non-negative value if Some — infinities
and NaN can destabilize the underlying Hungarian solver.
Implementations must not mutate per-car state inside rank: the
dispatch system calls rank(car, stop_0..stop_m) in a loop, so
mutating self on one call affects subsequent calls for the same
car within the same pass and produces an asymmetric cost matrix
whose results depend on iteration order. Use
prepare_car to compute and store any
per-car state before rank is called.
Provided Methods§
Sourcefn pre_dispatch(
&mut self,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &mut World,
)
fn pre_dispatch( &mut self, _group: &ElevatorGroup, _manifest: &DispatchManifest, _world: &mut World, )
Optional hook called once per group before the assignment pass.
Strategies that need to mutate World extension storage (e.g.
DestinationDispatch writing sticky rider → car assignments)
or pre-populate crate::components::DestinationQueue entries
override this. Default: no-op.
Sourcefn prepare_car(
&mut self,
_car: EntityId,
_car_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
)
fn prepare_car( &mut self, _car: EntityId, _car_position: f64, _group: &ElevatorGroup, _manifest: &DispatchManifest, _world: &World, )
Optional hook called once per candidate car, before any
rank calls for that car in the current pass.
Strategies whose ranking depends on stable per-car state (e.g. the
sweep direction used by SCAN/LOOK) set that state here so later
rank calls see a consistent view regardless of iteration order.
The default is a no-op.
Sourcefn fallback(
&mut self,
_car: EntityId,
_car_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
) -> DispatchDecision
fn fallback( &mut self, _car: EntityId, _car_position: f64, _group: &ElevatorGroup, _manifest: &DispatchManifest, _world: &World, ) -> DispatchDecision
Decide what an idle car should do when no stop was assigned to it.
Called for each car the assignment phase could not pair with a
stop (because there were no stops, or all candidate stops had
rank None for this car). Default: DispatchDecision::Idle.
Sourcefn notify_removed(&mut self, _elevator: EntityId)
fn notify_removed(&mut self, _elevator: EntityId)
Notify the strategy that an elevator has been removed.
Implementations with per-elevator state (e.g. direction tracking) should clean up here to prevent unbounded memory growth.
Sourcefn builtin_id(&self) -> Option<BuiltinStrategy>
fn builtin_id(&self) -> Option<BuiltinStrategy>
If this strategy is a known built-in variant, return it so
Simulation::new can stamp the
correct BuiltinStrategy into the group’s snapshot identity.
Without this, legacy-topology sims constructed via
Simulation::new(config, SomeNonScanStrategy::new()) silently
recorded BuiltinStrategy::Scan as their identity — so a
snapshot round-trip replaced the running strategy with Scan
and produced different dispatch decisions post-restore
(determinism regression).
Default: None (unidentified — the constructor falls back to
recording BuiltinStrategy::Scan, matching pre-fix behaviour
for callers that never cared about round-trip identity). Custom
strategies that DO care should override this to return
BuiltinStrategy::Custom with a stable name.
Sourcefn snapshot_config(&self) -> Option<String>
fn snapshot_config(&self) -> Option<String>
Serialize this strategy’s tunable configuration to a string
that restore_config can apply to a
freshly-instantiated instance.
Returning Some(..) makes the configuration survive snapshot
round-trip: without it, crate::snapshot::WorldSnapshot::restore
instantiates each built-in via BuiltinStrategy::instantiate,
which calls ::new() with default weights — silently dropping
any tuning applied via with_* builder methods (e.g.
EtdDispatch::with_delay_weight(2.5) degrades to the default
1.0 on the restored sim).
Default: None (no configuration to save). Built-ins with
tunable weights override to return a RON-serialized copy of
themselves; strategies with transient per-pass scratch should
use #[serde(skip)] on those fields so the snapshot stays
compact and deterministic.
Sourcefn restore_config(&mut self, _serialized: &str) -> Result<(), String>
fn restore_config(&mut self, _serialized: &str) -> Result<(), String>
Restore tunable configuration from a string previously produced
by snapshot_config on the same
strategy variant. Called by
crate::snapshot::WorldSnapshot::restore immediately after
BuiltinStrategy::instantiate builds the default instance,
so the restore writes over the defaults.
§Errors
Returns the underlying parse error as a String when the
serialized form doesn’t round-trip. Default implementation
ignores the argument and returns Ok(()) — paired with the
None default of snapshot_config, this means strategies that
don’t override either method skip configuration round-trip,
matching pre-fix behaviour.