start tracking and debugging phases of a trip

This commit is contained in:
Dustin Carlino 2019-11-29 13:15:10 -08:00
parent 1648b56009
commit 8d34759b7c
11 changed files with 179 additions and 75 deletions

View File

@ -3,7 +3,7 @@ use crate::render::DrawMap;
use crate::ui::UI;
use ezgui::{EventCtx, GfxCtx, Key, Line, Text};
use map_model::Map;
use sim::{CarID, Sim};
use sim::{AgentID, CarID, Sim};
pub struct ObjectDebugger {
debug_tooltip_key_held: bool,
@ -91,9 +91,21 @@ fn dump_debug(id: ID, map: &Map, sim: &Sim, draw_map: &DrawMap) {
}
ID::Car(id) => {
sim.debug_car(id);
if let Some(t) = sim.agent_to_trip(AgentID::Car(id)) {
println!("Trip log for {}", t);
for ev in sim.get_analytics().get_trip_log(t) {
println!("- {}", ev);
}
}
}
ID::Pedestrian(id) => {
sim.debug_ped(id);
if let Some(t) = sim.agent_to_trip(AgentID::Pedestrian(id)) {
println!("Trip log for {}", t);
for ev in sim.get_analytics().get_trip_log(t) {
println!("- {}", ev);
}
}
}
ID::PedCrowd(members) => {
println!("Crowd with {} members", members.len());

View File

@ -17,7 +17,9 @@ pub struct Game {
impl Game {
pub fn new(flags: Flags, ctx: &mut EventCtx) -> Game {
let title = !flags.dev && !flags.sim_flags.load.contains("data/save");
let title = !flags.dev
&& !flags.sim_flags.load.contains("data/save")
&& !flags.sim_flags.load.contains("data/scenarios");
let mut ui = UI::new(flags, ctx, title);
let states: Vec<Box<dyn State>> = if title {
vec![Box::new(TitleScreen::new(ctx, &ui))]

View File

@ -122,7 +122,7 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
.map(|m| Choice::new(m.to_string(), m).active(modes.contains(&m)))
.collect()
})?;
wizard.choose("Examine which trip?", || {
let (_, trip) = wizard.choose("Examine which trip?", || {
let trips = ui.primary.sim.get_finished_trips();
let mut filtered: Vec<&(TripID, TripMode, Duration)> = trips
.finished_trips
@ -137,6 +137,15 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
.map(|(id, _, dt)| Choice::new(format!("{} taking {}", id, dt), *id))
.collect()
})?;
// TODO show trip departure, where it started and ended
wizard.acknowledge(&format!("Log for {}", trip), || {
let lines = ui.primary.sim.get_analytics().get_trip_log(trip);
// TODO Because we need word wrap...
for l in &lines {
println!("- {}", l);
}
lines
})?;
Some(Transition::Pop)
}

View File

@ -283,7 +283,7 @@ impl Path {
// Who's asking for a path?
// TODO This is an awful name.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum PathConstraints {
Pedestrian,
Car,
@ -325,7 +325,7 @@ impl PathConstraints {
}
}
#[derive(Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct PathRequest {
pub start: Position,
pub end: Position,

View File

@ -3,7 +3,7 @@ use geom::{Angle, Distance, PolyLine, Pt2D, Speed};
use serde_derive::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Position {
// Don't let callers construct a Position directly, so it's easy to find callers of new().
lane: LaneID,

View File

@ -2,7 +2,7 @@ use crate::{AgentID, CarID, Event, TripID, TripMode, VehicleType};
use abstutil::Counter;
use derivative::Derivative;
use geom::{Duration, DurationHistogram, Time};
use map_model::{BusRouteID, BusStopID, IntersectionID, Map, RoadID, Traversable};
use map_model::{BusRouteID, BusStopID, IntersectionID, Map, PathRequest, RoadID, Traversable};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, VecDeque};
@ -19,6 +19,8 @@ pub struct Analytics {
// TODO Hack: No TripMode means aborted
// Finish time, ID, mode (or None as aborted), trip duration
pub finished_trips: Vec<(Time, TripID, Option<TripMode>, Duration)>,
// TODO This subsumes finished_trips
pub trip_log: Vec<(Time, TripID, Option<PathRequest>, String)>,
}
#[derive(Serialize, Deserialize, Derivative)]
@ -45,6 +47,7 @@ impl Analytics {
bus_arrivals: Vec::new(),
total_bus_passengers: Counter::new(),
finished_trips: Vec::new(),
trip_log: Vec::new(),
}
}
@ -104,6 +107,17 @@ impl Analytics {
} else if let Event::TripAborted(id) = ev {
self.finished_trips.push((time, id, None, Duration::ZERO));
}
// Trip log
if let Event::TripPhaseStarting(id, maybe_req, metadata) = ev {
self.trip_log.push((time, id, maybe_req, metadata));
} else if let Event::TripAborted(id) = ev {
self.trip_log
.push((time, id, None, format!("trip aborted for some reason")));
} else if let Event::TripFinished(id, _, dt) = ev {
self.trip_log
.push((time, id, None, format!("trip finished, total time {}", dt)));
}
}
// TODO If these ever need to be speeded up, just cache the histogram and index in the events
@ -266,4 +280,21 @@ impl Analytics {
}
per_mode
}
pub fn get_trip_log(&self, trip: TripID) -> Vec<String> {
self.trip_log
.iter()
.filter_map(|(t, id, maybe_req, md)| {
if *id == trip {
if let Some(req) = maybe_req {
Some(format!("At {}: {} via {}", t, md, req))
} else {
Some(format!("At {}: {}", t, md))
}
} else {
None
}
})
.collect()
}
}

View File

@ -1,6 +1,8 @@
use crate::{AgentID, CarID, ParkingSpot, PedestrianID, TripID, TripMode};
use geom::Duration;
use map_model::{BuildingID, BusRouteID, BusStopID, IntersectionID, LaneID, Traversable};
use map_model::{
BuildingID, BusRouteID, BusStopID, IntersectionID, LaneID, PathRequest, Traversable,
};
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
@ -24,4 +26,5 @@ pub enum Event {
TripFinished(TripID, TripMode, Duration),
TripAborted(TripID),
TripPhaseStarting(TripID, Option<PathRequest>, String),
}

View File

@ -196,6 +196,7 @@ impl TripSpawner {
(tuple, req.clone(), map.pathfind(req))
},
);
timer.start_iter("spawn trips", paths.len());
for ((start_time, ped_id, car_id, spec), req, maybe_path) in paths {
timer.next();
@ -231,6 +232,7 @@ impl TripSpawner {
start_time,
Command::SpawnCar(
CreateCar::for_appearing(vehicle, start_pos, router, trip),
req,
retry_if_no_room,
),
);
@ -276,14 +278,17 @@ impl TripSpawner {
if let Some(path) = maybe_path {
scheduler.quick_push(
start_time,
Command::SpawnPed(CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: parking_spot,
path,
trip,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: parking_spot,
path,
trip,
},
req,
),
);
} else {
timer.warn(format!(
@ -306,15 +311,18 @@ impl TripSpawner {
scheduler.quick_push(
start_time,
Command::SpawnPed(CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start: SidewalkSpot::building(start_bldg, map),
goal: walk_to,
// This is guaranteed to work, and is junk anyway.
path: maybe_path.unwrap(),
trip,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start: SidewalkSpot::building(start_bldg, map),
goal: walk_to,
// This is guaranteed to work, and is junk anyway.
path: maybe_path.unwrap(),
trip,
},
req,
),
);
}
TripSpec::JustWalking {
@ -338,14 +346,17 @@ impl TripSpawner {
if let Some(path) = maybe_path {
scheduler.quick_push(
start_time,
Command::SpawnPed(CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal,
path,
trip,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal,
path,
trip,
},
req,
),
);
} else {
timer.warn(format!(
@ -393,14 +404,17 @@ impl TripSpawner {
if let Some(path) = maybe_path {
scheduler.quick_push(
start_time,
Command::SpawnPed(CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: walk_to,
path,
trip,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: walk_to,
path,
trip,
},
req,
),
);
} else {
timer.warn(format!(
@ -439,14 +453,17 @@ impl TripSpawner {
if let Some(path) = maybe_path {
scheduler.quick_push(
start_time,
Command::SpawnPed(CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: walk_to,
path,
trip,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped_id.unwrap(),
speed: ped_speed,
start,
goal: walk_to,
path,
trip,
},
req,
),
);
} else {
timer.warn(format!(

View File

@ -1,7 +1,7 @@
use crate::{AgentID, CarID, CreateCar, CreatePedestrian, PedestrianID};
use derivative::Derivative;
use geom::{Duration, DurationHistogram, Time};
use map_model::IntersectionID;
use map_model::{IntersectionID, PathRequest};
use serde_derive::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::{BTreeMap, BinaryHeap};
@ -9,8 +9,8 @@ use std::collections::{BTreeMap, BinaryHeap};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub enum Command {
// If true, retry when there's no room to spawn somewhere
SpawnCar(CreateCar, bool),
SpawnPed(CreatePedestrian),
SpawnCar(CreateCar, PathRequest, bool),
SpawnPed(CreatePedestrian, PathRequest),
UpdateCar(CarID),
// Distinguish this from UpdateCar to avoid confusing things
UpdateLaggyHead(CarID),
@ -29,8 +29,8 @@ impl Command {
pub fn to_type(&self) -> CommandType {
match self {
Command::SpawnCar(ref create, _) => CommandType::Car(create.vehicle.id),
Command::SpawnPed(ref create) => CommandType::Ped(create.id),
Command::SpawnCar(ref create, _, _) => CommandType::Car(create.vehicle.id),
Command::SpawnPed(ref create, _) => CommandType::Ped(create.id),
Command::UpdateCar(id) => CommandType::Car(*id),
Command::UpdateLaggyHead(id) => CommandType::CarLaggyHead(*id),
Command::UpdatePed(id) => CommandType::Ped(*id),

View File

@ -371,8 +371,9 @@ impl Sim {
}
self.time = time;
let mut events = Vec::new();
match cmd {
Command::SpawnCar(create_car, retry_if_no_room) => {
Command::SpawnCar(create_car, req, retry_if_no_room) => {
if self.driving.start_car_on_lane(
self.time,
create_car.clone(),
@ -388,10 +389,16 @@ impl Sim {
if let Some(parked_car) = create_car.maybe_parked_car {
self.parking.remove_parked_car(parked_car);
}
events.push(Event::TripPhaseStarting(
create_car.trip,
Some(req),
format!("{}", create_car.vehicle.id),
));
} else if retry_if_no_room {
// TODO Record this in the trip log
self.scheduler.push(
self.time + BLIND_RETRY_TO_SPAWN,
Command::SpawnCar(create_car, retry_if_no_room),
Command::SpawnCar(create_car, req, retry_if_no_room),
);
} else {
println!(
@ -401,18 +408,19 @@ impl Sim {
self.trips.abort_trip_failed_start(create_car.trip);
}
}
Command::SpawnPed(mut create_ped) => {
Command::SpawnPed(mut create_ped, mut req) => {
let ok = if let SidewalkPOI::DeferredParkingSpot(b, driving_goal) =
create_ped.goal.connection.clone()
{
if let Some(parked_car) = self.parking.dynamically_reserve_car(b) {
create_ped.goal =
SidewalkSpot::parking_spot(parked_car.spot, map, &self.parking);
if let Some(path) = map.pathfind(PathRequest {
req = PathRequest {
start: create_ped.start.sidewalk_pos,
end: create_ped.goal.sidewalk_pos,
constraints: PathConstraints::Pedestrian,
}) {
};
if let Some(path) = map.pathfind(req.clone()) {
create_ped.path = path;
let mut legs = vec![
TripLeg::Walk(
@ -462,6 +470,16 @@ impl Sim {
AgentID::Pedestrian(create_ped.id),
create_ped.trip,
);
events.push(Event::TripPhaseStarting(
create_ped.trip,
Some(req),
format!(
"{} from {:?} to {:?}",
create_ped.id,
create_ped.start.connection,
create_ped.goal.connection
),
));
// Maybe there's actually no work to do!
match (&create_ped.start.connection, &create_ped.goal.connection) {
@ -538,7 +556,6 @@ impl Sim {
}
// Record events at precisely the time they occur.
let mut events = Vec::new();
events.extend(self.trips.collect_events());
events.extend(self.transit.collect_events());
events.extend(self.driving.collect_events());

View File

@ -192,11 +192,12 @@ impl TripManager {
start = Position::new(start.lane(), start.dist_along() + parked_car.vehicle.length);
}
let end = drive_to.goal_pos(PathConstraints::Car, map);
let path = if let Some(p) = map.pathfind(PathRequest {
let req = PathRequest {
start,
end,
constraints: PathConstraints::Car,
}) {
};
let path = if let Some(p) = map.pathfind(req.clone()) {
p
} else {
println!(
@ -214,6 +215,7 @@ impl TripManager {
now,
Command::SpawnCar(
CreateCar::for_parked_car(parked_car.clone(), router, start.dist_along(), trip.id),
req,
true,
),
);
@ -244,11 +246,12 @@ impl TripManager {
};
let end = drive_to.goal_pos(PathConstraints::Bike, map);
let path = if let Some(p) = map.pathfind(PathRequest {
let req = PathRequest {
start: driving_pos,
end,
constraints: PathConstraints::Bike,
}) {
};
let path = if let Some(p) = map.pathfind(req.clone()) {
p
} else {
println!(
@ -266,6 +269,7 @@ impl TripManager {
now,
Command::SpawnCar(
CreateCar::for_appearing(vehicle, driving_pos, router, trip.id),
req,
true,
),
);
@ -339,6 +343,11 @@ impl TripManager {
}
match trip.legs[1] {
TripLeg::RideBus(_, route, stop2) => {
self.events.push(Event::TripPhaseStarting(
trip.id,
None,
format!("{} waiting at {:?} for {}", ped, stop, route),
));
if transit.ped_waiting_for_bus(ped, stop, route, stop2) {
trip.legs.pop_front();
None
@ -623,11 +632,12 @@ impl Trip {
_ => unreachable!(),
};
let path = if let Some(p) = map.pathfind(PathRequest {
let req = PathRequest {
start: start.sidewalk_pos,
end: walk_to.sidewalk_pos,
constraints: PathConstraints::Pedestrian,
}) {
};
let path = if let Some(p) = map.pathfind(req.clone()) {
p
} else {
println!(
@ -639,14 +649,17 @@ impl Trip {
scheduler.push(
now,
Command::SpawnPed(CreatePedestrian {
id: ped,
speed,
start,
goal: walk_to,
path,
trip: self.id,
}),
Command::SpawnPed(
CreatePedestrian {
id: ped,
speed,
start,
goal: walk_to,
path,
trip: self.id,
},
req,
),
);
true
}