1 use crate::sim::Ctx;
2 use crate::{
3 AgentID, AgentType, AlertLocation, CarID, Command, CreateCar, CreatePedestrian, DrivingGoal,
4 Event, IndividTrip, OffMapLocation, OrigPersonID, ParkedCar, ParkingSpot, PedestrianID,
5 PersonID, PersonSpec, Scenario, Scheduler, SidewalkPOI, SidewalkSpot, SpawnTrip,
6 TransitSimState, TripID, TripPhaseType, TripSpec, Vehicle, VehicleSpec, VehicleType,
7 WalkingSimState,
8 };
9 use abstutil::{deserialize_btreemap, serialize_btreemap, Counter};
10 use geom::{Duration, Speed, Time};
11 use map_model::{
12 BuildingID, BusRouteID, BusStopID, IntersectionID, Map, Path, PathConstraints, PathRequest,
13 Position,
14 };
15 use serde::{Deserialize, Serialize};
16 use std::collections::{BTreeMap, VecDeque};
17
18 #[derive(Serialize, Deserialize, Debug, Clone)]
19 pub struct TripManager {
20 trips: Vec<Trip>,
21 people: Vec<Person>,
22 // For quick lookup of active agents
23 #[serde(
24 serialize_with = "serialize_btreemap",
25 deserialize_with = "deserialize_btreemap"
26 )]
27 active_trip_mode: BTreeMap<AgentID, TripID>,
28 unfinished_trips: usize,
29 pub pathfinding_upfront: bool,
30
31 car_id_counter: usize,
32
33 events: Vec<Event>,
34 }
35
36 impl TripManager {
new(pathfinding_upfront: bool) -> TripManager37 pub fn new(pathfinding_upfront: bool) -> TripManager {
38 TripManager {
39 trips: Vec::new(),
40 people: Vec::new(),
41 active_trip_mode: BTreeMap::new(),
42 unfinished_trips: 0,
43 car_id_counter: 0,
44 events: Vec::new(),
45 pathfinding_upfront,
46 }
47 }
48
49 // TODO assert the specs are correct yo
new_person( &mut self, id: PersonID, orig_id: Option<OrigPersonID>, ped_speed: Speed, vehicle_specs: Vec<VehicleSpec>, )50 pub fn new_person(
51 &mut self,
52 id: PersonID,
53 orig_id: Option<OrigPersonID>,
54 ped_speed: Speed,
55 vehicle_specs: Vec<VehicleSpec>,
56 ) {
57 assert_eq!(id.0, self.people.len());
58 let vehicles = vehicle_specs
59 .into_iter()
60 .map(|v| {
61 let c = CarID(self.new_car_id(), v.vehicle_type);
62 v.make(c, Some(id))
63 })
64 .collect();
65 self.people.push(Person {
66 id,
67 orig_id,
68 trips: Vec::new(),
69 // The first new_trip will set this properly.
70 state: PersonState::OffMap,
71 ped: PedestrianID(id.0),
72 ped_speed,
73 vehicles,
74 delayed_trips: Vec::new(),
75 on_bus: None,
76 });
77 }
random_person(&mut self, ped_speed: Speed, vehicle_specs: Vec<VehicleSpec>) -> &Person78 pub fn random_person(&mut self, ped_speed: Speed, vehicle_specs: Vec<VehicleSpec>) -> &Person {
79 let id = PersonID(self.people.len());
80 self.new_person(id, None, ped_speed, vehicle_specs);
81 self.get_person(id).unwrap()
82 }
83
new_car_id(&mut self) -> usize84 pub fn new_car_id(&mut self) -> usize {
85 let id = self.car_id_counter;
86 self.car_id_counter += 1;
87 id
88 }
89
new_trip( &mut self, person: PersonID, departure: Time, start: TripEndpoint, mode: TripMode, modified: bool, legs: Vec<TripLeg>, map: &Map, ) -> TripID90 pub fn new_trip(
91 &mut self,
92 person: PersonID,
93 departure: Time,
94 start: TripEndpoint,
95 mode: TripMode,
96 modified: bool,
97 legs: Vec<TripLeg>,
98 map: &Map,
99 ) -> TripID {
100 assert!(!legs.is_empty());
101 // TODO Make sure the legs constitute a valid state machine.
102
103 let id = TripID(self.trips.len());
104 let end = match legs.last() {
105 Some(TripLeg::Walk(ref spot)) => match spot.connection {
106 SidewalkPOI::Building(b) => TripEndpoint::Bldg(b),
107 SidewalkPOI::Border(i, ref loc) => TripEndpoint::Border(i, loc.clone()),
108 _ => unreachable!(),
109 },
110 Some(TripLeg::Drive(_, ref goal)) => match goal {
111 DrivingGoal::ParkNear(b) => TripEndpoint::Bldg(*b),
112 DrivingGoal::Border(i, _, loc) => TripEndpoint::Border(*i, loc.clone()),
113 },
114 Some(TripLeg::Remote(ref to)) => {
115 TripEndpoint::Border(map.all_incoming_borders()[0].id, Some(to.clone()))
116 }
117 Some(TripLeg::RideBus(r, ref maybe_stop2)) => {
118 assert!(maybe_stop2.is_none());
119 // TODO No way to plumb OffMapLocation here
120 TripEndpoint::Border(map.get_l(map.get_br(*r).end_border.unwrap()).dst_i, None)
121 }
122 _ => unreachable!(),
123 };
124 let trip = Trip {
125 id,
126 info: TripInfo {
127 departure,
128 mode,
129 start,
130 end,
131 modified,
132 },
133 person,
134 started: false,
135 finished_at: None,
136 total_blocked_time: Duration::ZERO,
137 aborted: false,
138 cancelled: false,
139 legs: VecDeque::from(legs),
140 };
141 self.unfinished_trips += 1;
142 let person = &mut self.people[trip.person.0];
143 if person.trips.is_empty() {
144 person.state = match trip.info.start {
145 TripEndpoint::Bldg(b) => {
146 self.events
147 .push(Event::PersonEntersBuilding(trip.person, b));
148 PersonState::Inside(b)
149 }
150 TripEndpoint::Border(_, ref loc) => {
151 if let Some(loc) = loc {
152 self.events
153 .push(Event::PersonEntersRemoteBuilding(trip.person, loc.clone()));
154 }
155 PersonState::OffMap
156 }
157 };
158 }
159 if let Some(t) = person.trips.last() {
160 // TODO If it's exactly ==, what?! See the ID.
161 if self.trips[t.0].info.departure > trip.info.departure {
162 panic!(
163 "{} has a trip starting at {}, then one at {}",
164 person.id, self.trips[t.0].info.departure, trip.info.departure
165 );
166 }
167 }
168 person.trips.push(id);
169 self.trips.push(trip);
170 id
171 }
172
agent_starting_trip_leg(&mut self, agent: AgentID, t: TripID)173 pub fn agent_starting_trip_leg(&mut self, agent: AgentID, t: TripID) {
174 if let Some(other) = self.active_trip_mode.get(&agent) {
175 panic!("{} is doing both {} and {}?", agent, t, other);
176 }
177 self.active_trip_mode.insert(agent, t);
178 }
179
car_reached_parking_spot( &mut self, now: Time, car: CarID, spot: ParkingSpot, blocked_time: Duration, ctx: &mut Ctx, )180 pub fn car_reached_parking_spot(
181 &mut self,
182 now: Time,
183 car: CarID,
184 spot: ParkingSpot,
185 blocked_time: Duration,
186 ctx: &mut Ctx,
187 ) {
188 let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
189 trip.total_blocked_time += blocked_time;
190
191 match trip.legs.pop_front() {
192 Some(TripLeg::Drive(c, DrivingGoal::ParkNear(_))) => {
193 assert_eq!(car, c);
194 }
195 _ => unreachable!(),
196 };
197
198 match &trip.legs[0] {
199 TripLeg::Walk(to) => match (spot, &to.connection) {
200 (ParkingSpot::Offstreet(b1, _), SidewalkPOI::Building(b2)) if b1 == *b2 => {
201 // Do the relevant parts of ped_reached_parking_spot.
202 assert_eq!(trip.legs.len(), 1);
203 assert!(!trip.finished_at.is_some());
204 trip.finished_at = Some(now);
205 self.unfinished_trips -= 1;
206 self.events.push(Event::TripFinished {
207 trip: trip.id,
208 mode: trip.info.mode,
209 total_time: now - trip.info.departure,
210 blocked_time: trip.total_blocked_time,
211 });
212 let person = trip.person;
213 self.people[person.0].state = PersonState::Inside(b1);
214 self.events.push(Event::PersonEntersBuilding(person, b1));
215 self.person_finished_trip(now, person, ctx);
216 return;
217 }
218 _ => {}
219 },
220 _ => unreachable!(),
221 };
222
223 if !trip.spawn_ped(
224 now,
225 SidewalkSpot::parking_spot(spot, ctx.map, ctx.parking),
226 &self.people[trip.person.0],
227 ctx.map,
228 ctx.scheduler,
229 &mut self.events,
230 ) {
231 self.unfinished_trips -= 1;
232 }
233 }
234
ped_reached_parking_spot( &mut self, now: Time, ped: PedestrianID, spot: ParkingSpot, blocked_time: Duration, ctx: &mut Ctx, )235 pub fn ped_reached_parking_spot(
236 &mut self,
237 now: Time,
238 ped: PedestrianID,
239 spot: ParkingSpot,
240 blocked_time: Duration,
241 ctx: &mut Ctx,
242 ) {
243 self.events.push(Event::PedReachedParkingSpot(ped, spot));
244 let trip = &mut self.trips[self
245 .active_trip_mode
246 .remove(&AgentID::Pedestrian(ped))
247 .unwrap()
248 .0];
249 trip.total_blocked_time += blocked_time;
250
251 trip.assert_walking_leg(SidewalkSpot::deferred_parking_spot());
252 let parked_car = ctx.parking.get_car_at_spot(spot).unwrap().clone();
253 let drive_to = match trip.legs[0] {
254 TripLeg::Drive(c, ref to) => {
255 assert_eq!(c, parked_car.vehicle.id);
256 to.clone()
257 }
258 _ => unreachable!(),
259 };
260
261 let mut start =
262 ctx.parking
263 .spot_to_driving_pos(parked_car.spot, &parked_car.vehicle, ctx.map);
264 match spot {
265 ParkingSpot::Onstreet(_, _) => {}
266 ParkingSpot::Offstreet(b, _) => {
267 self.events
268 .push(Event::PersonEntersBuilding(trip.person, b));
269 // Actually, to unpark, the car's front should be where it'll wind up at the end.
270 start = Position::new(start.lane(), start.dist_along() + parked_car.vehicle.length);
271 }
272 ParkingSpot::Lot(_, _) => {
273 start = Position::new(start.lane(), start.dist_along() + parked_car.vehicle.length);
274 }
275 }
276 let end = drive_to.goal_pos(PathConstraints::Car, ctx.map).unwrap();
277 let req = PathRequest {
278 start,
279 end,
280 constraints: PathConstraints::Car,
281 };
282 let path = if let Some(p) = ctx.map.pathfind(req.clone()) {
283 p
284 } else {
285 self.events.push(Event::Alert(
286 AlertLocation::Person(trip.person),
287 format!(
288 "Aborting {} because no path for the car portion! {} to {}",
289 trip.id, start, end
290 ),
291 ));
292 // Move the car to the destination...
293 ctx.parking.remove_parked_car(parked_car.clone());
294 let trip = trip.id;
295 self.abort_trip(now, trip, Some(parked_car.vehicle), ctx);
296 return;
297 };
298
299 if !ctx.cap.allow_trip(parked_car.vehicle.id, &path) {
300 // TODO Different ways to handle this: abort the trip, delay it an hour, route around
301 // the zone, switch modes...
302 self.events.push(Event::Alert(
303 AlertLocation::Person(trip.person),
304 format!(
305 "Aborting {} because it would exceed the cap through some zone",
306 trip.id
307 ),
308 ));
309 // Move the car to the destination...
310 ctx.parking.remove_parked_car(parked_car.clone());
311 let trip = trip.id;
312 self.abort_trip(now, trip, Some(parked_car.vehicle), ctx);
313 return;
314 }
315
316 let router = drive_to.make_router(parked_car.vehicle.id, path, ctx.map);
317 ctx.scheduler.push(
318 now,
319 Command::SpawnCar(
320 CreateCar::for_parked_car(
321 parked_car,
322 router,
323 req,
324 start.dist_along(),
325 trip.id,
326 trip.person,
327 ),
328 true,
329 ),
330 );
331 }
332
ped_ready_to_bike( &mut self, now: Time, ped: PedestrianID, spot: SidewalkSpot, blocked_time: Duration, ctx: &mut Ctx, )333 pub fn ped_ready_to_bike(
334 &mut self,
335 now: Time,
336 ped: PedestrianID,
337 spot: SidewalkSpot,
338 blocked_time: Duration,
339 ctx: &mut Ctx,
340 ) {
341 let trip = &mut self.trips[self
342 .active_trip_mode
343 .remove(&AgentID::Pedestrian(ped))
344 .unwrap()
345 .0];
346 trip.total_blocked_time += blocked_time;
347
348 trip.assert_walking_leg(spot.clone());
349 let (bike, drive_to) = match trip.legs[0] {
350 TripLeg::Drive(bike, ref to) => (bike, to.clone()),
351 _ => unreachable!(),
352 };
353 let driving_pos = match spot.connection {
354 SidewalkPOI::BikeRack(p) => p,
355 _ => unreachable!(),
356 };
357
358 let end = if let Some(end) = drive_to.goal_pos(PathConstraints::Bike, ctx.map) {
359 end
360 } else {
361 self.events.push(Event::Alert(
362 AlertLocation::Person(trip.person),
363 format!(
364 "Aborting {} because no bike connection at {:?}",
365 trip.id, drive_to
366 ),
367 ));
368 let trip = trip.id;
369 self.abort_trip(now, trip, None, ctx);
370 return;
371 };
372 let req = PathRequest {
373 start: driving_pos,
374 end,
375 constraints: PathConstraints::Bike,
376 };
377 if let Some(router) = ctx
378 .map
379 .pathfind(req.clone())
380 .map(|path| drive_to.make_router(bike, path, ctx.map))
381 {
382 ctx.scheduler.push(
383 now,
384 Command::SpawnCar(
385 CreateCar::for_appearing(
386 self.people[trip.person.0].get_vehicle(bike),
387 driving_pos,
388 router,
389 req,
390 trip.id,
391 trip.person,
392 ),
393 true,
394 ),
395 );
396 } else {
397 self.events.push(Event::Alert(
398 AlertLocation::Person(trip.person),
399 format!(
400 "Aborting {} because no path for the bike portion (or sidewalk connection at \
401 the end)! {} to {}",
402 trip.id, driving_pos, end
403 ),
404 ));
405 let trip = trip.id;
406 self.abort_trip(now, trip, None, ctx);
407 }
408 }
409
bike_reached_end( &mut self, now: Time, bike: CarID, bike_rack: SidewalkSpot, blocked_time: Duration, map: &Map, scheduler: &mut Scheduler, )410 pub fn bike_reached_end(
411 &mut self,
412 now: Time,
413 bike: CarID,
414 bike_rack: SidewalkSpot,
415 blocked_time: Duration,
416 map: &Map,
417 scheduler: &mut Scheduler,
418 ) {
419 self.events.push(Event::BikeStoppedAtSidewalk(
420 bike,
421 bike_rack.sidewalk_pos.lane(),
422 ));
423 let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(bike)).unwrap().0];
424 trip.total_blocked_time += blocked_time;
425
426 match trip.legs.pop_front() {
427 Some(TripLeg::Drive(c, DrivingGoal::ParkNear(_))) => {
428 assert_eq!(c, bike);
429 }
430 _ => unreachable!(),
431 };
432
433 if !trip.spawn_ped(
434 now,
435 bike_rack,
436 &self.people[trip.person.0],
437 map,
438 scheduler,
439 &mut self.events,
440 ) {
441 self.unfinished_trips -= 1;
442 }
443 }
444
ped_reached_building( &mut self, now: Time, ped: PedestrianID, bldg: BuildingID, blocked_time: Duration, ctx: &mut Ctx, )445 pub fn ped_reached_building(
446 &mut self,
447 now: Time,
448 ped: PedestrianID,
449 bldg: BuildingID,
450 blocked_time: Duration,
451 ctx: &mut Ctx,
452 ) {
453 let trip = &mut self.trips[self
454 .active_trip_mode
455 .remove(&AgentID::Pedestrian(ped))
456 .unwrap()
457 .0];
458 trip.total_blocked_time += blocked_time;
459
460 trip.assert_walking_leg(SidewalkSpot::building(bldg, ctx.map));
461 assert!(trip.legs.is_empty());
462 assert!(!trip.finished_at.is_some());
463 trip.finished_at = Some(now);
464 self.unfinished_trips -= 1;
465 self.events.push(Event::TripFinished {
466 trip: trip.id,
467 mode: trip.info.mode,
468 total_time: now - trip.info.departure,
469 blocked_time: trip.total_blocked_time,
470 });
471 let person = trip.person;
472 self.people[person.0].state = PersonState::Inside(bldg);
473 self.events.push(Event::PersonEntersBuilding(person, bldg));
474 self.person_finished_trip(now, person, ctx);
475 }
476
477 // If no route is returned, the pedestrian boarded a bus immediately.
ped_reached_bus_stop( &mut self, now: Time, ped: PedestrianID, stop: BusStopID, blocked_time: Duration, ctx: &mut Ctx, transit: &mut TransitSimState, ) -> Option<BusRouteID>478 pub fn ped_reached_bus_stop(
479 &mut self,
480 now: Time,
481 ped: PedestrianID,
482 stop: BusStopID,
483 blocked_time: Duration,
484 ctx: &mut Ctx,
485 transit: &mut TransitSimState,
486 ) -> Option<BusRouteID> {
487 let trip = &mut self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0];
488 trip.total_blocked_time += blocked_time;
489
490 match trip.legs[0] {
491 TripLeg::Walk(ref spot) => {
492 assert_eq!(*spot, SidewalkSpot::bus_stop(stop, ctx.map));
493 }
494 _ => unreachable!(),
495 }
496 match trip.legs[1] {
497 TripLeg::RideBus(route, maybe_stop2) => {
498 self.events.push(Event::TripPhaseStarting(
499 trip.id,
500 trip.person,
501 None,
502 TripPhaseType::WaitingForBus(route, stop),
503 ));
504 if let Some(bus) = transit.ped_waiting_for_bus(
505 now,
506 ped,
507 trip.id,
508 trip.person,
509 stop,
510 route,
511 maybe_stop2,
512 ctx.map,
513 ) {
514 trip.legs.pop_front();
515 self.active_trip_mode
516 .remove(&AgentID::Pedestrian(ped))
517 .unwrap();
518 self.active_trip_mode
519 .insert(AgentID::BusPassenger(trip.person, bus), trip.id);
520 self.people[trip.person.0].on_bus = Some(bus);
521 None
522 } else {
523 Some(route)
524 }
525 }
526 _ => unreachable!(),
527 }
528 }
529
ped_boarded_bus( &mut self, now: Time, ped: PedestrianID, bus: CarID, blocked_time: Duration, walking: &mut WalkingSimState, ) -> (TripID, PersonID)530 pub fn ped_boarded_bus(
531 &mut self,
532 now: Time,
533 ped: PedestrianID,
534 bus: CarID,
535 blocked_time: Duration,
536 walking: &mut WalkingSimState,
537 ) -> (TripID, PersonID) {
538 let trip = &mut self.trips[self
539 .active_trip_mode
540 .remove(&AgentID::Pedestrian(ped))
541 .unwrap()
542 .0];
543 trip.total_blocked_time += blocked_time;
544
545 trip.legs.pop_front();
546 walking.ped_boarded_bus(now, ped);
547 self.active_trip_mode
548 .insert(AgentID::BusPassenger(trip.person, bus), trip.id);
549 self.people[trip.person.0].on_bus = Some(bus);
550 (trip.id, trip.person)
551 }
552
553 // TODO Need to characterize delay the bus experienced
person_left_bus(&mut self, now: Time, person: PersonID, bus: CarID, ctx: &mut Ctx)554 pub fn person_left_bus(&mut self, now: Time, person: PersonID, bus: CarID, ctx: &mut Ctx) {
555 let trip = &mut self.trips[self
556 .active_trip_mode
557 .remove(&AgentID::BusPassenger(person, bus))
558 .unwrap()
559 .0];
560 let start = match trip.legs.pop_front().unwrap() {
561 TripLeg::RideBus(_, maybe_stop2) => SidewalkSpot::bus_stop(
562 maybe_stop2.expect("someone left a bus, even though they should've ridden off-map"),
563 ctx.map,
564 ),
565 _ => unreachable!(),
566 };
567 self.people[person.0].on_bus.take().unwrap();
568
569 if !trip.spawn_ped(
570 now,
571 start,
572 &self.people[trip.person.0],
573 ctx.map,
574 ctx.scheduler,
575 &mut self.events,
576 ) {
577 self.unfinished_trips -= 1;
578 }
579 }
580
ped_reached_border( &mut self, now: Time, ped: PedestrianID, i: IntersectionID, blocked_time: Duration, ctx: &mut Ctx, )581 pub fn ped_reached_border(
582 &mut self,
583 now: Time,
584 ped: PedestrianID,
585 i: IntersectionID,
586 blocked_time: Duration,
587 ctx: &mut Ctx,
588 ) {
589 let trip = &mut self.trips[self
590 .active_trip_mode
591 .remove(&AgentID::Pedestrian(ped))
592 .unwrap()
593 .0];
594 trip.total_blocked_time += blocked_time;
595
596 match trip.legs.pop_front() {
597 Some(TripLeg::Walk(spot)) => match spot.connection {
598 SidewalkPOI::Border(i2, _) => assert_eq!(i, i2),
599 _ => unreachable!(),
600 },
601 _ => unreachable!(),
602 }
603 assert!(trip.legs.is_empty());
604 assert!(!trip.finished_at.is_some());
605 trip.finished_at = Some(now);
606 self.unfinished_trips -= 1;
607 self.events.push(Event::TripFinished {
608 trip: trip.id,
609 mode: trip.info.mode,
610 total_time: now - trip.info.departure,
611 blocked_time: trip.total_blocked_time,
612 });
613 let person = trip.person;
614 if let TripEndpoint::Border(_, ref loc) = trip.info.end {
615 self.events.push(Event::PersonLeavesMap(
616 person,
617 Some(AgentID::Pedestrian(ped)),
618 i,
619 loc.clone(),
620 ));
621 }
622 self.people[person.0].state = PersonState::OffMap;
623 self.person_finished_trip(now, person, ctx);
624 }
625
transit_rider_reached_border( &mut self, now: Time, person: PersonID, bus: CarID, ctx: &mut Ctx, )626 pub fn transit_rider_reached_border(
627 &mut self,
628 now: Time,
629 person: PersonID,
630 bus: CarID,
631 ctx: &mut Ctx,
632 ) {
633 let agent = AgentID::BusPassenger(person, bus);
634 let trip = &mut self.trips[self.active_trip_mode.remove(&agent).unwrap().0];
635
636 match trip.legs.pop_front() {
637 Some(TripLeg::RideBus(_, maybe_spot2)) => assert!(maybe_spot2.is_none()),
638 _ => unreachable!(),
639 }
640 assert!(trip.legs.is_empty());
641 assert!(!trip.finished_at.is_some());
642 trip.finished_at = Some(now);
643 self.unfinished_trips -= 1;
644 self.events.push(Event::TripFinished {
645 trip: trip.id,
646 mode: trip.info.mode,
647 total_time: now - trip.info.departure,
648 blocked_time: trip.total_blocked_time,
649 });
650 let person = trip.person;
651 if let TripEndpoint::Border(i, ref loc) = trip.info.end {
652 self.events
653 .push(Event::PersonLeavesMap(person, Some(agent), i, loc.clone()));
654 } else {
655 unreachable!()
656 }
657 self.people[person.0].state = PersonState::OffMap;
658 self.person_finished_trip(now, person, ctx);
659 }
660
car_or_bike_reached_border( &mut self, now: Time, car: CarID, i: IntersectionID, blocked_time: Duration, ctx: &mut Ctx, )661 pub fn car_or_bike_reached_border(
662 &mut self,
663 now: Time,
664 car: CarID,
665 i: IntersectionID,
666 blocked_time: Duration,
667 ctx: &mut Ctx,
668 ) {
669 let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
670 trip.total_blocked_time += blocked_time;
671
672 match trip.legs.pop_front().unwrap() {
673 TripLeg::Drive(c, DrivingGoal::Border(int, _, _)) => {
674 assert_eq!(car, c);
675 assert_eq!(i, int);
676 }
677 _ => unreachable!(),
678 };
679 assert!(trip.legs.is_empty());
680 assert!(!trip.finished_at.is_some());
681 trip.finished_at = Some(now);
682 self.unfinished_trips -= 1;
683 self.events.push(Event::TripFinished {
684 trip: trip.id,
685 mode: trip.info.mode,
686 total_time: now - trip.info.departure,
687 blocked_time: trip.total_blocked_time,
688 });
689 let person = trip.person;
690 self.people[person.0].state = PersonState::OffMap;
691 if let TripEndpoint::Border(_, ref loc) = trip.info.end {
692 self.events.push(Event::PersonLeavesMap(
693 person,
694 Some(AgentID::Car(car)),
695 i,
696 loc.clone(),
697 ));
698 }
699 self.person_finished_trip(now, person, ctx);
700 }
701
remote_trip_finished(&mut self, now: Time, id: TripID, ctx: &mut Ctx)702 pub fn remote_trip_finished(&mut self, now: Time, id: TripID, ctx: &mut Ctx) {
703 let trip = &mut self.trips[id.0];
704
705 let to = match trip.legs.pop_front() {
706 Some(TripLeg::Remote(to)) => to,
707 _ => unreachable!(),
708 };
709 assert!(trip.legs.is_empty());
710 assert!(!trip.finished_at.is_some());
711 trip.finished_at = Some(now);
712 self.unfinished_trips -= 1;
713 self.events.push(Event::TripFinished {
714 trip: trip.id,
715 mode: trip.info.mode,
716 total_time: now - trip.info.departure,
717 blocked_time: trip.total_blocked_time,
718 });
719 let person = trip.person;
720 self.events
721 .push(Event::PersonEntersRemoteBuilding(person, to));
722 self.people[person.0].state = PersonState::OffMap;
723 self.person_finished_trip(now, person, ctx);
724 }
725
726 // Different than aborting a trip. Don't warp any vehicles or change where the person is.
cancel_trip(&mut self, id: TripID)727 pub fn cancel_trip(&mut self, id: TripID) {
728 let trip = &mut self.trips[id.0];
729 self.unfinished_trips -= 1;
730 trip.cancelled = true;
731 // TODO Still representing the same way in analytics
732 self.events.push(Event::TripAborted(trip.id));
733 }
734
abort_trip( &mut self, now: Time, id: TripID, abandoned_vehicle: Option<Vehicle>, ctx: &mut Ctx, )735 pub fn abort_trip(
736 &mut self,
737 now: Time,
738 id: TripID,
739 abandoned_vehicle: Option<Vehicle>,
740 ctx: &mut Ctx,
741 ) {
742 let trip = &mut self.trips[id.0];
743 self.unfinished_trips -= 1;
744 trip.aborted = true;
745 self.events.push(Event::TripAborted(trip.id));
746 let person = trip.person;
747
748 // Maintain consistentency for anyone listening to events
749 if let PersonState::Inside(b) = self.people[person.0].state {
750 self.events.push(Event::PersonLeavesBuilding(person, b));
751 }
752 match trip.info.end {
753 TripEndpoint::Bldg(b) => {
754 self.events.push(Event::PersonEntersBuilding(person, b));
755 }
756 TripEndpoint::Border(i, ref loc) => {
757 self.events
758 .push(Event::PersonLeavesMap(person, None, i, loc.clone()));
759 }
760 }
761
762 // Warp to the destination
763 self.people[person.0].state = match trip.info.end {
764 TripEndpoint::Bldg(b) => PersonState::Inside(b),
765 TripEndpoint::Border(_, _) => PersonState::OffMap,
766 };
767 // Don't forget the car!
768 if let Some(vehicle) = abandoned_vehicle {
769 if vehicle.vehicle_type == VehicleType::Car {
770 if let TripEndpoint::Bldg(b) = trip.info.end {
771 let driving_lane = ctx.map.find_driving_lane_near_building(b);
772 if let Some(spot) = ctx
773 .parking
774 .get_all_free_spots(Position::start(driving_lane), &vehicle, b, ctx.map)
775 // TODO Could pick something closer, but meh, aborted trips are bugs anyway
776 .get(0)
777 .map(|(spot, _)| spot.clone())
778 .or_else(|| {
779 ctx.parking
780 .path_to_free_parking_spot(driving_lane, &vehicle, b, ctx.map)
781 .map(|(_, spot, _)| spot)
782 })
783 {
784 self.events.push(Event::Alert(
785 AlertLocation::Person(person),
786 format!(
787 "{} had a trip aborted, and their car was warped to {:?}",
788 person, spot
789 ),
790 ));
791 ctx.parking.reserve_spot(spot);
792 ctx.parking.add_parked_car(ParkedCar { vehicle, spot });
793 } else {
794 self.events.push(Event::Alert(
795 AlertLocation::Person(person),
796 format!(
797 "{} had a trip aborted, but nowhere to warp their car! Sucks.",
798 person
799 ),
800 ));
801 }
802 }
803 }
804 } else {
805 // If the trip was aborted because we'e totally out of parking, don't forget to clean
806 // this up.
807 if let TripLeg::Drive(c, _) = &trip.legs[0] {
808 if let Some(t) = self.active_trip_mode.remove(&AgentID::Car(*c)) {
809 assert_eq!(t, trip.id);
810 }
811 }
812 }
813
814 self.person_finished_trip(now, person, ctx);
815 }
816
active_agents(&self) -> Vec<AgentID>817 pub fn active_agents(&self) -> Vec<AgentID> {
818 self.active_trip_mode.keys().cloned().collect()
819 }
get_active_trips(&self) -> Vec<TripID>820 pub fn get_active_trips(&self) -> Vec<TripID> {
821 self.active_trip_mode.values().cloned().collect()
822 }
num_active_agents(&self) -> usize823 pub fn num_active_agents(&self) -> usize {
824 self.active_trip_mode.len()
825 }
826
trip_to_agent(&self, id: TripID) -> TripResult<AgentID>827 pub fn trip_to_agent(&self, id: TripID) -> TripResult<AgentID> {
828 if id.0 >= self.trips.len() {
829 return TripResult::TripDoesntExist;
830 }
831 let trip = &self.trips[id.0];
832
833 if trip.finished_at.is_some() {
834 return TripResult::TripDone;
835 }
836 if trip.aborted {
837 return TripResult::TripAborted;
838 }
839 if trip.cancelled {
840 return TripResult::TripCancelled;
841 }
842 if !trip.started {
843 return TripResult::TripNotStarted;
844 }
845
846 let person = &self.people[trip.person.0];
847 let a = match &trip.legs[0] {
848 TripLeg::Walk(_) => AgentID::Pedestrian(person.ped),
849 TripLeg::Drive(c, _) => AgentID::Car(*c),
850 TripLeg::RideBus(_, _) => AgentID::BusPassenger(person.id, person.on_bus.unwrap()),
851 TripLeg::Remote(_) => {
852 return TripResult::RemoteTrip;
853 }
854 };
855 if self.active_trip_mode.get(&a) == Some(&id) {
856 TripResult::Ok(a)
857 } else {
858 //panic!("{} should be ongoing, but no agent in active_trip_mode", id);
859 TripResult::ModeChange
860 }
861 }
862
863 // This will be None for parked cars and buses. Should always work for pedestrians.
agent_to_trip(&self, id: AgentID) -> Option<TripID>864 pub fn agent_to_trip(&self, id: AgentID) -> Option<TripID> {
865 self.active_trip_mode.get(&id).cloned()
866 }
867
debug_trip(&self, id: AgentID)868 pub fn debug_trip(&self, id: AgentID) {
869 if let Some(t) = self.active_trip_mode.get(&id) {
870 let trip = &self.trips[t.0];
871 println!("{} has goal {:?}", trip.id, trip.legs.back().unwrap());
872 } else {
873 println!("{} has no trip, must be parked car", id);
874 }
875 }
876
num_trips(&self) -> (usize, usize)877 pub fn num_trips(&self) -> (usize, usize) {
878 (
879 self.trips.len() - self.unfinished_trips,
880 self.unfinished_trips,
881 )
882 }
num_agents(&self, transit: &TransitSimState) -> Counter<AgentType>883 pub fn num_agents(&self, transit: &TransitSimState) -> Counter<AgentType> {
884 let mut cnt = Counter::new();
885 for a in self.active_trip_mode.keys() {
886 cnt.inc(a.to_type());
887 }
888 let (buses, trains) = transit.active_vehicles();
889 cnt.add(AgentType::Bus, buses);
890 cnt.add(AgentType::Train, trains);
891 cnt
892 }
num_ppl(&self) -> (usize, usize, usize)893 pub fn num_ppl(&self) -> (usize, usize, usize) {
894 let mut ppl_in_bldg = 0;
895 let mut ppl_off_map = 0;
896 for p in &self.people {
897 match p.state {
898 PersonState::Trip(_) => {}
899 PersonState::Inside(_) => {
900 ppl_in_bldg += 1;
901 }
902 PersonState::OffMap => {
903 ppl_off_map += 1;
904 }
905 }
906 }
907 (self.people.len(), ppl_in_bldg, ppl_off_map)
908 }
909
is_done(&self) -> bool910 pub fn is_done(&self) -> bool {
911 self.unfinished_trips == 0
912 }
913
collect_events(&mut self) -> Vec<Event>914 pub fn collect_events(&mut self) -> Vec<Event> {
915 std::mem::replace(&mut self.events, Vec::new())
916 }
917
trip_info(&self, id: TripID) -> TripInfo918 pub fn trip_info(&self, id: TripID) -> TripInfo {
919 self.trips[id.0].info.clone()
920 }
all_trip_info(&self) -> Vec<(TripID, TripInfo)>921 pub fn all_trip_info(&self) -> Vec<(TripID, TripInfo)> {
922 self.trips.iter().map(|t| (t.id, t.info.clone())).collect()
923 }
finished_trip_time(&self, id: TripID) -> Option<(Duration, Duration)>924 pub fn finished_trip_time(&self, id: TripID) -> Option<(Duration, Duration)> {
925 let t = &self.trips[id.0];
926 Some((t.finished_at? - t.info.departure, t.total_blocked_time))
927 }
928
bldg_to_people(&self, b: BuildingID) -> Vec<PersonID>929 pub fn bldg_to_people(&self, b: BuildingID) -> Vec<PersonID> {
930 let mut people = Vec::new();
931 for p in &self.people {
932 if p.state == PersonState::Inside(b) {
933 people.push(p.id);
934 }
935 }
936 people
937 }
938
get_person(&self, p: PersonID) -> Option<&Person>939 pub fn get_person(&self, p: PersonID) -> Option<&Person> {
940 self.people.get(p.0)
941 }
get_all_people(&self) -> &Vec<Person>942 pub fn get_all_people(&self) -> &Vec<Person> {
943 &self.people
944 }
945
trip_to_person(&self, id: TripID) -> PersonID946 pub fn trip_to_person(&self, id: TripID) -> PersonID {
947 self.trips[id.0].person
948 }
949
person_finished_trip(&mut self, now: Time, person: PersonID, ctx: &mut Ctx)950 fn person_finished_trip(&mut self, now: Time, person: PersonID, ctx: &mut Ctx) {
951 let person = &mut self.people[person.0];
952 if person.delayed_trips.is_empty() {
953 return;
954 }
955 let (trip, spec, maybe_req, maybe_path) = person.delayed_trips.remove(0);
956 if false {
957 self.events.push(Event::Alert(
958 AlertLocation::Person(person.id),
959 format!(
960 "{} just freed up, so starting delayed trip {}",
961 person.id, trip
962 ),
963 ));
964 }
965 self.start_trip(now, trip, spec, maybe_req, maybe_path, ctx);
966 }
967
start_trip( &mut self, now: Time, trip: TripID, spec: TripSpec, maybe_req: Option<PathRequest>, mut maybe_path: Option<Path>, ctx: &mut Ctx, )968 pub fn start_trip(
969 &mut self,
970 now: Time,
971 trip: TripID,
972 spec: TripSpec,
973 maybe_req: Option<PathRequest>,
974 mut maybe_path: Option<Path>,
975 ctx: &mut Ctx,
976 ) {
977 assert!(!self.trips[trip.0].cancelled);
978 assert!(!self.trips[trip.0].aborted);
979 if !self.pathfinding_upfront && maybe_path.is_none() && maybe_req.is_some() {
980 maybe_path = ctx.map.pathfind(maybe_req.clone().unwrap());
981 }
982
983 let person = &mut self.people[self.trips[trip.0].person.0];
984 if let PersonState::Trip(_) = person.state {
985 // Previous trip isn't done. Defer this one!
986 if false {
987 self.events.push(Event::Alert(
988 AlertLocation::Person(person.id),
989 format!(
990 "{} is still doing a trip, so not starting {} yet",
991 person.id, trip
992 ),
993 ));
994 }
995 person
996 .delayed_trips
997 .push((trip, spec, maybe_req, maybe_path));
998 self.events.push(Event::TripPhaseStarting(
999 trip,
1000 person.id,
1001 None,
1002 TripPhaseType::DelayedStart,
1003 ));
1004 return;
1005 }
1006 self.trips[trip.0].started = true;
1007
1008 match spec {
1009 TripSpec::VehicleAppearing {
1010 start_pos,
1011 goal,
1012 retry_if_no_room,
1013 use_vehicle,
1014 origin,
1015 } => {
1016 assert_eq!(person.state, PersonState::OffMap);
1017 self.events.push(Event::PersonEntersMap(
1018 person.id,
1019 AgentID::Car(use_vehicle),
1020 ctx.map.get_l(start_pos.lane()).src_i,
1021 origin,
1022 ));
1023 person.state = PersonState::Trip(trip);
1024
1025 let vehicle = person.get_vehicle(use_vehicle);
1026 assert!(ctx.parking.lookup_parked_car(vehicle.id).is_none());
1027 let req = maybe_req.unwrap();
1028 if let Some(router) =
1029 maybe_path.map(|path| goal.make_router(vehicle.id, path, ctx.map))
1030 {
1031 if !ctx.cap.allow_trip(vehicle.id, router.get_path()) {
1032 self.events.push(Event::Alert(
1033 AlertLocation::Person(person.id),
1034 format!(
1035 "Aborting {} because it would exceed the cap through some zone",
1036 trip
1037 ),
1038 ));
1039 self.abort_trip(now, trip, Some(vehicle), ctx);
1040 return;
1041 }
1042
1043 ctx.scheduler.push(
1044 now,
1045 Command::SpawnCar(
1046 CreateCar::for_appearing(
1047 vehicle, start_pos, router, req, trip, person.id,
1048 ),
1049 retry_if_no_room,
1050 ),
1051 );
1052 } else {
1053 self.events.push(Event::Alert(
1054 AlertLocation::Person(person.id),
1055 format!(
1056 "VehicleAppearing trip couldn't find the first path: {}",
1057 req
1058 ),
1059 ));
1060 self.abort_trip(now, trip, Some(vehicle), ctx);
1061 }
1062 }
1063 TripSpec::NoRoomToSpawn {
1064 i,
1065 use_vehicle,
1066 error,
1067 ..
1068 } => {
1069 self.events.push(Event::Alert(
1070 AlertLocation::Intersection(i),
1071 format!("{} couldn't spawn at border {}: {}", person.id, i, error),
1072 ));
1073 let vehicle = person.get_vehicle(use_vehicle);
1074 self.abort_trip(now, trip, Some(vehicle), ctx);
1075 }
1076 TripSpec::UsingParkedCar {
1077 car, start_bldg, ..
1078 } => {
1079 assert_eq!(person.state, PersonState::Inside(start_bldg));
1080 person.state = PersonState::Trip(trip);
1081
1082 // TODO For now, use the car we decided to statically. That makes sense in most
1083 // cases.
1084
1085 if let Some(parked_car) = ctx.parking.lookup_parked_car(car).cloned() {
1086 let start = SidewalkSpot::building(start_bldg, ctx.map);
1087 let walking_goal =
1088 SidewalkSpot::parking_spot(parked_car.spot, ctx.map, ctx.parking);
1089 let req = PathRequest {
1090 start: start.sidewalk_pos,
1091 end: walking_goal.sidewalk_pos,
1092 constraints: PathConstraints::Pedestrian,
1093 };
1094 if let Some(path) = ctx.map.pathfind(req.clone()) {
1095 ctx.scheduler.push(
1096 now,
1097 Command::SpawnPed(CreatePedestrian {
1098 id: person.ped,
1099 speed: person.ped_speed,
1100 start,
1101 goal: walking_goal,
1102 path,
1103 req,
1104 trip,
1105 person: person.id,
1106 }),
1107 );
1108 } else {
1109 self.events.push(Event::Alert(
1110 AlertLocation::Person(person.id),
1111 format!("UsingParkedCar trip couldn't find the walking path {}", req),
1112 ));
1113 // Move the car to the destination
1114 ctx.parking.remove_parked_car(parked_car.clone());
1115 self.abort_trip(now, trip, Some(parked_car.vehicle), ctx);
1116 }
1117 } else {
1118 // This should only happen when a driving trip has been aborted and there was
1119 // absolutely no room to warp the car.
1120 self.events.push(Event::Alert(
1121 AlertLocation::Person(person.id),
1122 format!(
1123 "{} should have {} parked somewhere, but it's unavailable, so \
1124 aborting {}",
1125 person.id, car, trip
1126 ),
1127 ));
1128 self.abort_trip(now, trip, None, ctx);
1129 }
1130 }
1131 TripSpec::JustWalking { start, goal } => {
1132 assert_eq!(
1133 person.state,
1134 match start.connection {
1135 SidewalkPOI::Building(b) => PersonState::Inside(b),
1136 SidewalkPOI::Border(i, ref loc) => {
1137 self.events.push(Event::PersonEntersMap(
1138 person.id,
1139 AgentID::Pedestrian(person.ped),
1140 i,
1141 loc.clone(),
1142 ));
1143 PersonState::OffMap
1144 }
1145 SidewalkPOI::SuddenlyAppear => {
1146 // Unclear which end of the sidewalk this person should be associated
1147 // with. For interactively spawned people, doesn't really matter.
1148 self.events.push(Event::PersonEntersMap(
1149 person.id,
1150 AgentID::Pedestrian(person.ped),
1151 ctx.map.get_l(start.sidewalk_pos.lane()).src_i,
1152 None,
1153 ));
1154 PersonState::OffMap
1155 }
1156 _ => unreachable!(),
1157 }
1158 );
1159 person.state = PersonState::Trip(trip);
1160
1161 let req = maybe_req.unwrap();
1162 if let Some(path) = maybe_path {
1163 ctx.scheduler.push(
1164 now,
1165 Command::SpawnPed(CreatePedestrian {
1166 id: person.ped,
1167 speed: person.ped_speed,
1168 start,
1169 goal,
1170 path,
1171 req,
1172 trip,
1173 person: person.id,
1174 }),
1175 );
1176 } else {
1177 self.events.push(Event::Alert(
1178 AlertLocation::Person(person.id),
1179 format!("JustWalking trip couldn't find the first path {}", req),
1180 ));
1181 self.abort_trip(now, trip, None, ctx);
1182 }
1183 }
1184 TripSpec::UsingBike { start, .. } => {
1185 assert_eq!(person.state, PersonState::Inside(start));
1186 person.state = PersonState::Trip(trip);
1187
1188 if let Some(walk_to) = SidewalkSpot::bike_rack(start, ctx.map) {
1189 let req = maybe_req.unwrap();
1190 if let Some(path) = maybe_path {
1191 ctx.scheduler.push(
1192 now,
1193 Command::SpawnPed(CreatePedestrian {
1194 id: person.ped,
1195 speed: person.ped_speed,
1196 start: SidewalkSpot::building(start, ctx.map),
1197 goal: walk_to,
1198 path,
1199 req,
1200 trip,
1201 person: person.id,
1202 }),
1203 );
1204 } else {
1205 self.events.push(Event::Alert(
1206 AlertLocation::Person(person.id),
1207 format!("UsingBike trip couldn't find the first path {}", req),
1208 ));
1209 self.abort_trip(now, trip, None, ctx);
1210 }
1211 } else {
1212 self.events.push(Event::Alert(
1213 AlertLocation::Person(person.id),
1214 format!(
1215 "UsingBike trip couldn't find a way to start biking from {}",
1216 start
1217 ),
1218 ));
1219 self.abort_trip(now, trip, None, ctx);
1220 }
1221 }
1222 TripSpec::UsingTransit { start, stop1, .. } => {
1223 assert_eq!(
1224 person.state,
1225 match start.connection {
1226 SidewalkPOI::Building(b) => PersonState::Inside(b),
1227 SidewalkPOI::Border(i, ref loc) => {
1228 self.events.push(Event::PersonEntersMap(
1229 person.id,
1230 AgentID::Pedestrian(person.ped),
1231 i,
1232 loc.clone(),
1233 ));
1234 PersonState::OffMap
1235 }
1236 SidewalkPOI::SuddenlyAppear => {
1237 // Unclear which end of the sidewalk this person should be associated
1238 // with. For interactively spawned people, doesn't really matter.
1239 self.events.push(Event::PersonEntersMap(
1240 person.id,
1241 AgentID::Pedestrian(person.ped),
1242 ctx.map.get_l(start.sidewalk_pos.lane()).src_i,
1243 None,
1244 ));
1245 PersonState::OffMap
1246 }
1247 _ => unreachable!(),
1248 }
1249 );
1250 person.state = PersonState::Trip(trip);
1251
1252 let walk_to = SidewalkSpot::bus_stop(stop1, ctx.map);
1253 let req = maybe_req.unwrap();
1254 if let Some(path) = maybe_path {
1255 ctx.scheduler.push(
1256 now,
1257 Command::SpawnPed(CreatePedestrian {
1258 id: person.ped,
1259 speed: person.ped_speed,
1260 start,
1261 goal: walk_to,
1262 path,
1263 req,
1264 trip,
1265 person: person.id,
1266 }),
1267 );
1268 } else {
1269 self.events.push(Event::Alert(
1270 AlertLocation::Person(person.id),
1271 format!("UsingTransit trip couldn't find the first path {}", req),
1272 ));
1273 self.abort_trip(now, trip, None, ctx);
1274 }
1275 }
1276 TripSpec::Remote {
1277 trip_time, from, ..
1278 } => {
1279 assert_eq!(person.state, PersonState::OffMap);
1280 person.state = PersonState::Trip(trip);
1281 self.events
1282 .push(Event::PersonLeavesRemoteBuilding(person.id, from));
1283 ctx.scheduler
1284 .push(now + trip_time, Command::FinishRemoteTrip(trip));
1285 self.events.push(Event::TripPhaseStarting(
1286 trip,
1287 person.id,
1288 None,
1289 TripPhaseType::Remote,
1290 ));
1291 }
1292 }
1293 }
1294
all_arrivals_at_border(&self, at: IntersectionID) -> Vec<(Time, AgentType)>1295 pub fn all_arrivals_at_border(&self, at: IntersectionID) -> Vec<(Time, AgentType)> {
1296 let mut times = Vec::new();
1297 for t in &self.trips {
1298 if t.aborted || t.cancelled {
1299 continue;
1300 }
1301 if let TripEndpoint::Border(i, _) = t.info.start {
1302 if i == at {
1303 // We can make some assumptions here.
1304 let agent_type = match t.info.mode {
1305 TripMode::Walk => AgentType::Pedestrian,
1306 TripMode::Bike => AgentType::Bike,
1307 TripMode::Drive => AgentType::Car,
1308 // TODO Not true for long. People will be able to spawn at borders already
1309 // on a bus.
1310 TripMode::Transit => AgentType::Pedestrian,
1311 };
1312 times.push((t.info.departure, agent_type));
1313 }
1314 }
1315 }
1316 times.sort();
1317 times
1318 }
1319
1320 // TODO This could be lossy. There are a few layers in spawning trips, and things like
1321 // spawn_agents_around reach into one of the middle layers directly. So here in TripManager, we
1322 // might not have retained enough state to create a proper scenario. But this should work
1323 // reasonably for most cases.
generate_scenario(&self, map: &Map, name: String) -> Scenario1324 pub fn generate_scenario(&self, map: &Map, name: String) -> Scenario {
1325 let mut scenario = Scenario::empty(map, &name);
1326 for p in &self.people {
1327 scenario.people.push(PersonSpec {
1328 id: p.id,
1329 orig_id: p.orig_id,
1330 trips: p
1331 .trips
1332 .iter()
1333 .filter_map(|t| {
1334 let trip = &self.trips[t.0];
1335 SpawnTrip::new(
1336 trip.info.start.clone(),
1337 trip.info.end.clone(),
1338 trip.info.mode,
1339 map,
1340 )
1341 .map(|spawn| IndividTrip::new(trip.info.departure, spawn))
1342 })
1343 .collect(),
1344 });
1345 }
1346 scenario
1347 }
1348 }
1349
1350 #[derive(Serialize, Deserialize, Debug, Clone)]
1351 struct Trip {
1352 id: TripID,
1353 info: TripInfo,
1354 started: bool,
1355 finished_at: Option<Time>,
1356 total_blocked_time: Duration,
1357 aborted: bool,
1358 cancelled: bool,
1359 legs: VecDeque<TripLeg>,
1360 person: PersonID,
1361 }
1362
1363 #[derive(Serialize, Deserialize, Debug, Clone)]
1364 pub struct TripInfo {
1365 // Scheduled departure; the start may be delayed if the previous trip is taking too long.
1366 pub departure: Time,
1367 pub mode: TripMode,
1368 pub start: TripEndpoint,
1369 pub end: TripEndpoint,
1370 // Did a ScenarioModifier apply to this?
1371 pub modified: bool,
1372 }
1373
1374 impl Trip {
1375 // Returns true if this succeeds. If not, trip aborted.
spawn_ped( &self, now: Time, start: SidewalkSpot, person: &Person, map: &Map, scheduler: &mut Scheduler, events: &mut Vec<Event>, ) -> bool1376 fn spawn_ped(
1377 &self,
1378 now: Time,
1379 start: SidewalkSpot,
1380 person: &Person,
1381 map: &Map,
1382 scheduler: &mut Scheduler,
1383 events: &mut Vec<Event>,
1384 ) -> bool {
1385 let walk_to = match self.legs[0] {
1386 TripLeg::Walk(ref to) => to.clone(),
1387 _ => unreachable!(),
1388 };
1389
1390 let req = PathRequest {
1391 start: start.sidewalk_pos,
1392 end: walk_to.sidewalk_pos,
1393 constraints: PathConstraints::Pedestrian,
1394 };
1395 let path = if let Some(p) = map.pathfind(req.clone()) {
1396 p
1397 } else {
1398 events.push(Event::Alert(
1399 AlertLocation::Person(self.person),
1400 format!(
1401 "Aborting {} because no path for the walking portion! {:?} to {:?}",
1402 self.id, start, walk_to
1403 ),
1404 ));
1405 return false;
1406 };
1407
1408 scheduler.push(
1409 now,
1410 Command::SpawnPed(CreatePedestrian {
1411 id: person.ped,
1412 speed: person.ped_speed,
1413 start,
1414 goal: walk_to,
1415 path,
1416 req,
1417 trip: self.id,
1418 person: self.person,
1419 }),
1420 );
1421 true
1422 }
1423
assert_walking_leg(&mut self, goal: SidewalkSpot)1424 fn assert_walking_leg(&mut self, goal: SidewalkSpot) {
1425 match self.legs.pop_front() {
1426 Some(TripLeg::Walk(spot)) => {
1427 assert_eq!(goal, spot);
1428 }
1429 _ => unreachable!(),
1430 }
1431 }
1432 }
1433
1434 // These don't specify where the leg starts, since it might be unknown -- like when we drive and
1435 // don't know where we'll wind up parking.
1436 #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
1437 pub enum TripLeg {
1438 Walk(SidewalkSpot),
1439 // A person may own many vehicles, so specify which they use
1440 Drive(CarID, DrivingGoal),
1441 // Maybe get off at a stop, maybe ride off-map
1442 RideBus(BusRouteID, Option<BusStopID>),
1443 Remote(OffMapLocation),
1444 }
1445
1446 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
1447 pub enum TripMode {
1448 Walk,
1449 Bike,
1450 Transit,
1451 Drive,
1452 }
1453
1454 impl TripMode {
all() -> Vec<TripMode>1455 pub fn all() -> Vec<TripMode> {
1456 vec![
1457 TripMode::Walk,
1458 TripMode::Bike,
1459 TripMode::Transit,
1460 TripMode::Drive,
1461 ]
1462 }
1463
verb(self) -> &'static str1464 pub fn verb(self) -> &'static str {
1465 match self {
1466 TripMode::Walk => "walk",
1467 TripMode::Bike => "bike",
1468 TripMode::Transit => "use transit",
1469 TripMode::Drive => "drive",
1470 }
1471 }
1472
1473 // If I used "present participle" in a method name, I'd never live it down.
ongoing_verb(self) -> &'static str1474 pub fn ongoing_verb(self) -> &'static str {
1475 match self {
1476 TripMode::Walk => "walking",
1477 TripMode::Bike => "biking",
1478 TripMode::Transit => "using transit",
1479 TripMode::Drive => "driving",
1480 }
1481 }
1482
noun(self) -> &'static str1483 pub fn noun(self) -> &'static str {
1484 match self {
1485 TripMode::Walk => "Pedestrian",
1486 TripMode::Bike => "Bike",
1487 TripMode::Transit => "Bus",
1488 TripMode::Drive => "Car",
1489 }
1490 }
1491
to_constraints(self) -> PathConstraints1492 pub fn to_constraints(self) -> PathConstraints {
1493 match self {
1494 TripMode::Walk => PathConstraints::Pedestrian,
1495 TripMode::Bike => PathConstraints::Bike,
1496 // TODO WRONG
1497 TripMode::Transit => PathConstraints::Bus,
1498 TripMode::Drive => PathConstraints::Car,
1499 }
1500 }
1501
from_constraints(c: PathConstraints) -> TripMode1502 pub fn from_constraints(c: PathConstraints) -> TripMode {
1503 match c {
1504 PathConstraints::Pedestrian => TripMode::Walk,
1505 PathConstraints::Bike => TripMode::Bike,
1506 // TODO The bijection breaks down... transit rider vs train vs bus...
1507 PathConstraints::Bus | PathConstraints::Train => TripMode::Transit,
1508 PathConstraints::Car => TripMode::Drive,
1509 }
1510 }
1511 }
1512
1513 #[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
1514 pub enum TripEndpoint {
1515 Bldg(BuildingID),
1516 Border(IntersectionID, Option<OffMapLocation>),
1517 }
1518
1519 impl TripEndpoint {
path_req( from: TripEndpoint, to: TripEndpoint, mode: TripMode, map: &Map, ) -> Option<PathRequest>1520 pub fn path_req(
1521 from: TripEndpoint,
1522 to: TripEndpoint,
1523 mode: TripMode,
1524 map: &Map,
1525 ) -> Option<PathRequest> {
1526 Some(PathRequest {
1527 start: pos(from, mode, true, map)?,
1528 end: pos(to, mode, false, map)?,
1529 constraints: match mode {
1530 TripMode::Walk | TripMode::Transit => PathConstraints::Pedestrian,
1531 TripMode::Drive => PathConstraints::Car,
1532 TripMode::Bike => PathConstraints::Bike,
1533 },
1534 })
1535 }
1536 }
1537
pos(endpt: TripEndpoint, mode: TripMode, from: bool, map: &Map) -> Option<Position>1538 fn pos(endpt: TripEndpoint, mode: TripMode, from: bool, map: &Map) -> Option<Position> {
1539 match endpt {
1540 TripEndpoint::Bldg(b) => match mode {
1541 TripMode::Walk | TripMode::Transit => Some(map.get_b(b).sidewalk_pos),
1542 TripMode::Bike => Some(DrivingGoal::ParkNear(b).goal_pos(PathConstraints::Bike, map)?),
1543 TripMode::Drive => Some(
1544 DrivingGoal::ParkNear(b)
1545 .goal_pos(PathConstraints::Car, map)
1546 .unwrap(),
1547 ),
1548 },
1549 TripEndpoint::Border(i, _) => match mode {
1550 TripMode::Walk | TripMode::Transit => if from {
1551 SidewalkSpot::start_at_border(i, None, map)
1552 } else {
1553 SidewalkSpot::end_at_border(i, None, map)
1554 }
1555 .map(|spot| spot.sidewalk_pos),
1556 TripMode::Bike | TripMode::Drive => (if from {
1557 map.get_i(i).some_outgoing_road(map)
1558 } else {
1559 map.get_i(i).some_incoming_road(map)
1560 })
1561 .and_then(|dr| {
1562 dr.lanes(
1563 if mode == TripMode::Bike {
1564 PathConstraints::Bike
1565 } else {
1566 PathConstraints::Car
1567 },
1568 map,
1569 )
1570 .get(0)
1571 .map(|l| Position::start(*l))
1572 }),
1573 },
1574 }
1575 }
1576
1577 pub enum TripResult<T> {
1578 Ok(T),
1579 ModeChange,
1580 TripDone,
1581 TripDoesntExist,
1582 TripNotStarted,
1583 TripAborted,
1584 TripCancelled,
1585 RemoteTrip,
1586 }
1587
1588 impl<T> TripResult<T> {
ok(self) -> Option<T>1589 pub fn ok(self) -> Option<T> {
1590 match self {
1591 TripResult::Ok(data) => Some(data),
1592 _ => None,
1593 }
1594 }
1595
propagate_error<X>(self) -> TripResult<X>1596 pub fn propagate_error<X>(self) -> TripResult<X> {
1597 match self {
1598 TripResult::Ok(_) => panic!("TripResult is Ok, can't propagate_error"),
1599 TripResult::ModeChange => TripResult::ModeChange,
1600 TripResult::TripDone => TripResult::TripDone,
1601 TripResult::TripDoesntExist => TripResult::TripDoesntExist,
1602 TripResult::TripNotStarted => TripResult::TripNotStarted,
1603 TripResult::TripAborted => TripResult::TripAborted,
1604 TripResult::TripCancelled => TripResult::TripCancelled,
1605 TripResult::RemoteTrip => TripResult::RemoteTrip,
1606 }
1607 }
1608 }
1609
1610 #[derive(Serialize, Deserialize, Debug, Clone)]
1611 pub struct Person {
1612 pub id: PersonID,
1613 pub orig_id: Option<OrigPersonID>,
1614 pub trips: Vec<TripID>,
1615 // TODO home
1616 pub state: PersonState,
1617
1618 pub ped: PedestrianID,
1619 pub ped_speed: Speed,
1620 // Both cars and bikes
1621 pub vehicles: Vec<Vehicle>,
1622
1623 delayed_trips: Vec<(TripID, TripSpec, Option<PathRequest>, Option<Path>)>,
1624 on_bus: Option<CarID>,
1625 }
1626
1627 impl Person {
get_vehicle(&self, id: CarID) -> Vehicle1628 pub(crate) fn get_vehicle(&self, id: CarID) -> Vehicle {
1629 self.vehicles.iter().find(|v| v.id == id).unwrap().clone()
1630 }
1631 }
1632
1633 #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
1634 pub enum PersonState {
1635 Trip(TripID),
1636 Inside(BuildingID),
1637 OffMap,
1638 }
1639
1640 impl TripEndpoint {
start_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot>1641 pub(crate) fn start_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot> {
1642 match self {
1643 TripEndpoint::Bldg(b) => Some(SidewalkSpot::building(*b, map)),
1644 TripEndpoint::Border(i, origin) => {
1645 SidewalkSpot::start_at_border(*i, origin.clone(), map)
1646 }
1647 }
1648 }
1649
end_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot>1650 pub(crate) fn end_sidewalk_spot(&self, map: &Map) -> Option<SidewalkSpot> {
1651 match self {
1652 TripEndpoint::Bldg(b) => Some(SidewalkSpot::building(*b, map)),
1653 TripEndpoint::Border(i, destination) => {
1654 SidewalkSpot::end_at_border(*i, destination.clone(), map)
1655 }
1656 }
1657 }
1658
driving_goal( &self, constraints: PathConstraints, map: &Map, ) -> Option<DrivingGoal>1659 pub(crate) fn driving_goal(
1660 &self,
1661 constraints: PathConstraints,
1662 map: &Map,
1663 ) -> Option<DrivingGoal> {
1664 match self {
1665 TripEndpoint::Bldg(b) => Some(DrivingGoal::ParkNear(*b)),
1666 TripEndpoint::Border(i, destination) => DrivingGoal::end_at_border(
1667 map.get_i(*i).some_incoming_road(map)?,
1668 constraints,
1669 destination.clone(),
1670 map,
1671 ),
1672 }
1673 }
1674 }
1675