sim/
trips.rs

1use std::collections::{BTreeMap, VecDeque};
2
3use serde::{Deserialize, Serialize};
4
5use abstutil::{deserialize_btreemap, serialize_btreemap, Counter};
6use geom::{Distance, Duration, Speed, Time};
7use map_model::{
8    BuildingID, IntersectionID, Map, PathConstraints, PathRequest, Position, TransitRouteID,
9    TransitStopID,
10};
11use synthpop::{
12    IndividTrip, OrigPersonID, PersonSpec, Scenario, TripEndpoint, TripMode, TripPurpose,
13};
14
15use crate::sim::Ctx;
16use crate::{
17    AgentID, AgentType, AlertLocation, CarID, Command, CreateCar, CreatePedestrian, DrivingGoal,
18    Event, ParkedCar, ParkingSim, ParkingSpot, PedestrianID, PersonID, SidewalkPOI, SidewalkSpot,
19    StartTripArgs, TransitSimState, TripID, TripPhaseType, TripSpec, Vehicle, VehicleSpec,
20    VehicleType, WalkingSimState,
21};
22
23/// Manages people, each of which executes some trips through the day. Each trip is further broken
24/// down into legs -- for example, a driving trip might start with somebody walking to their car,
25/// driving somewhere, parking, and then walking to their final destination.
26/// https://a-b-street.github.io/tech/docs/trafficsim/trips.html describes some of the variations.
27//
28// Here be dragons, keep hands and feet inside the ride at all times...
29#[derive(Serialize, Deserialize, Debug, Clone)]
30pub(crate) struct TripManager {
31    trips: Vec<Trip>,
32    people: Vec<Person>,
33    // For quick lookup of active agents
34    #[serde(
35        serialize_with = "serialize_btreemap",
36        deserialize_with = "deserialize_btreemap"
37    )]
38    active_trip_mode: BTreeMap<AgentID, TripID>,
39    unfinished_trips: usize,
40
41    car_id_counter: usize,
42
43    events: Vec<Event>,
44}
45
46// Initialization
47impl TripManager {
48    pub fn new() -> TripManager {
49        TripManager {
50            trips: Vec::new(),
51            people: Vec::new(),
52            active_trip_mode: BTreeMap::new(),
53            unfinished_trips: 0,
54            car_id_counter: 0,
55            events: Vec::new(),
56        }
57    }
58
59    // TODO assert the specs are correct yo
60    pub fn new_person(
61        &mut self,
62        orig_id: Option<OrigPersonID>,
63        ped_speed: Speed,
64        vehicle_specs: Vec<VehicleSpec>,
65    ) -> &Person {
66        let id = PersonID(self.people.len());
67        let vehicles = vehicle_specs
68            .into_iter()
69            .map(|v| {
70                let c = CarID {
71                    id: self.new_car_id(),
72                    vehicle_type: v.vehicle_type,
73                };
74                v.make(c, Some(id))
75            })
76            .collect();
77        self.people.push(Person {
78            id,
79            orig_id,
80            trips: Vec::new(),
81            // The first new_trip will set this properly.
82            state: PersonState::OffMap,
83            ped: PedestrianID(id.0),
84            ped_speed,
85            vehicles,
86            delayed_trips: Vec::new(),
87            on_bus: None,
88        });
89        self.get_person(id).unwrap()
90    }
91
92    pub fn new_car_id(&mut self) -> usize {
93        let id = self.car_id_counter;
94        self.car_id_counter += 1;
95        id
96    }
97
98    pub fn new_trip(&mut self, person: PersonID, info: TripInfo) -> TripID {
99        let id = TripID(self.trips.len());
100        let trip = Trip {
101            id,
102            info,
103            person,
104            started: false,
105            finished_at: None,
106            total_blocked_time: Duration::ZERO,
107            total_distance: Distance::ZERO,
108            legs: VecDeque::new(),
109        };
110        self.unfinished_trips += 1;
111        let person = &mut self.people[trip.person.0];
112        if person.trips.is_empty() {
113            person.state = match trip.info.start {
114                TripEndpoint::Building(b) => {
115                    self.events
116                        .push(Event::PersonEntersBuilding(trip.person, b));
117                    PersonState::Inside(b)
118                }
119                TripEndpoint::Border(_) | TripEndpoint::SuddenlyAppear(_) => PersonState::OffMap,
120            };
121        }
122        if let Some(t) = person.trips.last() {
123            // TODO If it's exactly ==, what?! See the ID.
124            if self.trips[t.0].info.departure > trip.info.departure {
125                panic!(
126                    "{} has a trip starting at {}, then one at {}",
127                    person.id, self.trips[t.0].info.departure, trip.info.departure
128                );
129            }
130        }
131        person.trips.push(id);
132        self.trips.push(trip);
133        id
134    }
135
136    pub fn start_trip(&mut self, now: Time, trip: TripID, args: StartTripArgs, ctx: &mut Ctx) {
137        assert!(self.trips[trip.0].info.cancellation_reason.is_none());
138
139        let person = &mut self.people[self.trips[trip.0].person.0];
140        if let PersonState::Trip(_) = person.state {
141            // Previous trip isn't done. Defer this one!
142            if false {
143                self.events.push(Event::Alert(
144                    AlertLocation::Person(person.id),
145                    format!(
146                        "{} is still doing a trip, so not starting {} yet",
147                        person.id, trip
148                    ),
149                ));
150            }
151            person.delayed_trips.push((trip, args));
152            self.events.push(Event::TripPhaseStarting(
153                trip,
154                person.id,
155                None,
156                TripPhaseType::DelayedStart,
157            ));
158            return;
159        }
160        self.trips[trip.0].started = true;
161
162        let info = &self.trips[trip.0].info;
163        let spec = match TripSpec::maybe_new(
164            info.start,
165            info.end,
166            info.mode,
167            args.use_vehicle,
168            args.retry_if_no_room,
169            ctx.map,
170        ) {
171            Ok(spec) => spec,
172            Err(error) => TripSpec::SpawningFailure {
173                use_vehicle: args.use_vehicle,
174                error: error.to_string(),
175            },
176        };
177        // to_plan might actually change the TripSpec
178        let (spec, legs) = spec.into_plan(ctx.map);
179        assert!(self.trips[trip.0].legs.is_empty());
180        self.trips[trip.0].legs.extend(legs);
181
182        match spec {
183            TripSpec::VehicleAppearing {
184                start_pos,
185                goal,
186                retry_if_no_room,
187                use_vehicle,
188            } => {
189                assert_eq!(person.state, PersonState::OffMap);
190                self.events.push(Event::PersonEntersMap(
191                    person.id,
192                    AgentID::Car(use_vehicle),
193                    ctx.map.get_l(start_pos.lane()).src_i,
194                ));
195                person.state = PersonState::Trip(trip);
196
197                let vehicle = person.get_vehicle(use_vehicle);
198                assert!(ctx.parking.lookup_parked_car(vehicle.id).is_none());
199                let constraints = if use_vehicle.vehicle_type == VehicleType::Bike {
200                    PathConstraints::Bike
201                } else {
202                    PathConstraints::Car
203                };
204                let req = PathRequest::vehicle(
205                    start_pos,
206                    goal.goal_pos(constraints, ctx.map).unwrap(),
207                    constraints,
208                );
209                let person = person.id;
210
211                match ctx.map.pathfind(req) {
212                    Ok(path) => {
213                        let router = goal.make_router(vehicle.id, path, ctx.map);
214                        ctx.scheduler.push(
215                            now,
216                            Command::SpawnCar(
217                                CreateCar::for_appearing(vehicle, router, trip, person),
218                                retry_if_no_room,
219                            ),
220                        );
221                    }
222                    Err(err) => {
223                        self.cancel_trip(now, trip, err.to_string(), Some(vehicle), ctx);
224                    }
225                }
226            }
227            TripSpec::SpawningFailure {
228                use_vehicle, error, ..
229            } => {
230                let vehicle = use_vehicle.map(|v| person.get_vehicle(v));
231                self.cancel_trip(now, trip, error, vehicle, ctx);
232            }
233            TripSpec::UsingParkedCar {
234                car, start_bldg, ..
235            } => {
236                assert_eq!(person.state, PersonState::Inside(start_bldg));
237                person.state = PersonState::Trip(trip);
238
239                if let Some(parked_car) = ctx.parking.lookup_parked_car(car).cloned() {
240                    let start = SidewalkSpot::building(start_bldg, ctx.map);
241                    let walking_goal =
242                        SidewalkSpot::parking_spot(parked_car.spot, ctx.map, ctx.parking);
243                    let req = PathRequest::walking(start.sidewalk_pos, walking_goal.sidewalk_pos);
244                    match ctx.map.pathfind(req) {
245                        Ok(path) => {
246                            ctx.scheduler.push(
247                                now,
248                                Command::SpawnPed(CreatePedestrian {
249                                    id: person.ped,
250                                    speed: person.ped_speed,
251                                    start,
252                                    goal: walking_goal,
253                                    path,
254                                    trip,
255                                    person: person.id,
256                                }),
257                            );
258                        }
259                        Err(err) => {
260                            // Move the car to the destination
261                            ctx.parking.remove_parked_car(parked_car.clone());
262                            self.cancel_trip(
263                                now,
264                                trip,
265                                err.to_string(),
266                                Some(parked_car.vehicle),
267                                ctx,
268                            );
269                        }
270                    }
271                } else {
272                    // This should only happen when a driving trip has been cancelled and there was
273                    // absolutely no room to warp the car.
274                    self.cancel_trip(
275                        now,
276                        trip,
277                        format!("should have {} parked somewhere, but it's unavailable", car),
278                        None,
279                        ctx,
280                    );
281                }
282            }
283            TripSpec::JustWalking { start, goal } => {
284                assert_eq!(
285                    person.state,
286                    match start.connection {
287                        SidewalkPOI::Building(b) => PersonState::Inside(b),
288                        SidewalkPOI::Border(i) => {
289                            self.events.push(Event::PersonEntersMap(
290                                person.id,
291                                AgentID::Pedestrian(person.ped),
292                                i,
293                            ));
294                            PersonState::OffMap
295                        }
296                        SidewalkPOI::SuddenlyAppear => {
297                            // Unclear which end of the sidewalk this person should be associated
298                            // with. For interactively spawned people, doesn't really matter.
299                            self.events.push(Event::PersonEntersMap(
300                                person.id,
301                                AgentID::Pedestrian(person.ped),
302                                ctx.map.get_l(start.sidewalk_pos.lane()).src_i,
303                            ));
304                            PersonState::OffMap
305                        }
306                        _ => unreachable!(),
307                    }
308                );
309                person.state = PersonState::Trip(trip);
310
311                let req = PathRequest::walking(start.sidewalk_pos, goal.sidewalk_pos);
312                match ctx.map.pathfind(req) {
313                    Ok(path) => {
314                        ctx.scheduler.push(
315                            now,
316                            Command::SpawnPed(CreatePedestrian {
317                                id: person.ped,
318                                speed: person.ped_speed,
319                                start,
320                                goal,
321                                path,
322                                trip,
323                                person: person.id,
324                            }),
325                        );
326                    }
327                    Err(err) => {
328                        self.cancel_trip(now, trip, err.to_string(), None, ctx);
329                    }
330                }
331            }
332            TripSpec::UsingBike { start, .. } => {
333                assert_eq!(person.state, PersonState::Inside(start));
334                person.state = PersonState::Trip(trip);
335
336                if let Some(walk_to) = SidewalkSpot::bike_rack(start, ctx.map) {
337                    let req = PathRequest::walking(
338                        SidewalkSpot::building(start, ctx.map).sidewalk_pos,
339                        walk_to.sidewalk_pos,
340                    );
341                    match ctx.map.pathfind(req) {
342                        Ok(path) => {
343                            // Where we start biking may have slightly changed due to live map
344                            // edits!
345                            match self.trips[trip.0].legs.front_mut() {
346                                Some(TripLeg::Walk(ref mut spot)) => {
347                                    if spot.clone() != walk_to {
348                                        // We could assert both have a BikeRack connection, but eh
349                                        *spot = walk_to.clone();
350                                    }
351                                }
352                                _ => unreachable!(),
353                            }
354
355                            ctx.scheduler.push(
356                                now,
357                                Command::SpawnPed(CreatePedestrian {
358                                    id: person.ped,
359                                    speed: person.ped_speed,
360                                    start: SidewalkSpot::building(start, ctx.map),
361                                    goal: walk_to,
362                                    path,
363                                    trip,
364                                    person: person.id,
365                                }),
366                            );
367                        }
368                        Err(err) => {
369                            self.cancel_trip(now, trip, err.to_string(), None, ctx);
370                        }
371                    }
372                } else {
373                    self.cancel_trip(
374                        now,
375                        trip,
376                        format!(
377                            "UsingBike trip couldn't find a way to start biking from {}",
378                            start
379                        ),
380                        None,
381                        ctx,
382                    );
383                }
384            }
385            TripSpec::UsingTransit { start, stop1, .. } => {
386                assert_eq!(
387                    person.state,
388                    match start.connection {
389                        SidewalkPOI::Building(b) => PersonState::Inside(b),
390                        SidewalkPOI::Border(i) => {
391                            self.events.push(Event::PersonEntersMap(
392                                person.id,
393                                AgentID::Pedestrian(person.ped),
394                                i,
395                            ));
396                            PersonState::OffMap
397                        }
398                        SidewalkPOI::SuddenlyAppear => {
399                            // Unclear which end of the sidewalk this person should be associated
400                            // with. For interactively spawned people, doesn't really matter.
401                            self.events.push(Event::PersonEntersMap(
402                                person.id,
403                                AgentID::Pedestrian(person.ped),
404                                ctx.map.get_l(start.sidewalk_pos.lane()).src_i,
405                            ));
406                            PersonState::OffMap
407                        }
408                        _ => unreachable!(),
409                    }
410                );
411                person.state = PersonState::Trip(trip);
412
413                let walk_to = SidewalkSpot::bus_stop(stop1, ctx.map);
414                let req = PathRequest::walking(start.sidewalk_pos, walk_to.sidewalk_pos);
415                match ctx.map.pathfind(req) {
416                    Ok(path) => {
417                        ctx.scheduler.push(
418                            now,
419                            Command::SpawnPed(CreatePedestrian {
420                                id: person.ped,
421                                speed: person.ped_speed,
422                                start,
423                                goal: walk_to,
424                                path,
425                                trip,
426                                person: person.id,
427                            }),
428                        );
429                    }
430                    Err(err) => {
431                        self.cancel_trip(now, trip, err.to_string(), None, ctx);
432                    }
433                }
434            }
435        }
436    }
437
438    pub fn collect_events(&mut self) -> Vec<Event> {
439        std::mem::take(&mut self.events)
440    }
441}
442
443// Transitions between different legs of a trip
444impl TripManager {
445    /// This is idempotent to handle the case of cars retrying their spawning.
446    pub fn agent_starting_trip_leg(&mut self, agent: AgentID, t: TripID) {
447        if let Some(other) = self.active_trip_mode.get(&agent) {
448            if *other != t {
449                panic!("{} is doing both {} and {}?", agent, t, other);
450            }
451        }
452        self.active_trip_mode.insert(agent, t);
453    }
454
455    pub fn car_reached_parking_spot(
456        &mut self,
457        now: Time,
458        car: CarID,
459        spot: ParkingSpot,
460        blocked_time: Duration,
461        distance_crossed: Distance,
462        ctx: &mut Ctx,
463    ) {
464        let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
465        trip.total_blocked_time += blocked_time;
466        trip.total_distance += distance_crossed;
467
468        match trip.legs.pop_front() {
469            Some(TripLeg::Drive(c, DrivingGoal::ParkNear(_))) => {
470                assert_eq!(car, c);
471            }
472            _ => unreachable!(),
473        };
474
475        match &trip.legs[0] {
476            TripLeg::Walk(to) => match (spot, &to.connection) {
477                (ParkingSpot::Offstreet(b1, _), SidewalkPOI::Building(b2)) if b1 == *b2 => {
478                    assert_eq!(trip.legs.len(), 1);
479                    trip.legs.pop_front().unwrap();
480
481                    self.people[trip.person.0].state = PersonState::Inside(b1);
482                    self.events
483                        .push(Event::PersonEntersBuilding(trip.person, b1));
484                    let id = trip.id;
485                    self.trip_finished(now, id, ctx);
486                    return;
487                }
488                _ => {}
489            },
490            _ => unreachable!(),
491        };
492
493        let id = trip.id;
494        self.spawn_ped(
495            now,
496            id,
497            SidewalkSpot::parking_spot(spot, ctx.map, ctx.parking),
498            ctx,
499        );
500    }
501
502    pub fn ped_reached_parking_spot(
503        &mut self,
504        now: Time,
505        ped: PedestrianID,
506        spot: ParkingSpot,
507        blocked_time: Duration,
508        distance_crossed: Distance,
509        ctx: &mut Ctx,
510    ) {
511        self.events.push(Event::PedReachedParkingSpot(ped, spot));
512        let trip = &mut self.trips[self
513            .active_trip_mode
514            .remove(&AgentID::Pedestrian(ped))
515            .unwrap()
516            .0];
517        trip.total_blocked_time += blocked_time;
518        trip.total_distance += distance_crossed;
519
520        trip.assert_walking_leg(SidewalkSpot::deferred_parking_spot());
521        let parked_car = ctx.parking.get_car_at_spot(spot).unwrap().clone();
522        let drive_to = match trip.legs[0] {
523            TripLeg::Drive(c, ref to) => {
524                assert_eq!(c, parked_car.vehicle.id);
525                to.clone()
526            }
527            _ => unreachable!(),
528        };
529
530        let base_start =
531            ctx.parking
532                .spot_to_driving_pos(parked_car.spot, &parked_car.vehicle, ctx.map);
533        let end = drive_to.goal_pos(PathConstraints::Car, ctx.map).unwrap();
534        let req = match spot {
535            ParkingSpot::Onstreet(_, _) => {
536                PathRequest::vehicle(base_start, end, PathConstraints::Car)
537            }
538            ParkingSpot::Offstreet(b, _) => {
539                self.events
540                    .push(Event::PersonEntersBuilding(trip.person, b));
541                PathRequest::leave_from_driveway(base_start, end, PathConstraints::Car, ctx.map)
542            }
543            ParkingSpot::Lot(_, _) => {
544                PathRequest::leave_from_driveway(base_start, end, PathConstraints::Car, ctx.map)
545            }
546        };
547
548        let person = trip.person;
549        let trip = trip.id;
550        match ctx.map.pathfind(req) {
551            Ok(path) => {
552                let router = drive_to.make_router(parked_car.vehicle.id, path, ctx.map);
553                ctx.scheduler.push(
554                    now,
555                    Command::SpawnCar(
556                        CreateCar::for_parked_car(parked_car, router, trip, person),
557                        true,
558                    ),
559                );
560            }
561            Err(err) => {
562                // Move the car to the destination...
563                ctx.parking.remove_parked_car(parked_car.clone());
564                self.cancel_trip(now, trip, err.to_string(), Some(parked_car.vehicle), ctx);
565            }
566        }
567    }
568
569    pub fn ped_ready_to_bike(
570        &mut self,
571        now: Time,
572        ped: PedestrianID,
573        spot: SidewalkSpot,
574        blocked_time: Duration,
575        distance_crossed: Distance,
576        ctx: &mut Ctx,
577    ) {
578        let trip = &mut self.trips[self
579            .active_trip_mode
580            .remove(&AgentID::Pedestrian(ped))
581            .unwrap()
582            .0];
583        trip.total_blocked_time += blocked_time;
584        trip.total_distance += distance_crossed;
585
586        trip.assert_walking_leg(spot.clone());
587        let (bike, drive_to) = match trip.legs[0] {
588            TripLeg::Drive(bike, ref to) => (bike, to.clone()),
589            _ => unreachable!(),
590        };
591        let driving_pos = match spot.connection {
592            SidewalkPOI::BikeRack(p) => p,
593            _ => unreachable!(),
594        };
595
596        let end = if let Some(end) = drive_to.goal_pos(PathConstraints::Bike, ctx.map) {
597            end
598        } else {
599            let trip = trip.id;
600            self.cancel_trip(
601                now,
602                trip,
603                format!("no bike connection at {:?}", drive_to),
604                None,
605                ctx,
606            );
607            return;
608        };
609        let req = PathRequest::vehicle(driving_pos, end, PathConstraints::Bike);
610        let maybe_router = if req.start.lane() == req.end.lane() {
611            // TODO Convert to a walking trip! Ideally, do this earlier and convert the trip to
612            // walking, like schedule_trip does
613            Err(anyhow!(
614                "biking to a different part of {} is silly, why not walk?",
615                req.start.lane()
616            ))
617        } else {
618            ctx.map
619                .pathfind(req)
620                .map(|path| drive_to.make_router(bike, path, ctx.map))
621        };
622        match maybe_router {
623            Ok(router) => {
624                ctx.scheduler.push(
625                    now,
626                    Command::SpawnCar(
627                        CreateCar::for_appearing(
628                            self.people[trip.person.0].get_vehicle(bike),
629                            router,
630                            trip.id,
631                            trip.person,
632                        ),
633                        true,
634                    ),
635                );
636            }
637            Err(err) => {
638                let trip = trip.id;
639                self.cancel_trip(now, trip, err.to_string(), None, ctx);
640            }
641        }
642    }
643
644    pub fn bike_reached_end(
645        &mut self,
646        now: Time,
647        bike: CarID,
648        bike_rack: SidewalkSpot,
649        blocked_time: Duration,
650        distance_crossed: Distance,
651        ctx: &mut Ctx,
652    ) {
653        self.events.push(Event::BikeStoppedAtSidewalk(
654            bike,
655            bike_rack.sidewalk_pos.lane(),
656        ));
657        let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(bike)).unwrap().0];
658        trip.total_blocked_time += blocked_time;
659        trip.total_distance += distance_crossed;
660
661        match trip.legs.pop_front() {
662            Some(TripLeg::Drive(c, DrivingGoal::ParkNear(_))) => {
663                assert_eq!(c, bike);
664            }
665            _ => unreachable!(),
666        };
667
668        let id = trip.id;
669        self.spawn_ped(now, id, bike_rack, ctx);
670    }
671
672    pub fn ped_reached_building(
673        &mut self,
674        now: Time,
675        ped: PedestrianID,
676        bldg: BuildingID,
677        blocked_time: Duration,
678        distance_crossed: Distance,
679        ctx: &mut Ctx,
680    ) {
681        let trip = &mut self.trips[self
682            .active_trip_mode
683            .remove(&AgentID::Pedestrian(ped))
684            .unwrap()
685            .0];
686        trip.total_blocked_time += blocked_time;
687        trip.total_distance += distance_crossed;
688
689        trip.assert_walking_leg(SidewalkSpot::building(bldg, ctx.map));
690
691        self.people[trip.person.0].state = PersonState::Inside(bldg);
692        self.events
693            .push(Event::PersonEntersBuilding(trip.person, bldg));
694
695        let id = trip.id;
696        self.trip_finished(now, id, ctx);
697    }
698
699    /// If no route is returned, the pedestrian boarded a bus immediately.
700    pub fn ped_reached_bus_stop(
701        &mut self,
702        now: Time,
703        ped: PedestrianID,
704        stop: TransitStopID,
705        blocked_time: Duration,
706        distance_crossed: Distance,
707        ctx: &mut Ctx,
708        transit: &mut TransitSimState,
709    ) -> Option<TransitRouteID> {
710        let trip = &mut self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0];
711        trip.total_blocked_time += blocked_time;
712        trip.total_distance += distance_crossed;
713
714        match trip.legs[0] {
715            TripLeg::Walk(ref spot) => {
716                assert_eq!(*spot, SidewalkSpot::bus_stop(stop, ctx.map));
717            }
718            _ => unreachable!(),
719        }
720        match trip.legs[1] {
721            TripLeg::RideBus(route, maybe_stop2) => {
722                self.events.push(Event::TripPhaseStarting(
723                    trip.id,
724                    trip.person,
725                    None,
726                    TripPhaseType::WaitingForBus(route, stop),
727                ));
728                if let Some(bus) = transit.ped_waiting_for_bus(
729                    now,
730                    ped,
731                    trip.id,
732                    trip.person,
733                    stop,
734                    route,
735                    maybe_stop2,
736                    ctx.map,
737                ) {
738                    trip.legs.pop_front();
739                    self.active_trip_mode
740                        .remove(&AgentID::Pedestrian(ped))
741                        .unwrap();
742                    self.active_trip_mode
743                        .insert(AgentID::BusPassenger(trip.person, bus), trip.id);
744                    self.people[trip.person.0].on_bus = Some(bus);
745                    None
746                } else {
747                    Some(route)
748                }
749            }
750            _ => unreachable!(),
751        }
752    }
753
754    pub fn ped_boarded_bus(
755        &mut self,
756        now: Time,
757        ped: PedestrianID,
758        bus: CarID,
759        blocked_time: Duration,
760        walking: &mut WalkingSimState,
761    ) -> (TripID, PersonID) {
762        let trip = &mut self.trips[self
763            .active_trip_mode
764            .remove(&AgentID::Pedestrian(ped))
765            .unwrap()
766            .0];
767        trip.total_blocked_time += blocked_time;
768        // No distance crossed between waiting for a bus and boarding
769
770        trip.legs.pop_front();
771        walking.ped_boarded_bus(now, ped);
772        self.active_trip_mode
773            .insert(AgentID::BusPassenger(trip.person, bus), trip.id);
774        self.people[trip.person.0].on_bus = Some(bus);
775        (trip.id, trip.person)
776    }
777
778    // TODO Need to characterize delay the bus experienced
779    pub fn person_left_bus(&mut self, now: Time, person: PersonID, bus: CarID, ctx: &mut Ctx) {
780        let trip = &mut self.trips[self
781            .active_trip_mode
782            .remove(&AgentID::BusPassenger(person, bus))
783            .unwrap()
784            .0];
785        let start = match trip.legs.pop_front().unwrap() {
786            TripLeg::RideBus(_, maybe_stop2) => SidewalkSpot::bus_stop(
787                maybe_stop2.expect("someone left a bus, even though they should've ridden off-map"),
788                ctx.map,
789            ),
790            _ => unreachable!(),
791        };
792        self.people[person.0].on_bus.take().unwrap();
793
794        let id = trip.id;
795        self.spawn_ped(now, id, start, ctx);
796    }
797
798    pub fn ped_reached_border(
799        &mut self,
800        now: Time,
801        ped: PedestrianID,
802        i: IntersectionID,
803        blocked_time: Duration,
804        distance_crossed: Distance,
805        ctx: &mut Ctx,
806    ) {
807        let trip = &mut self.trips[self
808            .active_trip_mode
809            .remove(&AgentID::Pedestrian(ped))
810            .unwrap()
811            .0];
812        trip.total_blocked_time += blocked_time;
813        trip.total_distance += distance_crossed;
814
815        match trip.legs.pop_front() {
816            Some(TripLeg::Walk(spot)) => match spot.connection {
817                SidewalkPOI::Border(i2) => assert_eq!(i, i2),
818                _ => unreachable!(),
819            },
820            _ => unreachable!(),
821        }
822
823        if let TripEndpoint::Border(_) = trip.info.end {
824            self.events.push(Event::PersonLeavesMap(
825                trip.person,
826                Some(AgentID::Pedestrian(ped)),
827                i,
828            ));
829        }
830        self.people[trip.person.0].state = PersonState::OffMap;
831
832        let id = trip.id;
833        self.trip_finished(now, id, ctx);
834    }
835
836    pub fn transit_rider_reached_border(
837        &mut self,
838        now: Time,
839        person: PersonID,
840        bus: CarID,
841        ctx: &mut Ctx,
842    ) {
843        let agent = AgentID::BusPassenger(person, bus);
844        let trip = &mut self.trips[self.active_trip_mode.remove(&agent).unwrap().0];
845
846        match trip.legs.pop_front() {
847            Some(TripLeg::RideBus(_, maybe_spot2)) => assert!(maybe_spot2.is_none()),
848            _ => unreachable!(),
849        }
850
851        if let TripEndpoint::Border(i) = trip.info.end {
852            self.events
853                .push(Event::PersonLeavesMap(trip.person, Some(agent), i));
854        } else {
855            unreachable!()
856        }
857        self.people[trip.person.0].state = PersonState::OffMap;
858
859        let id = trip.id;
860        self.trip_finished(now, id, ctx);
861    }
862
863    pub fn car_or_bike_reached_border(
864        &mut self,
865        now: Time,
866        car: CarID,
867        i: IntersectionID,
868        blocked_time: Duration,
869        distance_crossed: Distance,
870        ctx: &mut Ctx,
871    ) {
872        let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
873        trip.total_blocked_time += blocked_time;
874        trip.total_distance += distance_crossed;
875
876        match trip.legs.pop_front().unwrap() {
877            TripLeg::Drive(c, DrivingGoal::Border(int, _)) => {
878                assert_eq!(car, c);
879                assert_eq!(i, int);
880            }
881            _ => unreachable!(),
882        };
883
884        self.people[trip.person.0].state = PersonState::OffMap;
885        if let TripEndpoint::Border(_) = trip.info.end {
886            self.events.push(Event::PersonLeavesMap(
887                trip.person,
888                Some(AgentID::Car(car)),
889                i,
890            ));
891        }
892
893        let id = trip.id;
894        self.trip_finished(now, id, ctx);
895    }
896
897    fn trip_finished(&mut self, now: Time, id: TripID, ctx: &mut Ctx) {
898        let trip = &mut self.trips[id.0];
899        assert!(trip.legs.is_empty());
900        assert!(!trip.finished_at.is_some());
901        trip.finished_at = Some(now);
902        self.unfinished_trips -= 1;
903        self.events.push(Event::TripFinished {
904            trip: trip.id,
905            mode: trip.info.mode,
906            total_time: now - trip.info.departure,
907            blocked_time: trip.total_blocked_time,
908        });
909
910        let person = trip.person;
911        self.start_delayed_trip(now, person, ctx);
912    }
913
914    fn start_delayed_trip(&mut self, now: Time, id: PersonID, ctx: &mut Ctx) {
915        let person = &mut self.people[id.0];
916        if person.delayed_trips.is_empty() {
917            return;
918        }
919        let (trip, args) = person.delayed_trips.remove(0);
920        if false {
921            self.events.push(Event::Alert(
922                AlertLocation::Person(person.id),
923                format!(
924                    "{} just freed up, so starting delayed trip {}",
925                    person.id, trip
926                ),
927            ));
928        }
929        self.start_trip(now, trip, args, ctx);
930    }
931
932    fn spawn_ped(&mut self, now: Time, id: TripID, start: SidewalkSpot, ctx: &mut Ctx) {
933        let trip = &self.trips[id.0];
934        let walk_to = match trip.legs[0] {
935            TripLeg::Walk(ref to) => to.clone(),
936            _ => unreachable!(),
937        };
938
939        let req = PathRequest::walking(start.sidewalk_pos, walk_to.sidewalk_pos);
940        match ctx.map.pathfind(req) {
941            Ok(path) => {
942                let person = &self.people[trip.person.0];
943                ctx.scheduler.push(
944                    now,
945                    Command::SpawnPed(CreatePedestrian {
946                        id: person.ped,
947                        speed: person.ped_speed,
948                        start,
949                        goal: walk_to,
950                        path,
951                        trip: id,
952                        person: person.id,
953                    }),
954                );
955            }
956            Err(err) => {
957                self.cancel_trip(now, id, err.to_string(), None, ctx);
958            }
959        }
960    }
961}
962
963// Cancelling trips
964impl TripManager {
965    /// Cancel a trip before it's started. The person will stay where they are.
966    pub fn cancel_unstarted_trip(&mut self, id: TripID, reason: String) {
967        let trip = &mut self.trips[id.0];
968        self.unfinished_trips -= 1;
969        trip.info.cancellation_reason = Some(reason);
970        self.events
971            .push(Event::TripCancelled(trip.id, trip.info.mode));
972    }
973
974    /// Cancel a trip after it's started. The person will be magically warped to their destination,
975    /// along with their car, as if the trip had completed normally.
976    pub fn cancel_trip(
977        &mut self,
978        now: Time,
979        id: TripID,
980        reason: String,
981        abandoned_vehicle: Option<Vehicle>,
982        ctx: &mut Ctx,
983    ) {
984        let trip = &mut self.trips[id.0];
985        self.unfinished_trips -= 1;
986        trip.info.cancellation_reason = Some(reason);
987        self.events
988            .push(Event::TripCancelled(trip.id, trip.info.mode));
989        let person = trip.person;
990
991        // Maintain consistentency for anyone listening to events
992        if let PersonState::Inside(b) = self.people[person.0].state {
993            self.events.push(Event::PersonLeavesBuilding(person, b));
994        }
995        // Warp to the destination
996        self.people[person.0].state = match trip.info.end {
997            TripEndpoint::Building(b) => {
998                self.events.push(Event::PersonEntersBuilding(person, b));
999                PersonState::Inside(b)
1000            }
1001            TripEndpoint::Border(i) => {
1002                self.events.push(Event::PersonLeavesMap(person, None, i));
1003                PersonState::OffMap
1004            }
1005            // Can't end trips here yet
1006            TripEndpoint::SuddenlyAppear(_) => unreachable!(),
1007        };
1008
1009        // Don't forget the car!
1010        if let Some(vehicle) = abandoned_vehicle {
1011            if vehicle.vehicle_type == VehicleType::Car {
1012                // First remove the parked car, if needed. Maybe the trip was cancelled while the
1013                // car was parked in the starting building.
1014                if let Some(parked_car) = ctx.parking.lookup_parked_car(vehicle.id).cloned() {
1015                    ctx.parking.remove_parked_car(parked_car);
1016                }
1017
1018                if let TripEndpoint::Building(b) = trip.info.end {
1019                    let driving_lane = ctx.map.find_driving_lane_near_building(b);
1020                    if let Some(spot) = ctx
1021                        .parking
1022                        .get_all_free_spots(Position::start(driving_lane), &vehicle, b, ctx.map)
1023                        // TODO Could pick something closer, but meh, cancelled trips are bugs
1024                        // anyway
1025                        .get(0)
1026                        .map(|(spot, _)| *spot)
1027                        .or_else(|| {
1028                            ctx.parking
1029                                .path_to_free_parking_spot(driving_lane, &vehicle, b, ctx.map)
1030                                .map(|(_, spot, _)| spot)
1031                        })
1032                    {
1033                        self.events.push(Event::Alert(
1034                            AlertLocation::Person(person),
1035                            format!(
1036                                "{} had a trip cancelled, and their car was warped to {:?}",
1037                                person, spot
1038                            ),
1039                        ));
1040                        ctx.parking.reserve_spot(spot, vehicle.id);
1041                        ctx.parking.add_parked_car(ParkedCar {
1042                            vehicle,
1043                            spot,
1044                            parked_since: now,
1045                        });
1046                    } else {
1047                        self.events.push(Event::Alert(
1048                            AlertLocation::Person(person),
1049                            format!(
1050                                "{} had a trip cancelled, but nowhere to warp their car! Sucks.",
1051                                person
1052                            ),
1053                        ));
1054                    }
1055                }
1056            }
1057        } else {
1058            // If the trip was cancelled because we'e totally out of parking, don't forget to clean
1059            // this up.
1060            if let TripLeg::Drive(c, _) = &trip.legs[0] {
1061                if let Some(t) = self.active_trip_mode.remove(&AgentID::Car(*c)) {
1062                    assert_eq!(t, trip.id);
1063                }
1064            }
1065        }
1066
1067        self.start_delayed_trip(now, person, ctx);
1068    }
1069
1070    pub fn trip_abruptly_cancelled(&mut self, trip: TripID, agent: AgentID) {
1071        assert_eq!(self.active_trip_mode.remove(&agent), Some(trip));
1072    }
1073}
1074
1075// Queries
1076impl TripManager {
1077    pub fn active_agents(&self) -> Vec<AgentID> {
1078        self.active_trip_mode.keys().cloned().collect()
1079    }
1080    pub fn active_agents_and_trips(&self) -> &BTreeMap<AgentID, TripID> {
1081        &self.active_trip_mode
1082    }
1083    pub fn num_active_agents(&self) -> usize {
1084        self.active_trip_mode.len()
1085    }
1086
1087    pub fn trip_to_agent(&self, id: TripID) -> TripResult<AgentID> {
1088        if id.0 >= self.trips.len() {
1089            return TripResult::TripDoesntExist;
1090        }
1091        let trip = &self.trips[id.0];
1092
1093        if trip.finished_at.is_some() {
1094            return TripResult::TripDone;
1095        }
1096        if trip.info.cancellation_reason.is_some() {
1097            return TripResult::TripCancelled;
1098        }
1099        if !trip.started {
1100            return TripResult::TripNotStarted;
1101        }
1102
1103        let person = &self.people[trip.person.0];
1104        let a = match &trip.legs[0] {
1105            TripLeg::Walk(_) => AgentID::Pedestrian(person.ped),
1106            TripLeg::Drive(c, _) => AgentID::Car(*c),
1107            TripLeg::RideBus(_, _) => AgentID::BusPassenger(person.id, person.on_bus.unwrap()),
1108        };
1109        if self.active_trip_mode.get(&a) == Some(&id) {
1110            TripResult::Ok(a)
1111        } else {
1112            //panic!("{} should be ongoing, but no agent in active_trip_mode", id);
1113            TripResult::ModeChange
1114        }
1115    }
1116
1117    /// This will be None for parked cars and buses. Should always work for pedestrians.
1118    pub fn agent_to_trip(&self, id: AgentID) -> Option<TripID> {
1119        self.active_trip_mode.get(&id).cloned()
1120    }
1121
1122    pub fn debug_trip(&self, id: AgentID) {
1123        if let Some(t) = self.active_trip_mode.get(&id) {
1124            let trip = &self.trips[t.0];
1125            println!("{} has goal {:?}", trip.id, trip.legs.back().unwrap());
1126        } else {
1127            println!("{} has no trip, must be parked car", id);
1128        }
1129    }
1130
1131    pub fn num_trips(&self) -> (usize, usize) {
1132        (
1133            self.trips.len() - self.unfinished_trips,
1134            self.unfinished_trips,
1135        )
1136    }
1137    pub fn num_agents(&self, transit: &TransitSimState) -> Counter<AgentType> {
1138        let mut cnt = Counter::new();
1139        for a in self.active_trip_mode.keys() {
1140            cnt.inc(a.to_type());
1141        }
1142        let (buses, trains) = transit.active_vehicles();
1143        cnt.add(AgentType::Bus, buses);
1144        cnt.add(AgentType::Train, trains);
1145        cnt
1146    }
1147    pub fn num_commuters_vehicles(
1148        &self,
1149        transit: &TransitSimState,
1150        walking: &WalkingSimState,
1151    ) -> CommutersVehiclesCounts {
1152        let (buses, trains) = transit.active_vehicles();
1153        let mut cnt = CommutersVehiclesCounts {
1154            walking_commuters: 0,
1155            walking_to_from_transit: 0,
1156            walking_to_from_car: 0,
1157            walking_to_from_bike: 0,
1158
1159            cyclists: 0,
1160
1161            sov_drivers: 0,
1162
1163            buses,
1164            trains,
1165            bus_riders: 0,
1166            train_riders: 0,
1167        };
1168
1169        for a in self.active_trip_mode.keys() {
1170            match a {
1171                AgentID::Car(c) => match c.vehicle_type {
1172                    VehicleType::Car => {
1173                        cnt.sov_drivers += 1;
1174                    }
1175                    VehicleType::Bike => {
1176                        cnt.cyclists += 1;
1177                    }
1178                    VehicleType::Bus | VehicleType::Train => unreachable!(),
1179                },
1180                AgentID::BusPassenger(_, c) => match c.vehicle_type {
1181                    VehicleType::Bus => {
1182                        cnt.bus_riders += 1;
1183                    }
1184                    VehicleType::Train => {
1185                        cnt.train_riders += 1;
1186                    }
1187                    VehicleType::Car | VehicleType::Bike => unreachable!(),
1188                },
1189                // These're counted separately
1190                AgentID::Pedestrian(_) => {}
1191            }
1192        }
1193        walking.populate_commuter_counts(&mut cnt);
1194
1195        cnt
1196    }
1197    pub fn num_ppl(&self) -> (usize, usize, usize) {
1198        let mut ppl_in_bldg = 0;
1199        let mut ppl_off_map = 0;
1200        for p in &self.people {
1201            match p.state {
1202                PersonState::Trip(_) => {}
1203                PersonState::Inside(_) => {
1204                    ppl_in_bldg += 1;
1205                }
1206                PersonState::OffMap => {
1207                    ppl_off_map += 1;
1208                }
1209            }
1210        }
1211        (self.people.len(), ppl_in_bldg, ppl_off_map)
1212    }
1213
1214    pub fn is_done(&self) -> bool {
1215        self.unfinished_trips == 0
1216    }
1217
1218    pub fn trip_info(&self, id: TripID) -> TripInfo {
1219        self.trips[id.0].info.clone()
1220    }
1221    pub fn all_trip_info(&self) -> Vec<(TripID, TripInfo)> {
1222        self.trips.iter().map(|t| (t.id, t.info.clone())).collect()
1223    }
1224    pub fn finished_trip_details(&self, id: TripID) -> Option<(Duration, Duration, Distance)> {
1225        let t = &self.trips[id.0];
1226        Some((
1227            t.finished_at? - t.info.departure,
1228            t.total_blocked_time,
1229            t.total_distance,
1230        ))
1231    }
1232    pub fn trip_blocked_time(&self, id: TripID) -> Duration {
1233        let t = &self.trips[id.0];
1234        t.total_blocked_time
1235    }
1236    pub fn bldg_to_people(&self, b: BuildingID) -> Vec<PersonID> {
1237        let mut people = Vec::new();
1238        for p in &self.people {
1239            if p.state == PersonState::Inside(b) {
1240                people.push(p.id);
1241            }
1242        }
1243        people
1244    }
1245
1246    pub fn get_person(&self, p: PersonID) -> Option<&Person> {
1247        self.people.get(p.0)
1248    }
1249    pub fn get_all_people(&self) -> &Vec<Person> {
1250        &self.people
1251    }
1252
1253    pub fn trip_to_person(&self, id: TripID) -> Option<PersonID> {
1254        Some(self.trips.get(id.0)?.person)
1255    }
1256
1257    pub fn all_arrivals_at_border(&self, at: IntersectionID) -> Vec<(Time, AgentType)> {
1258        let mut times = Vec::new();
1259        for t in &self.trips {
1260            if t.info.cancellation_reason.is_some() {
1261                continue;
1262            }
1263            if let TripEndpoint::Border(i) = t.info.start {
1264                if i == at {
1265                    // We can make some assumptions here.
1266                    let agent_type = match t.info.mode {
1267                        TripMode::Walk => AgentType::Pedestrian,
1268                        TripMode::Bike => AgentType::Bike,
1269                        TripMode::Drive => AgentType::Car,
1270                        // TODO Not true for long. People will be able to spawn at borders already
1271                        // on a bus.
1272                        TripMode::Transit => AgentType::Pedestrian,
1273                    };
1274                    times.push((t.info.departure, agent_type));
1275                }
1276            }
1277        }
1278        times.sort();
1279        times
1280    }
1281
1282    /// Recreate the Scenario from an instantiated simulation. The results should match the
1283    /// original Scenario used.
1284    pub fn generate_scenario(&self, map: &Map, name: String) -> Scenario {
1285        let mut scenario = Scenario::empty(map, &name);
1286        for p in &self.people {
1287            scenario.people.push(PersonSpec {
1288                orig_id: p.orig_id,
1289                trips: p
1290                    .trips
1291                    .iter()
1292                    .map(|t| {
1293                        let trip = &self.trips[t.0];
1294                        IndividTrip::new(
1295                            trip.info.departure,
1296                            trip.info.purpose,
1297                            trip.info.start,
1298                            trip.info.end,
1299                            trip.info.mode,
1300                        )
1301                    })
1302                    .collect(),
1303            });
1304        }
1305        scenario
1306    }
1307}
1308
1309#[derive(Serialize, Deserialize, Debug, Clone)]
1310struct Trip {
1311    id: TripID,
1312    info: TripInfo,
1313    started: bool,
1314    finished_at: Option<Time>,
1315    total_blocked_time: Duration,
1316    total_distance: Distance,
1317    // Not filled out until the trip starts
1318    legs: VecDeque<TripLeg>,
1319    person: PersonID,
1320}
1321
1322#[derive(Serialize, Deserialize, Debug, Clone)]
1323pub struct TripInfo {
1324    /// Scheduled departure; the start may be delayed if the previous trip is taking too long.
1325    pub departure: Time,
1326    pub mode: TripMode,
1327    pub start: TripEndpoint,
1328    pub end: TripEndpoint,
1329    pub purpose: TripPurpose,
1330    /// Did a ScenarioModifier apply to this?
1331    pub modified: bool,
1332    pub cancellation_reason: Option<String>,
1333}
1334
1335impl Trip {
1336    fn assert_walking_leg(&mut self, goal: SidewalkSpot) {
1337        match self.legs.pop_front() {
1338            Some(TripLeg::Walk(spot)) => {
1339                assert_eq!(goal, spot);
1340            }
1341            _ => unreachable!(),
1342        }
1343    }
1344}
1345
1346/// These don't specify where the leg starts, since it might be unknown -- like when we drive and
1347/// don't know where we'll wind up parking.
1348#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
1349pub(crate) enum TripLeg {
1350    Walk(SidewalkSpot),
1351    /// A person may own many vehicles, so specify which they use
1352    Drive(CarID, DrivingGoal),
1353    /// Maybe get off at a stop, maybe ride off-map
1354    RideBus(TransitRouteID, Option<TransitStopID>),
1355}
1356
1357pub enum TripResult<T> {
1358    Ok(T),
1359    ModeChange,
1360    TripDone,
1361    TripDoesntExist,
1362    TripNotStarted,
1363    TripCancelled,
1364}
1365
1366impl<T> TripResult<T> {
1367    pub fn ok(self) -> Option<T> {
1368        match self {
1369            TripResult::Ok(data) => Some(data),
1370            _ => None,
1371        }
1372    }
1373
1374    pub fn propagate_error<X>(self) -> TripResult<X> {
1375        match self {
1376            TripResult::Ok(_) => panic!("TripResult is Ok, can't propagate_error"),
1377            TripResult::ModeChange => TripResult::ModeChange,
1378            TripResult::TripDone => TripResult::TripDone,
1379            TripResult::TripDoesntExist => TripResult::TripDoesntExist,
1380            TripResult::TripNotStarted => TripResult::TripNotStarted,
1381            TripResult::TripCancelled => TripResult::TripCancelled,
1382        }
1383    }
1384}
1385
1386#[derive(Serialize, Deserialize, Debug, Clone)]
1387pub struct Person {
1388    pub id: PersonID,
1389    pub orig_id: Option<OrigPersonID>,
1390    pub trips: Vec<TripID>,
1391    pub state: PersonState,
1392
1393    pub ped: PedestrianID,
1394    pub ped_speed: Speed,
1395    /// Both cars and bikes
1396    pub vehicles: Vec<Vehicle>,
1397
1398    delayed_trips: Vec<(TripID, StartTripArgs)>,
1399    on_bus: Option<CarID>,
1400}
1401
1402impl Person {
1403    fn get_vehicle(&self, id: CarID) -> Vehicle {
1404        self.vehicles.iter().find(|v| v.id == id).unwrap().clone()
1405    }
1406}
1407
1408#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
1409pub enum PersonState {
1410    Trip(TripID),
1411    Inside(BuildingID),
1412    OffMap,
1413}
1414
1415/// The number of active vehicles and commuters, broken into different categories.
1416pub struct CommutersVehiclesCounts {
1417    pub walking_commuters: usize,
1418    pub walking_to_from_transit: usize,
1419    pub walking_to_from_car: usize,
1420    pub walking_to_from_bike: usize,
1421
1422    pub cyclists: usize,
1423
1424    pub sov_drivers: usize,
1425
1426    pub buses: usize,
1427    pub trains: usize,
1428    pub bus_riders: usize,
1429    pub train_riders: usize,
1430}