mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
generalizing the old gridlock checker. removing unused savestate_every
support. reimpl it later using the callbacks if needed.
This commit is contained in:
parent
2445bddeb9
commit
287d56efd9
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2852,6 +2852,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"abstutil 0.1.0",
|
||||
"derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"geom 0.1.0",
|
||||
"instant 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -7,10 +7,10 @@ use crate::render::{AgentCache, AgentColorScheme, DrawMap, DrawOptions, Renderab
|
||||
use crate::sandbox::{GameplayMode, TutorialState};
|
||||
use abstutil::{MeasureMemory, Timer};
|
||||
use ezgui::{EventCtx, GfxCtx, Prerender};
|
||||
use geom::{Bounds, Circle, Distance, Pt2D};
|
||||
use map_model::{Map, Traversable};
|
||||
use geom::{Bounds, Circle, Distance, Duration, Pt2D, Time};
|
||||
use map_model::{IntersectionID, Map, Traversable};
|
||||
use rand::seq::SliceRandom;
|
||||
use sim::{Analytics, GetDrawAgents, Sim, SimFlags};
|
||||
use sim::{Analytics, GetDrawAgents, Sim, SimCallback, SimFlags};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct App {
|
||||
@ -508,6 +508,7 @@ pub struct PerMap {
|
||||
pub current_selection: Option<ID>,
|
||||
pub current_flags: Flags,
|
||||
pub last_warped_from: Option<(Pt2D, f64)>,
|
||||
pub sim_cb: Option<Box<dyn SimCallback>>,
|
||||
}
|
||||
|
||||
impl PerMap {
|
||||
@ -528,6 +529,7 @@ impl PerMap {
|
||||
current_selection: None,
|
||||
current_flags: flags.clone(),
|
||||
last_warped_from: None,
|
||||
sim_cb: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,3 +581,21 @@ impl PerObjectActions {
|
||||
ctx.normal_left_click()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FindDelayedIntersections {
|
||||
pub halt_limit: Duration,
|
||||
pub report_limit: Duration,
|
||||
|
||||
pub currently_delayed: Vec<(IntersectionID, Time)>,
|
||||
}
|
||||
|
||||
impl SimCallback for FindDelayedIntersections {
|
||||
fn run(&mut self, sim: &Sim, _: &Map) -> bool {
|
||||
self.currently_delayed = sim.delayed_intersections(self.report_limit);
|
||||
if let Some((_, t)) = self.currently_delayed.get(0) {
|
||||
sim.time() - *t >= self.halt_limit
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,9 +362,14 @@ fn prebake(map: &Map, scenario: Scenario, time_limit: Option<Duration>, timer: &
|
||||
let mut rng = SimFlags::for_test("prebaked").make_rng();
|
||||
scenario.instantiate(&mut sim, &map, &mut rng, timer);
|
||||
if let Some(dt) = time_limit {
|
||||
sim.timed_step(&map, dt, timer);
|
||||
sim.timed_step(&map, dt, &mut None, timer);
|
||||
} else {
|
||||
sim.timed_step(&map, sim.get_end_of_day() - Time::START_OF_DAY, timer);
|
||||
sim.timed_step(
|
||||
&map,
|
||||
sim.get_end_of_day() - Time::START_OF_DAY,
|
||||
&mut None,
|
||||
timer,
|
||||
);
|
||||
}
|
||||
|
||||
abstutil::write_binary(
|
||||
|
@ -479,7 +479,9 @@ impl ContextualActions for Actions {
|
||||
}
|
||||
(ID::Car(c), "forcibly kill this car") => {
|
||||
app.primary.sim.kill_stuck_car(c, &app.primary.map);
|
||||
app.primary.sim.tiny_step(&app.primary.map);
|
||||
app.primary
|
||||
.sim
|
||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||
app.primary.current_selection = None;
|
||||
Transition::Keep
|
||||
}
|
||||
|
@ -621,9 +621,12 @@ fn make_previewer(i: IntersectionID, phase: usize) -> Box<dyn State> {
|
||||
for idx in 0..phase {
|
||||
step += signal.phases[idx].duration;
|
||||
}
|
||||
app.primary
|
||||
.sim
|
||||
.timed_step(&app.primary.map, step, &mut Timer::throwaway());
|
||||
app.primary.sim.timed_step(
|
||||
&app.primary.map,
|
||||
step,
|
||||
&mut app.primary.sim_cb,
|
||||
&mut Timer::throwaway(),
|
||||
);
|
||||
|
||||
spawn_agents_around(i, app);
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ impl State for AgentSpawner {
|
||||
&mut rng,
|
||||
&mut Timer::new("spawn trip"),
|
||||
);
|
||||
app.primary.sim.tiny_step(map);
|
||||
app.primary.sim.tiny_step(map, &mut app.primary.sim_cb);
|
||||
app.recalculate_current_selection(ctx);
|
||||
return Transition::Pop;
|
||||
}
|
||||
@ -573,7 +573,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
|
||||
}
|
||||
|
||||
sim.flush_spawner(spawner, map, &mut timer);
|
||||
sim.tiny_step(map);
|
||||
sim.tiny_step(map, &mut app.primary.sim_cb);
|
||||
}
|
||||
|
||||
pub fn actions(_: &App, id: ID) -> Vec<(Key, String)> {
|
||||
|
@ -185,7 +185,9 @@ impl GameplayMode {
|
||||
&mut app.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
app.primary.sim.tiny_step(&app.primary.map);
|
||||
app.primary
|
||||
.sim
|
||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||
|
||||
// Maybe we've already got prebaked data for this map+scenario.
|
||||
if !app
|
||||
|
@ -752,7 +752,9 @@ impl TutorialState {
|
||||
|
||||
if let Some(ref cb) = self.stage().spawn {
|
||||
(cb)(app);
|
||||
app.primary.sim.tiny_step(&app.primary.map);
|
||||
app.primary
|
||||
.sim
|
||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||
}
|
||||
|
||||
let last_finished_task = if self.current.stage == 0 {
|
||||
@ -1088,7 +1090,7 @@ impl TutorialState {
|
||||
&mut rng,
|
||||
&mut Timer::new("spawn trip"),
|
||||
);
|
||||
app.primary.sim.tiny_step(map);
|
||||
app.primary.sim.tiny_step(map, &mut app.primary.sim_cb);
|
||||
|
||||
// And add some noise
|
||||
spawn_agents_around(app.primary.map.find_i_by_osm_id(1709145066).unwrap(), app);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::app::App;
|
||||
use crate::app::{App, FindDelayedIntersections};
|
||||
use crate::common::Warping;
|
||||
use crate::game::{msg, State, Transition};
|
||||
use crate::helpers::ID;
|
||||
@ -187,7 +187,9 @@ impl SpeedControls {
|
||||
"step forwards" => {
|
||||
let dt = self.composite.persistent_split_value("step forwards");
|
||||
if dt == Duration::seconds(0.1) {
|
||||
app.primary.sim.tiny_step(&app.primary.map);
|
||||
app.primary
|
||||
.sim
|
||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||
app.recalculate_current_selection(ctx);
|
||||
return Some(Transition::KeepWithMouseover);
|
||||
}
|
||||
@ -259,9 +261,12 @@ impl SpeedControls {
|
||||
let dt = multiplier * real_dt;
|
||||
// TODO This should match the update frequency in ezgui. Plumb along the deadline
|
||||
// or frequency to here.
|
||||
app.primary
|
||||
.sim
|
||||
.time_limited_step(&app.primary.map, dt, Duration::seconds(0.033));
|
||||
app.primary.sim.time_limited_step(
|
||||
&app.primary.map,
|
||||
dt,
|
||||
Duration::seconds(0.033),
|
||||
&mut app.primary.sim_cb,
|
||||
);
|
||||
app.recalculate_current_selection(ctx);
|
||||
}
|
||||
}
|
||||
@ -476,12 +481,20 @@ impl TimeWarpScreen {
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
target: Time,
|
||||
traffic_jams: bool,
|
||||
mut traffic_jams: bool,
|
||||
) -> Box<dyn State> {
|
||||
if traffic_jams {
|
||||
app.primary
|
||||
.sim
|
||||
.set_gridlock_checker(Some(Duration::minutes(5)));
|
||||
if app.primary.sim_cb.is_none() {
|
||||
app.primary.sim_cb = Some(Box::new(FindDelayedIntersections {
|
||||
halt_limit: Duration::minutes(5),
|
||||
report_limit: Duration::minutes(5),
|
||||
currently_delayed: Vec::new(),
|
||||
}));
|
||||
// TODO Can we get away with less frequently? Not sure about all the edge cases
|
||||
app.primary.sim.set_periodic_callback(Duration::minutes(1));
|
||||
} else {
|
||||
traffic_jams = false;
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(TimeWarpScreen {
|
||||
@ -507,24 +520,12 @@ impl State for TimeWarpScreen {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if ctx.input.nonblocking_is_update_event().is_some() {
|
||||
ctx.input.use_update_event();
|
||||
if let Some(problems) = app.primary.sim.time_limited_step(
|
||||
app.primary.sim.time_limited_step(
|
||||
&app.primary.map,
|
||||
self.target - app.primary.sim.time(),
|
||||
Duration::seconds(0.033),
|
||||
) {
|
||||
let id = ID::Intersection(problems[0].0);
|
||||
app.layer = Some(Box::new(crate::layer::traffic::Dynamic::traffic_jams(
|
||||
ctx, app,
|
||||
)));
|
||||
return Transition::Replace(Warping::new(
|
||||
ctx,
|
||||
id.canonical_point(&app.primary).unwrap(),
|
||||
Some(10.0),
|
||||
Some(id),
|
||||
&mut app.primary,
|
||||
));
|
||||
}
|
||||
|
||||
&mut app.primary.sim_cb,
|
||||
);
|
||||
for (t, maybe_i, alert) in app.primary.sim.clear_alerts() {
|
||||
// TODO Just the first :(
|
||||
return Transition::Replace(msg(
|
||||
@ -532,6 +533,22 @@ impl State for TimeWarpScreen {
|
||||
vec![format!("At {}, near {:?}, {}", t, maybe_i, alert)],
|
||||
));
|
||||
}
|
||||
if let Some(ref mut cb) = app.primary.sim_cb {
|
||||
let di = cb.downcast_mut::<FindDelayedIntersections>().unwrap();
|
||||
if let Some((i, _)) = di.currently_delayed.get(0) {
|
||||
let id = ID::Intersection(*i);
|
||||
app.layer = Some(Box::new(crate::layer::traffic::Dynamic::traffic_jams(
|
||||
ctx, app,
|
||||
)));
|
||||
return Transition::Replace(Warping::new(
|
||||
ctx,
|
||||
id.canonical_point(&app.primary).unwrap(),
|
||||
Some(10.0),
|
||||
Some(id),
|
||||
&mut app.primary,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// I'm covered in shame for not doing this from the start.
|
||||
let mut txt = Text::from(Line("Let's do the time warp again!").small_heading());
|
||||
@ -578,7 +595,7 @@ impl State for TimeWarpScreen {
|
||||
|
||||
fn on_destroy(&mut self, _: &mut EventCtx, app: &mut App) {
|
||||
if self.traffic_jams {
|
||||
app.primary.sim.set_gridlock_checker(None);
|
||||
app.primary.sim.unset_periodic_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
abstutil = { path = "../abstutil" }
|
||||
derivative = "2.1.1"
|
||||
downcast-rs = "1.1.1"
|
||||
geom = { path = "../geom" }
|
||||
instant = "0.1.2"
|
||||
libm = "0.2.1"
|
||||
|
@ -23,7 +23,7 @@ pub(crate) use self::mechanics::{
|
||||
pub(crate) use self::pandemic::PandemicModel;
|
||||
pub(crate) use self::router::{ActionAtEnd, Router};
|
||||
pub(crate) use self::scheduler::{Command, Scheduler};
|
||||
pub use self::sim::{AgentProperties, AlertHandler, Sim, SimOptions};
|
||||
pub use self::sim::{AgentProperties, AlertHandler, Sim, SimCallback, SimOptions};
|
||||
pub(crate) use self::transit::TransitSimState;
|
||||
pub use self::trips::{Person, PersonState, TripResult};
|
||||
pub use self::trips::{TripEndpoint, TripMode};
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{AlertHandler, Scenario, Sim, SimOptions};
|
||||
use abstutil::CmdArgs;
|
||||
use geom::Duration;
|
||||
use map_model::{Map, MapEdits};
|
||||
use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
@ -29,7 +28,6 @@ impl SimFlags {
|
||||
run_name: args
|
||||
.optional("--run_name")
|
||||
.unwrap_or_else(|| "unnamed".to_string()),
|
||||
savestate_every: args.optional_parse("--savestate_every", Duration::parse),
|
||||
use_freeform_policy_everywhere: args.enabled("--freeform_policy"),
|
||||
dont_block_the_box: !args.enabled("--disable_block_the_box"),
|
||||
recalc_lanechanging: !args.enabled("--disable_recalc_lc"),
|
||||
|
@ -20,7 +20,7 @@ pub enum Command {
|
||||
UpdateLaggyHead(CarID),
|
||||
UpdatePed(PedestrianID),
|
||||
UpdateIntersection(IntersectionID),
|
||||
Savestate(Duration),
|
||||
Callback(Duration),
|
||||
Pandemic(pandemic::Cmd),
|
||||
FinishRemoteTrip(TripID),
|
||||
}
|
||||
@ -42,7 +42,7 @@ impl Command {
|
||||
Command::UpdateLaggyHead(id) => CommandType::CarLaggyHead(*id),
|
||||
Command::UpdatePed(id) => CommandType::Ped(*id),
|
||||
Command::UpdateIntersection(id) => CommandType::Intersection(*id),
|
||||
Command::Savestate(_) => CommandType::Savestate,
|
||||
Command::Callback(_) => CommandType::Callback,
|
||||
Command::Pandemic(ref p) => CommandType::Pandemic(p.clone()),
|
||||
Command::FinishRemoteTrip(t) => CommandType::FinishRemoteTrip(*t),
|
||||
}
|
||||
@ -58,7 +58,7 @@ pub enum CommandType {
|
||||
CarLaggyHead(CarID),
|
||||
Ped(PedestrianID),
|
||||
Intersection(IntersectionID),
|
||||
Savestate,
|
||||
Callback,
|
||||
Pandemic(pandemic::Cmd),
|
||||
FinishRemoteTrip(TripID),
|
||||
}
|
||||
|
133
sim/src/sim.rs
133
sim/src/sim.rs
@ -57,10 +57,6 @@ pub struct Sim {
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
analytics: Analytics,
|
||||
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
check_for_gridlock: Option<(Time, Duration)>,
|
||||
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
alerts: AlertHandler,
|
||||
@ -69,7 +65,6 @@ pub struct Sim {
|
||||
#[derive(Clone)]
|
||||
pub struct SimOptions {
|
||||
pub run_name: String,
|
||||
pub savestate_every: Option<Duration>,
|
||||
pub use_freeform_policy_everywhere: bool,
|
||||
pub dont_block_the_box: bool,
|
||||
pub recalc_lanechanging: bool,
|
||||
@ -98,7 +93,6 @@ impl SimOptions {
|
||||
pub fn new(run_name: &str) -> SimOptions {
|
||||
SimOptions {
|
||||
run_name: run_name.to_string(),
|
||||
savestate_every: None,
|
||||
use_freeform_policy_everywhere: false,
|
||||
dont_block_the_box: true,
|
||||
recalc_lanechanging: true,
|
||||
@ -113,9 +107,6 @@ impl SimOptions {
|
||||
impl Sim {
|
||||
pub fn new(map: &Map, opts: SimOptions, timer: &mut Timer) -> Sim {
|
||||
let mut scheduler = Scheduler::new();
|
||||
if let Some(d) = opts.savestate_every {
|
||||
scheduler.push(Time::START_OF_DAY + d, Command::Savestate(d));
|
||||
}
|
||||
Sim {
|
||||
driving: DrivingSimState::new(map, opts.recalc_lanechanging),
|
||||
parking: ParkingSimState::new(map, timer),
|
||||
@ -143,7 +134,6 @@ impl Sim {
|
||||
run_name: opts.run_name,
|
||||
step_count: 0,
|
||||
trip_positions: None,
|
||||
check_for_gridlock: None,
|
||||
alerts: opts.alerts,
|
||||
|
||||
analytics: Analytics::new(),
|
||||
@ -385,47 +375,57 @@ impl GetDrawAgents for Sim {
|
||||
|
||||
// Running
|
||||
impl Sim {
|
||||
// Advances time as minimally as possible, also limited by max_dt.
|
||||
fn minimal_step(&mut self, map: &Map, max_dt: Duration) {
|
||||
// Advances time as minimally as possible, also limited by max_dt. Returns true if the callback
|
||||
// said to halt the sim.
|
||||
fn minimal_step(
|
||||
&mut self,
|
||||
map: &Map,
|
||||
max_dt: Duration,
|
||||
maybe_cb: &mut Option<Box<dyn SimCallback>>,
|
||||
) -> bool {
|
||||
self.step_count += 1;
|
||||
|
||||
let max_time = if let Some(t) = self.scheduler.peek_next_time() {
|
||||
if t > self.time + max_dt {
|
||||
// Next event is after when we want to stop.
|
||||
self.time += max_dt;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
t
|
||||
} else {
|
||||
// No events left at all
|
||||
self.time += max_dt;
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
|
||||
let mut savestate = false;
|
||||
let mut halt = false;
|
||||
while let Some(time) = self.scheduler.peek_next_time() {
|
||||
if time > max_time {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if let Some(cmd) = self.scheduler.get_next() {
|
||||
if self.do_step(map, time, cmd) {
|
||||
savestate = true;
|
||||
if self.do_step(map, time, cmd, maybe_cb) {
|
||||
halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.trip_positions = None;
|
||||
|
||||
if savestate {
|
||||
self.save();
|
||||
}
|
||||
halt
|
||||
}
|
||||
|
||||
// If true, savestate was requested.
|
||||
fn do_step(&mut self, map: &Map, time: Time, cmd: Command) -> bool {
|
||||
// If true, halt simulation because the callback said so.
|
||||
fn do_step(
|
||||
&mut self,
|
||||
map: &Map,
|
||||
time: Time,
|
||||
cmd: Command,
|
||||
maybe_cb: &mut Option<Box<dyn SimCallback>>,
|
||||
) -> bool {
|
||||
self.time = time;
|
||||
let mut events = Vec::new();
|
||||
let mut savestate = false;
|
||||
let mut halt = false;
|
||||
match cmd {
|
||||
Command::StartTrip(id, trip_spec, maybe_req, maybe_path) => {
|
||||
self.trips.start_trip(
|
||||
@ -576,10 +576,12 @@ impl Sim {
|
||||
self.intersections
|
||||
.update_intersection(self.time, i, map, &mut self.scheduler);
|
||||
}
|
||||
Command::Savestate(frequency) => {
|
||||
Command::Callback(frequency) => {
|
||||
self.scheduler
|
||||
.push(self.time + frequency, Command::Savestate(frequency));
|
||||
savestate = true;
|
||||
.push(self.time + frequency, Command::Callback(frequency));
|
||||
if maybe_cb.as_mut().unwrap().run(self, map) {
|
||||
halt = true;
|
||||
}
|
||||
}
|
||||
Command::Pandemic(cmd) => {
|
||||
self.pandemic
|
||||
@ -601,7 +603,7 @@ impl Sim {
|
||||
// Record events at precisely the time they occur.
|
||||
self.dispatch_events(events, map);
|
||||
|
||||
savestate
|
||||
halt
|
||||
}
|
||||
|
||||
fn dispatch_events(&mut self, mut events: Vec<Event>, map: &Map) {
|
||||
@ -620,14 +622,22 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timed_step(&mut self, map: &Map, dt: Duration, timer: &mut Timer) {
|
||||
pub fn timed_step(
|
||||
&mut self,
|
||||
map: &Map,
|
||||
dt: Duration,
|
||||
maybe_cb: &mut Option<Box<dyn SimCallback>>,
|
||||
timer: &mut Timer,
|
||||
) {
|
||||
let end_time = self.time + dt;
|
||||
let start = Instant::now();
|
||||
let mut last_update = Instant::now();
|
||||
|
||||
timer.start(format!("Advance sim to {}", end_time));
|
||||
while self.time < end_time {
|
||||
self.minimal_step(map, end_time - self.time);
|
||||
if self.minimal_step(map, end_time - self.time, maybe_cb) {
|
||||
break;
|
||||
}
|
||||
if !self.analytics.alerts.is_empty() {
|
||||
match self.alerts {
|
||||
AlertHandler::Print => {
|
||||
@ -658,32 +668,29 @@ impl Sim {
|
||||
}
|
||||
timer.stop(format!("Advance sim to {}", end_time));
|
||||
}
|
||||
pub fn tiny_step(&mut self, map: &Map) {
|
||||
self.timed_step(map, Duration::seconds(0.1), &mut Timer::throwaway());
|
||||
pub fn tiny_step(&mut self, map: &Map, maybe_cb: &mut Option<Box<dyn SimCallback>>) {
|
||||
self.timed_step(
|
||||
map,
|
||||
Duration::seconds(0.1),
|
||||
maybe_cb,
|
||||
&mut Timer::throwaway(),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO Do this like periodic savestating instead?
|
||||
pub fn set_gridlock_checker(&mut self, freq: Option<Duration>) {
|
||||
if let Some(dt) = freq {
|
||||
assert!(self.check_for_gridlock.is_none());
|
||||
self.check_for_gridlock = Some((self.time + dt, dt));
|
||||
} else {
|
||||
assert!(self.check_for_gridlock.is_some());
|
||||
self.check_for_gridlock = None;
|
||||
}
|
||||
}
|
||||
// This will return delayed intersections if that's why it stops early.
|
||||
pub fn time_limited_step(
|
||||
&mut self,
|
||||
map: &Map,
|
||||
dt: Duration,
|
||||
real_time_limit: Duration,
|
||||
) -> Option<Vec<(IntersectionID, Time)>> {
|
||||
maybe_cb: &mut Option<Box<dyn SimCallback>>,
|
||||
) {
|
||||
let started_at = Instant::now();
|
||||
let end_time = self.time + dt;
|
||||
|
||||
while self.time < end_time && Duration::realtime_elapsed(started_at) < real_time_limit {
|
||||
self.minimal_step(map, end_time - self.time);
|
||||
if self.minimal_step(map, end_time - self.time, maybe_cb) {
|
||||
break;
|
||||
}
|
||||
if !self.analytics.alerts.is_empty() {
|
||||
match self.alerts {
|
||||
AlertHandler::Print => {
|
||||
@ -702,18 +709,7 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((ref mut t, dt)) = self.check_for_gridlock {
|
||||
if self.time >= *t {
|
||||
*t += dt;
|
||||
let gridlock = self.delayed_intersections(dt);
|
||||
if !gridlock.is_empty() {
|
||||
return Some(gridlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn dump_before_abort(&self) {
|
||||
@ -745,7 +741,7 @@ impl Sim {
|
||||
let dt = time_limit.unwrap_or_else(|| Duration::seconds(30.0));
|
||||
|
||||
match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
self.timed_step(map, dt, &mut Timer::throwaway());
|
||||
self.timed_step(map, dt, &mut None, &mut Timer::throwaway());
|
||||
})) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
@ -1197,6 +1193,27 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
pub trait SimCallback: downcast_rs::Downcast {
|
||||
// Run at some scheduled time. If this returns true, halt simulation.
|
||||
fn run(&mut self, sim: &Sim, map: &Map) -> bool;
|
||||
}
|
||||
downcast_rs::impl_downcast!(SimCallback);
|
||||
|
||||
impl Sim {
|
||||
// Only one at a time supported.
|
||||
pub fn set_periodic_callback(&mut self, frequency: Duration) {
|
||||
// TODO Round up time nicely?
|
||||
self.scheduler
|
||||
.push(self.time + frequency, Command::Callback(frequency));
|
||||
}
|
||||
pub fn unset_periodic_callback(&mut self) {
|
||||
// Frequency doesn't matter
|
||||
self.scheduler
|
||||
.cancel(Command::Callback(Duration::seconds(1.0)));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AgentProperties {
|
||||
// TODO Of this leg of the trip only!
|
||||
pub total_time: Duration,
|
||||
|
Loading…
Reference in New Issue
Block a user