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);
self.states.push(s1);
self.states.push(s2);
return EventLoopMode::InputOnly;
}
};
// 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
}

View File

@ -26,6 +26,7 @@ pub struct Analytics {
pub intersection_delays: BTreeMap<IntersectionID, Vec<(Time, Duration)>>,
// Per parking lane, when does a spot become filled (true) or free (false)
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
// if savestates are only used for quickly previewing against prebaked results, where we have
@ -65,6 +66,7 @@ impl Analytics {
trip_log: Vec::new(),
intersection_delays: BTreeMap::new(),
parking_spot_changes: BTreeMap::new(),
alerts: Vec::new(),
record_anything: true,
}
}
@ -192,6 +194,9 @@ impl Analytics {
Event::PathAmended(path) => {
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
// to plumb info into Analytics is Event.
PathAmended(Path),
Alert(IntersectionID, String),
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]

View File

@ -219,8 +219,8 @@ impl IntersectionSimState {
let allowed = if self.use_freeform_policy_everywhere {
state.freeform_policy(
&req,
now,
map,
&mut self.events,
if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by)
} else {
@ -235,6 +235,7 @@ impl IntersectionSimState {
now,
map,
scheduler,
&mut self.events,
if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by)
} else {
@ -248,6 +249,7 @@ impl IntersectionSimState {
now,
map,
scheduler,
&mut self.events,
if self.break_turn_conflict_cycles {
Some(&mut self.blocked_by)
} else {
@ -372,8 +374,8 @@ impl State {
fn handle_accepted_conflicts(
&self,
req: &Request,
now: Time,
map: &Map,
events: &mut Vec<Event>,
mut maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool {
let turn = map.get_t(req.turn);
@ -393,10 +395,10 @@ impl State {
while !queue.is_empty() {
let current = queue.pop().unwrap();
if seen.contains(&current) {
println!(
"!!! At {}, gridlock near {}. Allowing {:?}!",
now, req.turn.parent, req.agent
);
events.push(Event::Alert(
req.turn.parent,
format!("Turn conflict cycle involving {:?}", seen),
));
return true;
}
seen.insert(current);
@ -416,12 +418,12 @@ impl State {
fn freeform_policy(
&self,
req: &Request,
now: Time,
map: &Map,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool {
// 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(
@ -431,9 +433,10 @@ impl State {
now: Time,
map: &Map,
scheduler: &mut Scheduler,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> 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;
}
@ -478,6 +481,7 @@ impl State {
now: Time,
map: &Map,
scheduler: &mut Scheduler,
events: &mut Vec<Event>,
maybe_blocked_by: Option<&mut Vec<(AgentID, AgentID)>>,
) -> bool {
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.
if !self.handle_accepted_conflicts(req, now, map, maybe_blocked_by) {
if !self.handle_accepted_conflicts(req, map, events, maybe_blocked_by) {
return false;
}

View File

@ -681,6 +681,9 @@ impl Sim {
timer.start(format!("Advance sim to {}", end_time));
while self.time < end_time {
if !self.analytics.alerts.is_empty() {
break;
}
self.minimal_step(map, end_time - self.time);
if Duration::realtime_elapsed(last_update) >= Duration::seconds(1.0) {
// TODO Not timer?
@ -719,6 +722,9 @@ impl Sim {
let end_time = self.time + dt;
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);
if let Some((ref mut t, dt)) = self.check_for_gridlock {
if self.time >= *t {
@ -1218,6 +1224,10 @@ impl Sim {
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 {