bringing back skeleton of transit sim...

This commit is contained in:
Dustin Carlino 2019-03-01 12:35:44 -08:00
parent 3bbcb00d43
commit c99d9f5138
4 changed files with 336 additions and 6 deletions

View File

@ -6,6 +6,7 @@ mod render;
mod router;
mod scheduler;
mod sim;
mod transit;
mod trips;
pub use self::events::Event;
@ -20,6 +21,7 @@ pub use self::query::{Benchmark, ScoreSummary, SimStats, Summary};
pub(crate) use self::router::{ActionAtEnd, Router};
pub(crate) use self::scheduler::{Command, Scheduler};
pub use self::sim::Sim;
pub(crate) use self::transit::TransitSimState;
pub(crate) use self::trips::{TripLeg, TripManager};
pub use crate::render::{CarStatus, DrawCarInput, DrawPedestrianInput, GetDrawAgents};
use abstutil::Cloneable;

View File

@ -32,6 +32,9 @@ enum Goal {
BikeThenStop {
end_dist: Distance,
},
FollowBusRoute {
end_dist: Distance,
},
}
impl Router {
@ -59,6 +62,13 @@ impl Router {
}
}
pub fn follow_bus_route(path: Path, end_dist: Distance) -> Router {
Router {
path,
goal: Goal::FollowBusRoute { end_dist },
}
}
pub fn head(&self) -> Traversable {
self.path.current_step().as_traversable()
}
@ -78,6 +88,7 @@ impl Router {
Goal::EndAtBorder { end_dist, .. } => end_dist,
Goal::ParkNearBuilding { spot, .. } => spot.unwrap().1,
Goal::BikeThenStop { end_dist } => end_dist,
Goal::FollowBusRoute { end_dist } => end_dist,
}
}
@ -157,6 +168,14 @@ impl Router {
None
}
}
Goal::FollowBusRoute { end_dist } => {
if end_dist == front {
// TODO mark when they reach the stop, start waiting, replan, etc
None
} else {
None
}
}
}
}

View File

@ -1,8 +1,9 @@
use crate::{
AgentID, Benchmark, CarID, DrawCarInput, DrawPedestrianInput, DrivingGoal, DrivingSimState,
Event, GetDrawAgents, IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot,
PedestrianID, Scheduler, ScoreSummary, SimStats, Summary, TripID, TripManager, TripSpawner,
TripSpec, VehicleSpec, VehicleType, WalkingSimState, TIMESTEP,
AgentID, Benchmark, CarID, CreateCar, DrawCarInput, DrawPedestrianInput, DrivingGoal,
DrivingSimState, Event, GetDrawAgents, IntersectionSimState, ParkedCar, ParkingSimState,
ParkingSpot, PedestrianID, Router, Scheduler, ScoreSummary, SimStats, Summary, TransitSimState,
TripID, TripLeg, TripManager, TripSpawner, TripSpec, VehicleSpec, VehicleType, WalkingSimState,
BUS_LENGTH, TIMESTEP,
};
use abstutil::Timer;
use derivative::Derivative;
@ -23,6 +24,7 @@ pub struct Sim {
parking: ParkingSimState,
walking: WalkingSimState,
intersections: IntersectionSimState,
transit: TransitSimState,
trips: TripManager,
scheduler: Scheduler,
spawner: TripSpawner,
@ -53,6 +55,7 @@ impl Sim {
parking: ParkingSimState::new(map),
walking: WalkingSimState::new(),
intersections: IntersectionSimState::new(map),
transit: TransitSimState::new(),
trips: TripManager::new(),
scheduler: Scheduler::new(),
spawner: TripSpawner::new(),
@ -145,8 +148,66 @@ impl Sim {
}
pub fn seed_bus_route(&mut self, route: &BusRoute, map: &Map, timer: &mut Timer) -> Vec<CarID> {
// TODO implement
Vec::new()
let mut results: Vec<CarID> = Vec::new();
// Try to spawn a bus at each stop
for (next_stop_idx, start_dist, path, end_dist) in
self.transit.create_empty_route(route, map).into_iter()
{
// For now, no desire for randomness. Caller can pass in list of specs if that ever
// changes.
let vehicle_spec = VehicleSpec {
vehicle_type: VehicleType::Bus,
length: BUS_LENGTH,
max_speed: None,
};
// TODO Do this validation more up-front in the map layer
if start_dist < vehicle_spec.length {
timer.warn(format!(
"Stop at {:?} is too short to spawn a bus there; giving up on one bus for {}",
path.current_step(),
route.id
));
continue;
}
let id = CarID(self.car_id_counter, VehicleType::Bus);
self.car_id_counter += 1;
// Bypass some layers of abstraction that don't make sense for buses.
// TODO Aww, we create an orphan trip if the bus can't spawn.
let trip =
self.trips
.new_trip(self.time, None, vec![TripLeg::ServeBusRoute(id, route.id)]);
if self.driving.start_car_on_lane(
self.time,
CreateCar {
vehicle: vehicle_spec.make(id, None),
router: Router::follow_bus_route(path, end_dist),
start_dist,
maybe_parked_car: None,
trip,
},
map,
&self.intersections,
) {
self.trips.agent_starting_trip_leg(AgentID::Car(id), trip);
self.transit.bus_created(id, route.id, next_stop_idx);
timer.note(format!(
"Spawned bus {} for route {} ({})",
id, route.name, route.id
));
results.push(id);
} else {
timer.warn(format!(
"No room for a bus headed towards stop {} of {} ({}), giving up",
next_stop_idx, route.name, route.id
));
}
}
results
}
}

248
sim/src/transit.rs Normal file
View File

@ -0,0 +1,248 @@
use crate::{CarID, Event, PedestrianID};
use abstutil::{deserialize_btreemap, serialize_btreemap};
use geom::{Distance, Duration};
use map_model::{BusRoute, BusRouteID, BusStop, Map, Path, PathRequest, Pathfinder};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
// These index stops along a route, not stops along a single sidewalk.
type StopIdx = usize;
#[derive(Serialize, Deserialize, PartialEq)]
struct Route {
// Just copy the info over here from map_model for convenience
id: BusRouteID,
name: String,
stops: Vec<BusStop>,
buses: Vec<CarID>,
// TODO info on schedules
}
impl Route {
fn next_stop(&self, idx: StopIdx) -> StopIdx {
if idx + 1 == self.stops.len() {
0
} else {
idx + 1
}
}
}
#[derive(Serialize, Deserialize, PartialEq)]
struct Bus {
car: CarID,
route: BusRouteID,
passengers: Vec<PedestrianID>,
state: BusState,
}
#[derive(Serialize, Deserialize, PartialEq)]
enum BusState {
DrivingToStop(StopIdx),
// When do we leave?
AtStop(StopIdx, Duration),
}
// This kind of acts like TripManager, managing transitions... but a bit more statefully.
#[derive(Serialize, Deserialize, PartialEq)]
pub struct TransitSimState {
#[serde(
serialize_with = "serialize_btreemap",
deserialize_with = "deserialize_btreemap"
)]
buses: BTreeMap<CarID, Bus>,
#[serde(
serialize_with = "serialize_btreemap",
deserialize_with = "deserialize_btreemap"
)]
routes: BTreeMap<BusRouteID, Route>,
}
impl TransitSimState {
pub fn new() -> TransitSimState {
TransitSimState {
buses: BTreeMap::new(),
routes: BTreeMap::new(),
}
}
// Returns (next stop, start distance on the driving lane, first path, end distance for next
// stop) for all of the stops in the route.
pub fn create_empty_route(
&mut self,
route: &BusRoute,
map: &Map,
) -> Vec<(StopIdx, Distance, Path, Distance)> {
assert!(route.stops.len() > 1);
let route = Route {
id: route.id,
name: route.name.clone(),
stops: route.stops.iter().map(|s| map.get_bs(*s).clone()).collect(),
buses: Vec::new(),
};
let stops = route
.stops
.iter()
.enumerate()
.map(|(idx, stop1)| {
let next_stop = route.next_stop(idx);
let stop2 = &route.stops[next_stop];
let path = Pathfinder::shortest_distance(
map,
PathRequest {
start: stop1.driving_pos,
end: stop2.driving_pos,
can_use_bike_lanes: false,
can_use_bus_lanes: true,
},
)
.expect(&format!(
"No route between bus stops {:?} and {:?}",
stop1, stop2
));
(
next_stop,
stop1.driving_pos.dist_along(),
path,
stop2.driving_pos.dist_along(),
)
})
.collect();
self.routes.insert(route.id, route);
stops
}
pub fn bus_created(&mut self, bus: CarID, route: BusRouteID, next_stop_idx: StopIdx) {
self.routes.get_mut(&route).unwrap().buses.push(bus);
self.buses.insert(
bus,
Bus {
car: bus,
route,
passengers: Vec::new(),
state: BusState::DrivingToStop(next_stop_idx),
},
);
}
/*
// Returns (should idle, new path)
pub fn get_action_when_stopped_at_end(
&mut self,
events: &mut Vec<Event>,
view: &AgentView,
time: Tick,
map: &Map,
) -> (bool, Option<Path>) {
let car = view.id.as_car();
let route = &self.routes[&self.buses[&car].route];
match self.buses[&car].state {
BusState::DrivingToStop(stop_idx) => {
let stop = &route.stops[stop_idx];
assert_eq!(stop.driving_pos.lane(), view.on.as_lane());
if stop.driving_pos.dist_along() == view.dist_along {
if !view.speed.is_zero(TIMESTEP) {
panic!(
"{} arrived at stop {}, but speed is {}",
car, stop.id, view.speed
);
}
// TODO constant for stop time
self.buses.get_mut(&car).unwrap().state =
BusState::AtStop(stop_idx, time + Duration::seconds(10.0));
events.push(Event::BusArrivedAtStop(car, stop.id));
return (true, None);
}
// No, keep creeping forwards
(false, None)
}
BusState::AtStop(stop_idx, wait_until) => {
let stop = &route.stops[stop_idx];
assert_eq!(stop.driving_pos.lane(), view.on.as_lane());
if stop.driving_pos.dist_along() != view.dist_along {
panic!(
"{} stopped at {}, but dist_along is {}, not {}. Speed is {}",
car,
stop.id,
view.dist_along,
stop.driving_pos.dist_along(),
view.speed
);
}
assert_eq!(stop.driving_pos.dist_along(), view.dist_along);
if time == wait_until {
let next_stop = route.next_stop(stop_idx);
self.buses.get_mut(&car).unwrap().state = BusState::DrivingToStop(next_stop);
events.push(Event::BusDepartedFromStop(car, stop.id));
let new_path = Pathfinder::shortest_distance(
map,
PathRequest {
start: stop.driving_pos,
end: route.stops[next_stop].driving_pos,
can_use_bike_lanes: false,
can_use_bus_lanes: true,
},
)
.expect(&format!(
"No route between bus stops {:?} and {:?}",
stop, route.stops[next_stop]
));
return (true, Some(new_path));
}
(true, None)
}
}
}
pub fn step(
&mut self,
now: Tick,
events: &mut Vec<Event>,
walking_sim: &mut WalkingSimState,
trips: &mut TripManager,
spawner: &mut Spawner,
map: &Map,
) {
for b in self.buses.values_mut() {
if let BusState::AtStop(stop_idx, _) = b.state {
let stop = &self.routes[&b.route].stops[stop_idx];
// Let anybody new on?
for p in walking_sim.get_peds_waiting_at_stop(stop.id).into_iter() {
if trips.should_ped_board_bus(p, b.route) {
events.push(Event::PedEntersBus(p, b.car));
b.passengers.push(p);
walking_sim.ped_joined_bus(p, stop.id);
}
}
// Let anybody off?
// TODO ideally dont even ask if they just got on, but the trip planner things
// should be fine with this
// TODO only do this if we JUST arrived at the stop, and in fact, wait for everyone
// to leave, since it may take time.
// so actually, we shouldnt statechange mutably in get_action_when_stopped_at_end,
// which is called by router! thats convoluted
let car = b.car;
b.passengers.retain(|p| {
if trips.should_ped_leave_bus(*p, stop.id) {
events.push(Event::PedLeavesBus(*p, car));
// TODO would be a little cleaner to return this info up to sim and have it
// plumb through to spawner? not sure
spawner.ped_finished_bus_ride(now, *p, stop.id, trips, map);
false
} else {
true
}
});
}
}
}*/
}