plumb through alerts from the sim that pause the UI, use for gridlock

This commit is contained in:
Dustin Carlino 2020-04-21 12:17:01 -07:00
parent 09e9e52042
commit 067cc5b307
6 changed files with 53 additions and 11 deletions

View File

@ -135,7 +135,6 @@ impl GUI for Game {
.on_suspend(ctx, &mut self.app); .on_suspend(ctx, &mut self.app);
self.states.push(s1); self.states.push(s1);
self.states.push(s2); self.states.push(s2);
return EventLoopMode::InputOnly;
} }
}; };
// Let the new state initialize with a fake event. Usually these just return // Let the new state initialize with a fake event. Usually these just return

View File

@ -269,6 +269,28 @@ impl SpeedControls {
} }
} }
// TODO Need to do this anywhere that steps the sim, like TimeWarpScreen.
let alerts = app.primary.sim.clear_alerts();
if !alerts.is_empty() {
self.pause(ctx, app);
// Just go to the first one, but print all messages
let id = ID::Intersection(alerts[0].1);
return Some(Transition::PushTwice(
msg(
"Alerts",
alerts.into_iter().map(|(_, _, msg)| msg).collect(),
),
Warping::new(
ctx,
id.canonical_point(&app.primary).unwrap(),
Some(10.0),
None,
&mut app.primary,
),
));
}
None None
} }

View File

@ -26,6 +26,7 @@ pub struct Analytics {
pub intersection_delays: BTreeMap<IntersectionID, Vec<(Time, Duration)>>, pub intersection_delays: BTreeMap<IntersectionID, Vec<(Time, Duration)>>,
// Per parking lane, when does a spot become filled (true) or free (false) // Per parking lane, when does a spot become filled (true) or free (false)
parking_spot_changes: BTreeMap<LaneID, Vec<(Time, bool)>>, parking_spot_changes: BTreeMap<LaneID, Vec<(Time, bool)>>,
pub(crate) alerts: Vec<(Time, IntersectionID, String)>,
// After we restore from a savestate, don't record anything. This is only going to make sense // After we restore from a savestate, don't record anything. This is only going to make sense
// if savestates are only used for quickly previewing against prebaked results, where we have // if savestates are only used for quickly previewing against prebaked results, where we have
@ -65,6 +66,7 @@ impl Analytics {
trip_log: Vec::new(), trip_log: Vec::new(),
intersection_delays: BTreeMap::new(), intersection_delays: BTreeMap::new(),
parking_spot_changes: BTreeMap::new(), parking_spot_changes: BTreeMap::new(),
alerts: Vec::new(),
record_anything: true, record_anything: true,
} }
} }
@ -192,6 +194,9 @@ impl Analytics {
Event::PathAmended(path) => { Event::PathAmended(path) => {
self.record_demand(&path, map); self.record_demand(&path, map);
} }
Event::Alert(i, msg) => {
self.alerts.push((time, i, msg));
}
_ => {} _ => {}
} }
} }

View File

@ -46,6 +46,8 @@ pub enum Event {
// Just use for parking replanning. Not happy about copying the full path in here, but the way // Just use for parking replanning. Not happy about copying the full path in here, but the way
// to plumb info into Analytics is Event. // to plumb info into Analytics is Event.
PathAmended(Path), PathAmended(Path),
Alert(IntersectionID, String),
} }
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]

View File

@ -219,8 +219,8 @@ impl IntersectionSimState {
let allowed = if self.use_freeform_policy_everywhere { let allowed = if self.use_freeform_policy_everywhere {
state.freeform_policy( state.freeform_policy(
&req, &req,
now,
map, map,
&mut self.events,
if self.break_turn_conflict_cycles { if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by) Some(&mut self.blocked_by)
} else { } else {
@ -235,6 +235,7 @@ impl IntersectionSimState {
now, now,
map, map,
scheduler, scheduler,
&mut self.events,
if self.break_turn_conflict_cycles { if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by) Some(&mut self.blocked_by)
} else { } else {
@ -248,6 +249,7 @@ impl IntersectionSimState {
now, now,
map, map,
scheduler, scheduler,
&mut self.events,
if self.break_turn_conflict_cycles { if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by) Some(&mut self.blocked_by)
} else { } else {
@ -372,8 +374,8 @@ impl State {
fn handle_accepted_conflicts( fn handle_accepted_conflicts(
&self, &self,
req: &Request, req: &Request,
now: Time,
map: &Map, map: &Map,
events: &mut Vec<Event>,
mut maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>, mut maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool { ) -> bool {
let turn = map.get_t(req.turn); let turn = map.get_t(req.turn);
@ -393,10 +395,10 @@ impl State {
while !queue.is_empty() { while !queue.is_empty() {
let current = queue.pop().unwrap(); let current = queue.pop().unwrap();
if seen.contains(&current) { if seen.contains(&current) {
println!( events.push(Event::Alert(
"!!! At {}, gridlock near {}. Allowing {:?}!", req.turn.parent,
now, req.turn.parent, req.agent format!("Turn conflict cycle involving {:?}", seen),
); ));
return true; return true;
} }
seen.insert(current); seen.insert(current);
@ -416,12 +418,12 @@ impl State {
fn freeform_policy( fn freeform_policy(
&self, &self,
req: &Request, req: &Request,
now: Time,
map: &Map, map: &Map,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>, maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool { ) -> bool {
// Allow concurrent turns that don't conflict // Allow concurrent turns that don't conflict
self.handle_accepted_conflicts(req, now, map, maybe_blocked_by) self.handle_accepted_conflicts(req, map, events, maybe_blocked_by)
} }
fn stop_sign_policy( fn stop_sign_policy(
@ -431,9 +433,10 @@ impl State {
now: Time, now: Time,
map: &Map, map: &Map,
scheduler: &mut Scheduler, scheduler: &mut Scheduler,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>, maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool { ) -> bool {
if !self.handle_accepted_conflicts(req, now, map, maybe_blocked_by) { if !self.handle_accepted_conflicts(req, map, events, maybe_blocked_by) {
return false; return false;
} }
@ -478,6 +481,7 @@ impl State {
now: Time, now: Time,
map: &Map, map: &Map,
scheduler: &mut Scheduler, scheduler: &mut Scheduler,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>, maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool { ) -> bool {
let turn = map.get_t(req.turn); let turn = map.get_t(req.turn);
@ -496,7 +500,7 @@ impl State {
} }
// Somebody might already be doing a Yield turn that conflicts with this one. // Somebody might already be doing a Yield turn that conflicts with this one.
if !self.handle_accepted_conflicts(req, now, map, maybe_blocked_by) { if !self.handle_accepted_conflicts(req, map, events, maybe_blocked_by) {
return false; return false;
} }

View File

@ -681,6 +681,9 @@ impl Sim {
timer.start(format!("Advance sim to {}", end_time)); timer.start(format!("Advance sim to {}", end_time));
while self.time < end_time { while self.time < end_time {
if !self.analytics.alerts.is_empty() {
break;
}
self.minimal_step(map, end_time - self.time); self.minimal_step(map, end_time - self.time);
if Duration::realtime_elapsed(last_update) >= Duration::seconds(1.0) { if Duration::realtime_elapsed(last_update) >= Duration::seconds(1.0) {
// TODO Not timer? // TODO Not timer?
@ -719,6 +722,9 @@ impl Sim {
let end_time = self.time + dt; let end_time = self.time + dt;
while self.time < end_time && Duration::realtime_elapsed(started_at) < real_time_limit { while self.time < end_time && Duration::realtime_elapsed(started_at) < real_time_limit {
if !self.analytics.alerts.is_empty() {
break;
}
self.minimal_step(map, end_time - self.time); self.minimal_step(map, end_time - self.time);
if let Some((ref mut t, dt)) = self.check_for_gridlock { if let Some((ref mut t, dt)) = self.check_for_gridlock {
if self.time >= *t { if self.time >= *t {
@ -1218,6 +1224,10 @@ impl Sim {
println!("{} has no trip?!", id); println!("{} has no trip?!", id);
} }
} }
pub fn clear_alerts(&mut self) -> Vec<(Time, IntersectionID, String)> {
std::mem::replace(&mut self.analytics.alerts, Vec::new())
}
} }
pub struct AgentProperties { pub struct AgentProperties {