mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +03:00
plumb through alerts from the sim that pause the UI, use for gridlock
This commit is contained in:
parent
09e9e52042
commit
067cc5b307
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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(¤t) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user