From fd2fbf6a5ef88bea214cc62e27b64fcb62b9986e Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sat, 4 Jan 2020 10:11:22 -0600 Subject: [PATCH] info panel for bus stop has number of people waiting currently, time spent waiting --- game/src/common/info.rs | 9 +++---- sim/src/analytics.rs | 8 ------ sim/src/mechanics/walking.rs | 2 +- sim/src/sim.rs | 10 ++++--- sim/src/transit.rs | 51 +++++++++++++++++++++++++++--------- sim/src/trips.rs | 3 ++- 6 files changed, 53 insertions(+), 30 deletions(-) diff --git a/game/src/common/info.rs b/game/src/common/info.rs index 0f97a5c3d3..5491eacba6 100644 --- a/game/src/common/info.rs +++ b/game/src/common/info.rs @@ -261,7 +261,7 @@ fn info_for(id: ID, ui: &UI) -> Text { } ID::BusStop(id) => { let all_arrivals = &sim.get_analytics().bus_arrivals; - let passengers = &sim.get_analytics().total_bus_passengers; + let mut waiting = sim.peds_waiting_stats(id); for r in map.get_routes_serving_stop(id) { txt.add_appended(vec![Line("- Route "), Line(&r.name).fg(name_color)]); let arrivals: Vec<(Time, CarID)> = all_arrivals @@ -278,10 +278,9 @@ fn info_for(id: ID, ui: &UI) -> Text { } else { txt.add(Line(" No arrivals yet")); } - txt.add(Line(format!( - " {} passengers total (any stop)", - prettyprint_usize(passengers.get(r.id)) - ))); + if let Some(hgram) = waiting.remove(&r.id) { + txt.add(Line(format!(" Waiting: {}", hgram.describe()))); + } } } ID::Area(id) => { diff --git a/sim/src/analytics.rs b/sim/src/analytics.rs index 9b315decfa..c48078e068 100644 --- a/sim/src/analytics.rs +++ b/sim/src/analytics.rs @@ -14,8 +14,6 @@ pub struct Analytics { #[serde(skip_serializing, skip_deserializing)] pub(crate) test_expectations: VecDeque, pub bus_arrivals: Vec<(Time, CarID, BusRouteID, BusStopID)>, - #[serde(skip_serializing, skip_deserializing)] - pub total_bus_passengers: Counter, // TODO Hack: No TripMode means aborted // Finish time, ID, mode (or None as aborted), trip duration pub finished_trips: Vec<(Time, TripID, Option, Duration)>, @@ -55,7 +53,6 @@ impl Analytics { }, test_expectations: VecDeque::new(), bus_arrivals: Vec::new(), - total_bus_passengers: Counter::new(), finished_trips: Vec::new(), trip_log: Vec::new(), intersection_delays: BTreeMap::new(), @@ -116,11 +113,6 @@ impl Analytics { self.bus_arrivals.push((time, bus, route, stop)); } - // Bus passengers - if let Event::PedEntersBus(_, _, route) = ev { - self.total_bus_passengers.inc(route); - } - // Finished trips if let Event::TripFinished(id, mode, dt) = ev { self.finished_trips.push((time, id, Some(mode), dt)); diff --git a/sim/src/mechanics/walking.rs b/sim/src/mechanics/walking.rs index 44013788b1..edb402de1d 100644 --- a/sim/src/mechanics/walking.rs +++ b/sim/src/mechanics/walking.rs @@ -140,7 +140,7 @@ impl WalkingSimState { } SidewalkPOI::BusStop(stop) => { if let Some(route) = - trips.ped_reached_bus_stop(ped.id, stop, map, transit) + trips.ped_reached_bus_stop(now, ped.id, stop, map, transit) { ped.state = PedState::WaitingForBus(route); ped.blocked_since = Some(now); diff --git a/sim/src/sim.rs b/sim/src/sim.rs index 83860cfdc2..098f1ce340 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -8,10 +8,10 @@ use crate::{ }; use abstutil::Timer; use derivative::Derivative; -use geom::{Distance, Duration, PolyLine, Pt2D, Time}; +use geom::{Distance, Duration, DurationHistogram, PolyLine, Pt2D, Time}; use map_model::{ - BuildingID, BusRoute, BusRouteID, IntersectionID, LaneID, Map, Path, PathConstraints, - PathRequest, PathStep, Traversable, + BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, LaneID, Map, Path, + PathConstraints, PathRequest, PathStep, Traversable, }; use serde_derive::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashSet}; @@ -1022,6 +1022,10 @@ impl Sim { pub fn trip_spec_to_path_req(&self, spec: &TripSpec, map: &Map) -> PathRequest { spec.get_pathfinding_request(map, &self.parking) } + + pub fn peds_waiting_stats(&self, stop: BusStopID) -> BTreeMap { + self.transit.peds_waiting_stats(self.time, stop) + } } // Invasive debugging diff --git a/sim/src/transit.rs b/sim/src/transit.rs index d00349c1b4..cb7e5e74c3 100644 --- a/sim/src/transit.rs +++ b/sim/src/transit.rs @@ -1,6 +1,6 @@ use crate::{CarID, Event, PedestrianID, Router, Scheduler, TripManager, WalkingSimState}; use abstutil::{deserialize_btreemap, serialize_btreemap}; -use geom::{Distance, Time}; +use geom::{Distance, DurationHistogram, Time}; use map_model::{ BusRoute, BusRouteID, BusStopID, Map, Path, PathConstraints, PathRequest, Position, }; @@ -53,8 +53,12 @@ pub struct TransitSimState { deserialize_with = "deserialize_btreemap" )] routes: BTreeMap, - // Can organize this more to make querying cheaper - peds_waiting: Vec<(PedestrianID, BusStopID, BusRouteID, BusStopID)>, + // waiting at => (ped, route, bound for, started waiting) + #[serde( + serialize_with = "serialize_btreemap", + deserialize_with = "deserialize_btreemap" + )] + peds_waiting: BTreeMap>, events: Vec, } @@ -64,7 +68,7 @@ impl TransitSimState { TransitSimState { buses: BTreeMap::new(), routes: BTreeMap::new(), - peds_waiting: Vec::new(), + peds_waiting: BTreeMap::new(), events: Vec::new(), } } @@ -153,14 +157,14 @@ impl TransitSimState { match bus.state { BusState::DrivingToStop(stop_idx) => { bus.state = BusState::AtStop(stop_idx); - let stop = self.routes[&bus.route].stops[stop_idx].id; + let stop1 = self.routes[&bus.route].stops[stop_idx].id; self.events - .push(Event::BusArrivedAtStop(id, bus.route, stop)); + .push(Event::BusArrivedAtStop(id, bus.route, stop1)); // Deboard existing passengers. let mut still_riding = Vec::new(); for (ped, stop2) in bus.passengers.drain(..) { - if stop == stop2 { + if stop1 == stop2 { self.events.push(Event::PedLeavesBus(ped, id, bus.route)); trips.ped_left_bus(now, ped, map, scheduler); } else { @@ -171,8 +175,10 @@ impl TransitSimState { // Board new passengers. let mut still_waiting = Vec::new(); - for (ped, stop1, route, stop2) in self.peds_waiting.drain(..) { - if stop == stop1 && bus.route == route { + for (ped, route, stop2, started_waiting) in + self.peds_waiting.remove(&stop1).unwrap_or_else(Vec::new) + { + if bus.route == route { bus.passengers.push((ped, stop2)); self.events.push(Event::PedEntersBus(ped, id, route)); let trip = trips.ped_boarded_bus(ped, walking); @@ -186,10 +192,10 @@ impl TransitSimState { format!("{} riding {}", ped, route), )); } else { - still_waiting.push((ped, stop1, route, stop2)); + still_waiting.push((ped, route, stop2, started_waiting)); } } - self.peds_waiting = still_waiting; + self.peds_waiting.insert(stop1, still_waiting); } BusState::AtStop(_) => unreachable!(), }; @@ -217,6 +223,7 @@ impl TransitSimState { // If true, the pedestrian boarded a bus immediately. pub fn ped_waiting_for_bus( &mut self, + now: Time, ped: PedestrianID, stop1: BusStopID, route_id: BusRouteID, @@ -239,7 +246,10 @@ impl TransitSimState { } } - self.peds_waiting.push((ped, stop1, route_id, stop2)); + self.peds_waiting + .entry(stop1) + .or_insert_with(Vec::new) + .push((ped, route_id, stop2, now)); false } @@ -262,4 +272,21 @@ impl TransitSimState { Vec::new() } } + + pub fn peds_waiting_stats( + &self, + now: Time, + stop: BusStopID, + ) -> BTreeMap { + let mut per_route = BTreeMap::new(); + if let Some(list) = self.peds_waiting.get(&stop) { + for (_, route, _, since) in list { + per_route + .entry(*route) + .or_insert_with(DurationHistogram::new) + .add(now - *since); + } + } + per_route + } } diff --git a/sim/src/trips.rs b/sim/src/trips.rs index eb8c824f36..5cbd0f18ab 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -329,6 +329,7 @@ impl TripManager { // If no route is returned, the pedestrian boarded a bus immediately. pub fn ped_reached_bus_stop( &mut self, + now: Time, ped: PedestrianID, stop: BusStopID, map: &Map, @@ -350,7 +351,7 @@ impl TripManager { None, format!("{} waiting at {:?} for {}", ped, stop, route), )); - if transit.ped_waiting_for_bus(ped, stop, route, stop2) { + if transit.ped_waiting_for_bus(now, ped, stop, route, stop2) { trip.legs.pop_front(); None } else {