mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 19:27:11 +03:00
bringing back skeleton of transit sim...
This commit is contained in:
parent
3bbcb00d43
commit
c99d9f5138
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
248
sim/src/transit.rs
Normal 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
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
Loading…
Reference in New Issue
Block a user