track blocked time for finished trips, make a way to find people with

high blocked times
This commit is contained in:
Dustin Carlino 2020-04-08 15:50:25 -07:00
parent 008ac27abb
commit 3e49134088
11 changed files with 165 additions and 49 deletions

View File

@ -111,7 +111,7 @@ pub fn trips(
.has_prebaked()
.and_then(|_| app.prebaked().finished_trip_time(*t))
{
let experiment = app.primary.sim.finished_trip_time(*t);
let (experiment, _) = app.primary.sim.finished_trip_time(*t);
let mut txt = Text::from(Line("finished ").small());
txt.append_all(cmp_duration_shorter(experiment, orig));
txt.draw(ctx)

View File

@ -39,8 +39,9 @@ pub fn details(ctx: &mut EventCtx, app: &App, trip: TripID, details: &mut Detail
let total_trip_time = end_time.unwrap_or_else(|| sim.time()) - phases[0].start_time;
// Describe this leg of the trip
let col_width = 7;
let progress_along_path = if let Some(a) = sim.trip_to_agent(trip).ok() {
let col_width = 7;
let props = sim.agent_properties(a);
// This is different than the entire TripMode, and also not the current TripPhaseType.
// Sigh.
@ -101,11 +102,19 @@ pub fn details(ctx: &mut EventCtx, app: &App, trip: TripID, details: &mut Detail
Some(props.dist_crossed / props.total_dist)
} else {
let col_width = 15;
// The trip is finished
col.push(Widget::row(vec![
Widget::row(vec![Line("Trip time").secondary().draw(ctx)]).force_width(ctx, col_width),
total_trip_time.to_string().draw_text(ctx),
]));
let (_, waiting) = sim.finished_trip_time(trip);
col.push(Widget::row(vec![
Widget::row(vec![Line("Total waiting time").secondary().draw(ctx)])
.force_width(ctx, col_width),
waiting.to_string().draw_text(ctx),
]));
None
};

View File

@ -414,7 +414,7 @@ impl Layers {
app,
population::Options {
heatmap: Some(HeatmapOptions::new()),
with_finished_trip: false,
with_finished_trip_blocked_pct: None,
},
);
Some(Transition::Pop)

View File

@ -4,7 +4,7 @@ use crate::layer::Layers;
use abstutil::prettyprint_usize;
use ezgui::{
hotkey, Btn, Checkbox, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line,
VerticalAlignment, Widget,
Spinner, TextExt, VerticalAlignment, Widget,
};
use geom::{Circle, Distance, Pt2D};
use sim::{GetDrawAgents, PersonState, TripResult};
@ -14,11 +14,12 @@ use std::collections::HashSet;
// return this kind of data instead!
pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
let filter = |p| {
if opts.with_finished_trip {
if let Some(pct) = opts.with_finished_trip_blocked_pct {
// TODO This is probably inefficient...
app.primary.sim.get_person(p).trips.iter().any(|t| {
if let TripResult::TripDone = app.primary.sim.trip_to_agent(*t) {
true
let (total, blocked) = app.primary.sim.finished_trip_time(*t);
(100.0 * blocked / total) as usize >= pct
} else {
false
}
@ -88,7 +89,7 @@ pub struct Options {
// If None, just a dot map
pub heatmap: Option<HeatmapOptions>,
// TODO More filters... Find people with finished/future trips of any/some mode
pub with_finished_trip: bool,
pub with_finished_trip_blocked_pct: Option<usize>,
}
fn make_controls(
@ -122,8 +123,17 @@ fn make_controls(
ctx,
"Filter by people with a finished trip",
None,
opts.with_finished_trip,
opts.with_finished_trip_blocked_pct.is_some(),
));
if let Some(pct) = opts.with_finished_trip_blocked_pct {
col.push(Widget::row(vec![
"% of time spent waiting".draw_text(ctx).margin(5),
Spinner::new(ctx, (0, 100), pct)
.named("blocked ratio")
.align_right()
.centered_vert(),
]));
}
col.push(Checkbox::text(
ctx,
@ -148,6 +158,15 @@ pub fn options(c: &mut Composite) -> Options {
};
Options {
heatmap,
with_finished_trip: c.is_checked("Filter by people with a finished trip"),
with_finished_trip_blocked_pct: if c.is_checked("Filter by people with a finished trip") {
if c.has_widget("blocked ratio") {
Some(c.spinner("blocked ratio"))
} else {
// Just changed, use default
Some(0)
}
} else {
None
},
}
}

View File

@ -131,8 +131,15 @@ impl Analytics {
}
// Finished trips
if let Event::TripFinished(id, mode, dt) = ev {
self.finished_trips.push((time, id, Some(mode), dt));
if let Event::TripFinished {
trip,
mode,
total_time,
..
} = ev
{
self.finished_trips
.push((time, trip, Some(mode), total_time));
} else if let Event::TripAborted(id, mode) = ev {
self.finished_trips.push((time, id, None, Duration::ZERO));
if !self.started_trips.contains_key(&id) {
@ -156,9 +163,9 @@ impl Analytics {
Event::TripAborted(id, _) => {
self.trip_log.push((time, id, None, TripPhaseType::Aborted));
}
Event::TripFinished(id, _, _) => {
Event::TripFinished { trip, .. } => {
self.trip_log
.push((time, id, None, TripPhaseType::Finished));
.push((time, trip, None, TripPhaseType::Finished));
}
Event::PathAmended(path) => {
self.record_demand(&path, map);

View File

@ -27,7 +27,12 @@ pub enum Event {
AgentEntersTraversable(AgentID, Traversable),
IntersectionDelayMeasured(IntersectionID, Duration),
TripFinished(TripID, TripMode, Duration),
TripFinished {
trip: TripID,
mode: TripMode,
total_time: Duration,
blocked_time: Duration,
},
TripAborted(TripID, TripMode),
TripPhaseStarting(
TripID,

View File

@ -451,7 +451,12 @@ impl DrivingSimState {
) {
Some(ActionAtEnd::VanishAtBorder(i)) => {
car.total_blocked_time += now - blocked_since;
trips.car_or_bike_reached_border(now, car.vehicle.id, i);
trips.car_or_bike_reached_border(
now,
car.vehicle.id,
i,
car.total_blocked_time,
);
false
}
Some(ActionAtEnd::AbortTrip) => {
@ -483,7 +488,14 @@ impl DrivingSimState {
}
Some(ActionAtEnd::StopBiking(bike_rack)) => {
car.total_blocked_time += now - blocked_since;
trips.bike_reached_end(now, car.vehicle.id, bike_rack, map, scheduler);
trips.bike_reached_end(
now,
car.vehicle.id,
bike_rack,
car.total_blocked_time,
map,
scheduler,
);
false
}
Some(ActionAtEnd::BusAtStop) => {
@ -538,7 +550,15 @@ impl DrivingSimState {
vehicle: car.vehicle.clone(),
spot,
});
trips.car_reached_parking_spot(now, car.vehicle.id, spot, map, parking, scheduler);
trips.car_reached_parking_spot(
now,
car.vehicle.id,
spot,
car.total_blocked_time,
map,
parking,
scheduler,
);
false
}
}

View File

@ -125,7 +125,13 @@ impl WalkingSimState {
self.peds_per_traversable
.remove(ped.path.current_step().as_traversable(), ped.id);
trips.ped_reached_parking_spot(
now, ped.id, spot, map, parking, scheduler,
now,
ped.id,
spot,
ped.total_blocked_time,
map,
parking,
scheduler,
);
self.peds.remove(&id);
}
@ -140,9 +146,14 @@ impl WalkingSimState {
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
}
SidewalkPOI::BusStop(stop) => {
if let Some(route) =
trips.ped_reached_bus_stop(now, ped.id, stop, map, transit)
{
if let Some(route) = trips.ped_reached_bus_stop(
now,
ped.id,
stop,
ped.total_blocked_time,
map,
transit,
) {
ped.state = PedState::WaitingForBus(route, now);
} else {
self.peds_per_traversable
@ -153,7 +164,7 @@ impl WalkingSimState {
SidewalkPOI::Border(i) => {
self.peds_per_traversable
.remove(ped.path.current_step().as_traversable(), ped.id);
trips.ped_reached_border(now, ped.id, i, map);
trips.ped_reached_border(now, ped.id, i, ped.total_blocked_time, map);
self.peds.remove(&id);
}
SidewalkPOI::BikeRack(driving_pos) => {
@ -217,13 +228,20 @@ impl WalkingSimState {
PedState::EnteringBuilding(bldg, _) => {
self.peds_per_traversable
.remove(ped.path.current_step().as_traversable(), ped.id);
trips.ped_reached_building(now, ped.id, bldg, map);
trips.ped_reached_building(now, ped.id, bldg, ped.total_blocked_time, map);
self.peds.remove(&id);
}
PedState::StartingToBike(ref spot, _, _) => {
self.peds_per_traversable
.remove(ped.path.current_step().as_traversable(), ped.id);
trips.ped_ready_to_bike(now, ped.id, spot.clone(), map, scheduler);
trips.ped_ready_to_bike(
now,
ped.id,
spot.clone(),
ped.total_blocked_time,
map,
scheduler,
);
self.peds.remove(&id);
}
PedState::FinishingBiking(ref spot, _, _) => {

View File

@ -535,6 +535,7 @@ impl Sim {
self.time,
create_ped.id,
ParkingSpot::Offstreet(*b2, *idx),
Duration::ZERO,
map,
&self.parking,
&mut self.scheduler,
@ -966,7 +967,8 @@ impl Sim {
pub fn trip_info(&self, id: TripID) -> (Time, TripEndpoint, TripEndpoint, TripMode) {
self.trips.trip_info(id)
}
pub fn finished_trip_time(&self, id: TripID) -> Duration {
// Only for finished trips. Returns (total time, total waiting time)
pub fn finished_trip_time(&self, id: TripID) -> (Duration, Duration) {
self.trips.finished_trip_time(id)
}

View File

@ -182,7 +182,8 @@ impl TransitSimState {
{
if bus.route == route {
bus.passengers.push((ped, stop2));
let (trip, person) = trips.ped_boarded_bus(now, ped, walking);
let (trip, person) =
trips.ped_boarded_bus(now, ped, now - started_waiting, walking);
self.events.push(Event::TripPhaseStarting(
trip,
person,

View File

@ -103,6 +103,7 @@ impl TripManager {
person,
spawned_at,
finished_at: None,
total_blocked_time: Duration::ZERO,
aborted: false,
mode,
legs: VecDeque::from(legs),
@ -156,12 +157,14 @@ impl TripManager {
now: Time,
car: CarID,
spot: ParkingSpot,
blocked_time: Duration,
map: &Map,
parking: &ParkingSimState,
scheduler: &mut Scheduler,
) {
self.events.push(Event::CarReachedParkingSpot(car, spot));
let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
trip.total_blocked_time += blocked_time;
match trip.legs.pop_front() {
Some(TripLeg::Drive(vehicle, DrivingGoal::ParkNear(_))) => assert_eq!(car, vehicle.id),
@ -176,11 +179,12 @@ impl TripManager {
assert!(!trip.finished_at.is_some());
trip.finished_at = Some(now);
self.unfinished_trips -= 1;
self.events.push(Event::TripFinished(
trip.id,
trip.mode,
now - trip.spawned_at,
));
self.events.push(Event::TripFinished {
trip: trip.id,
mode: trip.mode,
total_time: now - trip.spawned_at,
blocked_time: trip.total_blocked_time,
});
self.people[trip.person.0].state = PersonState::Inside(b1);
self.events
.push(Event::PersonEntersBuilding(trip.person, b1));
@ -206,6 +210,7 @@ impl TripManager {
now: Time,
ped: PedestrianID,
spot: ParkingSpot,
blocked_time: Duration,
map: &Map,
parking: &ParkingSimState,
scheduler: &mut Scheduler,
@ -216,6 +221,7 @@ impl TripManager {
.remove(&AgentID::Pedestrian(ped))
.unwrap()
.0];
trip.total_blocked_time += blocked_time;
trip.assert_walking_leg(ped, SidewalkSpot::parking_spot(spot, map, parking));
let (car, drive_to) = match trip.legs[0] {
@ -272,6 +278,7 @@ impl TripManager {
now: Time,
ped: PedestrianID,
spot: SidewalkSpot,
blocked_time: Duration,
map: &Map,
scheduler: &mut Scheduler,
) {
@ -280,6 +287,7 @@ impl TripManager {
.remove(&AgentID::Pedestrian(ped))
.unwrap()
.0];
trip.total_blocked_time += blocked_time;
trip.assert_walking_leg(ped, spot.clone());
let (vehicle, drive_to) = match trip.legs[0] {
@ -326,6 +334,7 @@ impl TripManager {
now: Time,
bike: CarID,
bike_rack: SidewalkSpot,
blocked_time: Duration,
map: &Map,
scheduler: &mut Scheduler,
) {
@ -334,6 +343,7 @@ impl TripManager {
bike_rack.sidewalk_pos.lane(),
));
let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(bike)).unwrap().0];
trip.total_blocked_time += blocked_time;
match trip.legs.pop_front() {
Some(TripLeg::Drive(vehicle, DrivingGoal::ParkNear(_))) => assert_eq!(vehicle.id, bike),
@ -350,6 +360,7 @@ impl TripManager {
now: Time,
ped: PedestrianID,
bldg: BuildingID,
blocked_time: Duration,
map: &Map,
) {
let trip = &mut self.trips[self
@ -357,16 +368,19 @@ impl TripManager {
.remove(&AgentID::Pedestrian(ped))
.unwrap()
.0];
trip.total_blocked_time += blocked_time;
trip.assert_walking_leg(ped, SidewalkSpot::building(bldg, map));
assert!(trip.legs.is_empty());
assert!(!trip.finished_at.is_some());
trip.finished_at = Some(now);
self.unfinished_trips -= 1;
self.events.push(Event::TripFinished(
trip.id,
trip.mode,
now - trip.spawned_at,
));
self.events.push(Event::TripFinished {
trip: trip.id,
mode: trip.mode,
total_time: now - trip.spawned_at,
blocked_time: trip.total_blocked_time,
});
self.people[trip.person.0].state = PersonState::Inside(bldg);
self.events
.push(Event::PersonEntersBuilding(trip.person, bldg));
@ -378,10 +392,13 @@ impl TripManager {
now: Time,
ped: PedestrianID,
stop: BusStopID,
blocked_time: Duration,
map: &Map,
transit: &mut TransitSimState,
) -> Option<BusRouteID> {
let trip = &mut self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0];
trip.total_blocked_time += blocked_time;
match trip.legs[0] {
TripLeg::Walk(p, _, ref spot) => {
assert_eq!(p, ped);
@ -422,15 +439,19 @@ impl TripManager {
&mut self,
now: Time,
ped: PedestrianID,
blocked_time: Duration,
walking: &mut WalkingSimState,
) -> (TripID, PersonID) {
// TODO Make sure canonical pt is the bus while the ped is riding it
let trip = &mut self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0];
trip.total_blocked_time += blocked_time;
trip.legs.pop_front();
walking.ped_boarded_bus(now, ped);
(trip.id, trip.person)
}
// TODO Need to characterize delay the bus experienced
pub fn ped_left_bus(
&mut self,
now: Time,
@ -458,6 +479,7 @@ impl TripManager {
now: Time,
ped: PedestrianID,
i: IntersectionID,
blocked_time: Duration,
map: &Map,
) {
self.events.push(Event::PedReachedBorder(ped, i));
@ -466,22 +488,33 @@ impl TripManager {
.remove(&AgentID::Pedestrian(ped))
.unwrap()
.0];
trip.total_blocked_time += blocked_time;
trip.assert_walking_leg(ped, SidewalkSpot::end_at_border(i, map).unwrap());
assert!(trip.legs.is_empty());
assert!(!trip.finished_at.is_some());
trip.finished_at = Some(now);
self.unfinished_trips -= 1;
self.events.push(Event::TripFinished(
trip.id,
trip.mode,
now - trip.spawned_at,
));
self.events.push(Event::TripFinished {
trip: trip.id,
mode: trip.mode,
total_time: now - trip.spawned_at,
blocked_time: trip.total_blocked_time,
});
self.people[trip.person.0].state = PersonState::OffMap;
}
pub fn car_or_bike_reached_border(&mut self, now: Time, car: CarID, i: IntersectionID) {
pub fn car_or_bike_reached_border(
&mut self,
now: Time,
car: CarID,
i: IntersectionID,
blocked_time: Duration,
) {
self.events.push(Event::CarOrBikeReachedBorder(car, i));
let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
trip.total_blocked_time += blocked_time;
match trip.legs.pop_front().unwrap() {
TripLeg::Drive(_, DrivingGoal::Border(int, _)) => assert_eq!(i, int),
_ => unreachable!(),
@ -490,11 +523,12 @@ impl TripManager {
assert!(!trip.finished_at.is_some());
trip.finished_at = Some(now);
self.unfinished_trips -= 1;
self.events.push(Event::TripFinished(
trip.id,
trip.mode,
now - trip.spawned_at,
));
self.events.push(Event::TripFinished {
trip: trip.id,
mode: trip.mode,
total_time: now - trip.spawned_at,
blocked_time: trip.total_blocked_time,
});
self.people[trip.person.0].state = PersonState::OffMap;
}
@ -616,9 +650,9 @@ impl TripManager {
let t = &self.trips[id.0];
(t.spawned_at, t.start.clone(), t.end.clone(), t.mode)
}
pub fn finished_trip_time(&self, id: TripID) -> Duration {
pub fn finished_trip_time(&self, id: TripID) -> (Duration, Duration) {
let t = &self.trips[id.0];
t.finished_at.unwrap() - t.spawned_at
(t.finished_at.unwrap() - t.spawned_at, t.total_blocked_time)
}
pub fn count_trips(&self, endpt: TripEndpoint, now: Time) -> TripCount {
@ -708,6 +742,7 @@ struct Trip {
id: TripID,
spawned_at: Time,
finished_at: Option<Time>,
total_blocked_time: Duration,
aborted: bool,
legs: VecDeque<TripLeg>,
mode: TripMode,