mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
start tracking and debugging phases of a trip
This commit is contained in:
parent
1648b56009
commit
8d34759b7c
@ -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());
|
||||
|
@ -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))]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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!(
|
||||
|
@ -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),
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user