sim/
recorder.rs

1use std::collections::BTreeSet;
2
3use geom::Time;
4use map_model::{IntersectionID, LaneID, Map, PathStep, Position, Traversable};
5use synthpop::{IndividTrip, PersonSpec, Scenario, TripEndpoint, TripMode, TripPurpose};
6
7use crate::{AgentID, CarID, DrivingSimState, Event, TripID, VehicleType};
8
9/// Records trips beginning and ending at a specified set of intersections. This can be used to
10/// capture and reproduce behavior in a gridlock-prone chunk of the map, without simulating
11/// everything.
12#[derive(Clone)]
13pub(crate) struct TrafficRecorder {
14    capture_points: BTreeSet<IntersectionID>,
15    // TODO The RNG will determine vehicle length, so this won't be a perfect capture. Hopefully
16    // good enough.
17    trips: Vec<IndividTrip>,
18    seen_trips: BTreeSet<TripID>,
19}
20
21impl TrafficRecorder {
22    pub fn new(capture_points: BTreeSet<IntersectionID>) -> TrafficRecorder {
23        TrafficRecorder {
24            capture_points,
25            trips: Vec::new(),
26            seen_trips: BTreeSet::new(),
27        }
28    }
29
30    pub fn handle_event(&mut self, time: Time, ev: &Event, map: &Map, driving: &DrivingSimState) {
31        if let Event::AgentEntersTraversable(AgentID::Car(car), Some(trip), on, _) = ev {
32            self.on_car_enters_traversable(time, *car, *trip, *on, map, driving);
33        }
34    }
35
36    fn on_car_enters_traversable(
37        &mut self,
38        time: Time,
39        car: CarID,
40        trip: TripID,
41        on: Traversable,
42        map: &Map,
43        driving: &DrivingSimState,
44    ) {
45        if self.seen_trips.contains(&trip) {
46            return;
47        }
48        if let Traversable::Lane(lane) = on {
49            self.on_car_enters_lane(time, car, trip, lane, map, driving);
50        }
51    }
52
53    fn on_car_enters_lane(
54        &mut self,
55        time: Time,
56        car: CarID,
57        trip: TripID,
58        lane: LaneID,
59        map: &Map,
60        driving: &DrivingSimState,
61    ) {
62        if !self.capture_points.contains(&map.get_l(lane).src_i) {
63            return;
64        }
65        // Where do they exit?
66        let exit_intersection =
67            driving
68                .get_path(car)
69                .unwrap()
70                .get_steps()
71                .iter()
72                .find_map(|step| {
73                    if let PathStep::Turn(t) = step {
74                        if self.capture_points.contains(&t.parent) {
75                            return Some(t.parent);
76                        }
77                    }
78                    None
79                });
80        if let Some(exit_intersection) = exit_intersection {
81            self.trips.push(IndividTrip::new(
82                time,
83                TripPurpose::Shopping,
84                TripEndpoint::SuddenlyAppear(Position::start(lane)),
85                TripEndpoint::Border(exit_intersection),
86                if car.vehicle_type == VehicleType::Bike {
87                    TripMode::Bike
88                } else {
89                    TripMode::Drive
90                },
91            ));
92            self.seen_trips.insert(trip);
93        };
94    }
95
96    pub fn num_recorded_trips(&self) -> usize {
97        self.trips.len()
98    }
99
100    pub fn save(mut self, map: &Map) {
101        Scenario {
102            scenario_name: "recorded".to_string(),
103            map_name: map.get_name().clone(),
104            people: self
105                .trips
106                .drain(..)
107                .map(|trip| PersonSpec {
108                    orig_id: None,
109                    trips: vec![trip],
110                })
111                .collect::<Vec<_>>(),
112            only_seed_buses: None,
113        }
114        .save();
115    }
116}