mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +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);
|
.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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
||||||
|
@ -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(¤t) {
|
if seen.contains(¤t) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user