mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 11:44:25 +03:00
moving state to transit model, making router get directives from it
This commit is contained in:
parent
da402dd2c1
commit
c685a80a98
@ -652,13 +652,32 @@ for now, since pathfinding ignores live traffic, probably fine to ignore this.
|
||||
- this actually belongs to the map layer! associated with a sidewalk I guess.
|
||||
- render the bus in a special color, and also, make it really long (adjust following dist, but not parking spot len)
|
||||
- how to unit test that a bus has reached a stop and is waiting? how do we even know that a bus is at a stop for peds to soon board it? I think a transit state will happen soon...
|
||||
|
||||
- step 2: make some peds pick a SINGLE bus to use for their route, if it helps
|
||||
|
||||
- step 3: make peds load on the bus and get off at the correct stop. make buses usually wait a fixed time at each stop, but wait a littl extra if loading passengers takes a while.
|
||||
- will need to store transit state of what peds are on what bus somewhere... right? or can trips somehow do it? but will want to jump to a ped and spot the bus
|
||||
- should walking state own peds waiting for a bus?
|
||||
- yes: easier drawing, later need to know how crowded a sidewalk is, it's just weird to keep indicating we're at a place. router for cars does this, and the transit sim holds the higher-level state. do the same for now.
|
||||
- no: transit sim can also contribute DrawPeds. the walking layer has nothing left to do with them... right?
|
||||
|
||||
- step N: load in GTFS for seattle to get real routes and stops
|
||||
|
||||
later: multiple transfers, dedicated bus lanes, light rail...
|
||||
|
||||
Actually, jump to step 3 and just hardcode a ped to use a route, for now. what should the setup be? hardcode what stop to go to, what route to use, what stop to get off at? trip plan is a sequence...
|
||||
|
||||
- walk to a sidewalk POI (bldg, parking spot, bus stop)
|
||||
- drive somewhere and park
|
||||
- ride bus route until some stop
|
||||
|
||||
for now, these trip sequences can be hardcoded, and planned later.
|
||||
|
||||
## Everything as FSMs
|
||||
|
||||
Driving and walking layer are both kind of broken, since they know about
|
||||
parking spots and bus stops. Feels like they need to be dumb, mechanical layers
|
||||
that're guided by higher-level behaviors, which understand trips and such.
|
||||
|
||||
## Routers, lookahead, cars as FSMs
|
||||
|
||||
Hmm, hard to figure out the interactions for the router. when should it get a
|
||||
|
@ -175,6 +175,7 @@ impl Car {
|
||||
vehicle,
|
||||
map,
|
||||
parking_sim,
|
||||
transit_sim,
|
||||
);
|
||||
let dist_to_maybe_stop_at = maybe_stop_early.unwrap_or(current_on.length(map));
|
||||
let dist_from_stop = dist_to_maybe_stop_at - current_dist_along;
|
||||
|
@ -36,13 +36,16 @@ pub fn small_spawn(sim: &mut Sim, map: &Map) {
|
||||
sim.seed_walking_trips(&map, 100);
|
||||
sim.seed_driving_trips(&map, 100);
|
||||
|
||||
sim.seed_bus(
|
||||
if sim.seed_bus_route(
|
||||
vec![
|
||||
map.get_l(LaneID(309)).bus_stops[0].clone(),
|
||||
map.get_l(LaneID(840)).bus_stops[0].clone(),
|
||||
],
|
||||
map,
|
||||
).expect("Bus didn't fit");
|
||||
).len() != 2
|
||||
{
|
||||
panic!("Two buses didn't fit");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn big_spawn(sim: &mut Sim, map: &Map) {
|
||||
|
@ -62,6 +62,15 @@ impl fmt::Display for PedestrianID {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct RouteID(pub usize);
|
||||
|
||||
impl fmt::Display for RouteID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RouteID({0})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash)]
|
||||
pub enum AgentID {
|
||||
Car(CarID),
|
||||
|
@ -2,8 +2,7 @@ use dimensioned::si;
|
||||
use driving::{Action, CarView};
|
||||
use kinematics;
|
||||
use kinematics::Vehicle;
|
||||
use map_model;
|
||||
use map_model::{BuildingID, BusStop, LaneID, Map, TurnID};
|
||||
use map_model::{BuildingID, LaneID, Map, TurnID};
|
||||
use parking::ParkingSimState;
|
||||
use rand::Rng;
|
||||
use std::collections::VecDeque;
|
||||
@ -13,11 +12,7 @@ use {Distance, Event, On, ParkingSpot, Tick};
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
enum Goal {
|
||||
ParkNearBuilding(BuildingID),
|
||||
// This is stateful -- first stop is the one we're aiming for now, and we might also be waiting
|
||||
// at that stop for a little while.
|
||||
// Buses really have a few states... GotoNextStop(list of stops), WaitAtStop(list of stops,
|
||||
// tick)
|
||||
CycleThroughStops(Vec<BusStop>, Option<Tick>),
|
||||
FollowBusRoute,
|
||||
}
|
||||
|
||||
// Gives higher-level instructions to a car.
|
||||
@ -37,11 +32,10 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_router_for_bus(first_path: VecDeque<LaneID>, stops: Vec<BusStop>) -> Router {
|
||||
assert_eq!(*first_path.back().unwrap(), stops[1].driving_lane);
|
||||
pub fn make_router_for_bus(first_path: VecDeque<LaneID>) -> Router {
|
||||
Router {
|
||||
path: first_path,
|
||||
goal: Goal::CycleThroughStops(rotate_stops(&stops), None),
|
||||
goal: Goal::FollowBusRoute,
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,73 +58,32 @@ impl Router {
|
||||
transit_sim: &mut TransitSimState,
|
||||
rng: &mut R,
|
||||
) -> Option<Action> {
|
||||
if self.path.is_empty() && view.speed <= kinematics::EPSILON_SPEED {
|
||||
match self.goal {
|
||||
Goal::ParkNearBuilding(_) => {
|
||||
let last_lane = view.on.as_lane();
|
||||
if let Some(spot) =
|
||||
find_parking_spot(last_lane, view.dist_along, map, parking_sim)
|
||||
{
|
||||
if parking_sim.dist_along_for_car(spot, vehicle) == view.dist_along {
|
||||
return Some(Action::StartParking(spot));
|
||||
}
|
||||
// Being stopped before the parking spot is normal if the final road is
|
||||
// clogged with other drivers.
|
||||
} else {
|
||||
return self.look_for_parking(last_lane, view, map, rng);
|
||||
if !self.path.is_empty() || view.speed > kinematics::EPSILON_SPEED {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.goal {
|
||||
Goal::ParkNearBuilding(_) => {
|
||||
let last_lane = view.on.as_lane();
|
||||
if let Some(spot) = find_parking_spot(last_lane, view.dist_along, map, parking_sim)
|
||||
{
|
||||
if parking_sim.dist_along_for_car(spot, vehicle) == view.dist_along {
|
||||
return Some(Action::StartParking(spot));
|
||||
}
|
||||
// Being stopped before the parking spot is normal if the final road is
|
||||
// clogged with other drivers.
|
||||
} else {
|
||||
return self.look_for_parking(last_lane, view, map, rng);
|
||||
}
|
||||
Goal::CycleThroughStops(ref mut stops, ref mut wait_until) => {
|
||||
if view.dist_along == stops[0].dist_along {
|
||||
if let Some(wait) = wait_until.clone() {
|
||||
if time == wait {
|
||||
// TODO is this the right place to actually do the state
|
||||
// transition, or should we just indicate an event and let
|
||||
// something else handle it?
|
||||
events.push(Event::BusDepartedFromStop(view.id, stops[0].clone()));
|
||||
|
||||
if view.debug || true {
|
||||
println!(
|
||||
"{} finished waiting at bus stop, going to next stop {:?}",
|
||||
view.id, stops[1]
|
||||
);
|
||||
}
|
||||
|
||||
let mut new_path = VecDeque::from(
|
||||
map_model::pathfind(
|
||||
map,
|
||||
stops[0].driving_lane,
|
||||
stops[1].driving_lane,
|
||||
).expect(&format!(
|
||||
"No route between bus stops {:?} and {:?}",
|
||||
stops[0], stops[1]
|
||||
)),
|
||||
);
|
||||
new_path.pop_front();
|
||||
self.path = new_path;
|
||||
|
||||
let new_stops = rotate_stops(stops);
|
||||
stops.clear();
|
||||
stops.extend(new_stops);
|
||||
*wait_until = None;
|
||||
|
||||
transit_sim.bus_is_driving(view.id);
|
||||
}
|
||||
} else {
|
||||
assert_eq!(view.on.as_lane(), stops[0].driving_lane);
|
||||
events.push(Event::BusArrivedAtStop(view.id, stops[0].clone()));
|
||||
// TODO const
|
||||
*wait_until = Some(time + 10.0 * si::S);
|
||||
if view.debug || true {
|
||||
println!(
|
||||
"{} reached {:?}, now waiting until {:?}",
|
||||
view.id, stops[0], wait_until
|
||||
);
|
||||
}
|
||||
transit_sim.bus_is_at_stop(view.id, stops[0].clone());
|
||||
return Some(Action::Continue(0.0 * si::MPS2, Vec::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Goal::FollowBusRoute => {
|
||||
let (should_idle, new_path) =
|
||||
transit_sim.get_action_when_stopped_at_end(events, view, time, map);
|
||||
if let Some(p) = new_path {
|
||||
self.path = p;
|
||||
}
|
||||
if should_idle {
|
||||
return Some(Action::Continue(0.0 * si::MPS2, Vec::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,6 +99,7 @@ impl Router {
|
||||
vehicle: &Vehicle,
|
||||
map: &Map,
|
||||
parking_sim: &ParkingSimState,
|
||||
transit_sim: &TransitSimState,
|
||||
) -> Option<Distance> {
|
||||
if self.path.is_empty() {
|
||||
match self.goal {
|
||||
@ -160,8 +114,8 @@ impl Router {
|
||||
return Some(on.length(map));
|
||||
}
|
||||
}
|
||||
Goal::CycleThroughStops(ref stops, _) => {
|
||||
return Some(stops[0].dist_along);
|
||||
Goal::FollowBusRoute => {
|
||||
return Some(transit_sim.get_dist_to_stop_at(vehicle.id, on.as_lane()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,10 +175,3 @@ fn find_parking_spot(
|
||||
.find_parking_lane(driving_lane)
|
||||
.and_then(|l| parking_sim.get_first_free_spot(l, dist_along))
|
||||
}
|
||||
|
||||
// TODO double-ended list can do this natively
|
||||
fn rotate_stops(stops: &Vec<BusStop>) -> Vec<BusStop> {
|
||||
let mut cycled_stops: Vec<BusStop> = stops[1..].to_vec();
|
||||
cycled_stops.push(stops[0].clone());
|
||||
cycled_stops
|
||||
}
|
||||
|
@ -115,10 +115,11 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seed_bus(&mut self, stops: Vec<BusStop>, map: &Map) -> Option<CarID> {
|
||||
pub fn seed_bus_route(&mut self, stops: Vec<BusStop>, map: &Map) -> Vec<CarID> {
|
||||
// TODO throw away the events? :(
|
||||
let mut events: Vec<Event> = Vec::new();
|
||||
if let Some(v) = self.spawner.seed_bus(
|
||||
let mut result: Vec<CarID> = Vec::new();
|
||||
for v in self.spawner.seed_bus_route(
|
||||
&mut events,
|
||||
stops,
|
||||
&mut self.rng,
|
||||
@ -130,10 +131,9 @@ impl Sim {
|
||||
) {
|
||||
let id = v.id;
|
||||
self.car_properties.insert(v.id, v);
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
result.push(id);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn seed_specific_parked_cars(&mut self, lane: LaneID, spots: Vec<usize>) -> Vec<CarID> {
|
||||
|
@ -168,7 +168,7 @@ impl Spawner {
|
||||
}
|
||||
|
||||
// This happens immediately; it isn't scheduled.
|
||||
pub fn seed_bus<R: Rng + ?Sized>(
|
||||
pub fn seed_bus_route<R: Rng + ?Sized>(
|
||||
&mut self,
|
||||
events: &mut Vec<Event>,
|
||||
stops: Vec<BusStop>,
|
||||
@ -178,38 +178,40 @@ impl Spawner {
|
||||
transit_sim: &mut TransitSimState,
|
||||
now: Tick,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Option<Vehicle> {
|
||||
assert!(stops.len() > 1);
|
||||
let id = CarID(self.car_id_counter);
|
||||
self.car_id_counter += 1;
|
||||
let vehicle = Vehicle::generate_bus(id, rng);
|
||||
) -> Vec<Vehicle> {
|
||||
let route = transit_sim.create_empty_route(stops);
|
||||
let mut vehicles: Vec<Vehicle> = Vec::new();
|
||||
// Try to spawn a bus at each stop
|
||||
for (next_stop_idx, start_dist_along, mut path) in
|
||||
transit_sim.get_route_starts(route, map).into_iter()
|
||||
{
|
||||
let id = CarID(self.car_id_counter);
|
||||
self.car_id_counter += 1;
|
||||
let vehicle = Vehicle::generate_bus(id, rng);
|
||||
|
||||
let mut first_path = VecDeque::from(
|
||||
map_model::pathfind(map, stops[0].driving_lane, stops[1].driving_lane).expect(
|
||||
&format!(
|
||||
"No route between bus stops {:?} and {:?}",
|
||||
stops[0], stops[1]
|
||||
),
|
||||
),
|
||||
);
|
||||
let start = first_path.pop_front().unwrap();
|
||||
if driving_sim.start_car_on_lane(
|
||||
events,
|
||||
now,
|
||||
id,
|
||||
None,
|
||||
stops[0].dist_along,
|
||||
start,
|
||||
Router::make_router_for_bus(first_path, stops),
|
||||
map,
|
||||
properties,
|
||||
) {
|
||||
transit_sim.bus_is_driving(id);
|
||||
println!("Spawned bus {}", id);
|
||||
return Some(vehicle);
|
||||
let start = path.pop_front().unwrap();
|
||||
if driving_sim.start_car_on_lane(
|
||||
events,
|
||||
now,
|
||||
id,
|
||||
None,
|
||||
start_dist_along,
|
||||
start,
|
||||
Router::make_router_for_bus(path),
|
||||
map,
|
||||
properties,
|
||||
) {
|
||||
transit_sim.bus_created(id, route, next_stop_idx);
|
||||
println!("Spawned bus {} for route {}", id, route);
|
||||
vehicles.push(vehicle);
|
||||
} else {
|
||||
println!(
|
||||
"No room for a bus headed towards stop {} of {}, giving up",
|
||||
next_stop_idx, route
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("No room for bus, giving up");
|
||||
None
|
||||
vehicles
|
||||
}
|
||||
|
||||
// This happens immediately; it isn't scheduled.
|
||||
|
@ -1,31 +1,181 @@
|
||||
use map_model::BusStop;
|
||||
use std::collections::BTreeMap;
|
||||
use CarID;
|
||||
use dimensioned::si;
|
||||
use driving::CarView;
|
||||
use events::Event;
|
||||
use map_model;
|
||||
use map_model::{BusStop, LaneID, Map};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use {CarID, Distance, PedestrianID, RouteID, Tick};
|
||||
|
||||
type StopIdx = usize;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
struct Route {
|
||||
id: RouteID,
|
||||
buses: Vec<CarID>,
|
||||
stops: Vec<BusStop>,
|
||||
// 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, Eq)]
|
||||
struct Bus {
|
||||
car: CarID,
|
||||
route: RouteID,
|
||||
passengers: Vec<PedestrianID>,
|
||||
state: BusState,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
enum BusState {
|
||||
Driving,
|
||||
AtStop(BusStop),
|
||||
DrivingToStop(StopIdx),
|
||||
// When do we leave?
|
||||
AtStop(StopIdx, Tick),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct TransitSimState {
|
||||
buses: BTreeMap<CarID, BusState>,
|
||||
buses: BTreeMap<CarID, Bus>,
|
||||
routes: BTreeMap<RouteID, Route>,
|
||||
}
|
||||
|
||||
impl TransitSimState {
|
||||
pub fn new() -> TransitSimState {
|
||||
TransitSimState {
|
||||
buses: BTreeMap::new(),
|
||||
routes: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Transitions
|
||||
pub fn bus_is_driving(&mut self, bus: CarID) {
|
||||
self.buses.insert(bus, BusState::Driving);
|
||||
pub fn create_empty_route(&mut self, stops: Vec<BusStop>) -> RouteID {
|
||||
assert!(stops.len() > 1);
|
||||
let id = RouteID(self.routes.len());
|
||||
self.routes.insert(
|
||||
id,
|
||||
Route {
|
||||
id,
|
||||
buses: Vec::new(),
|
||||
stops: stops.clone(),
|
||||
},
|
||||
);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn bus_is_at_stop(&mut self, bus: CarID, stop: BusStop) {
|
||||
self.buses.insert(bus, BusState::AtStop(stop));
|
||||
// (next stop, start distance, first path)
|
||||
pub fn get_route_starts(
|
||||
&self,
|
||||
id: RouteID,
|
||||
map: &Map,
|
||||
) -> Vec<(StopIdx, Distance, VecDeque<LaneID>)> {
|
||||
let route = &self.routes[&id];
|
||||
route
|
||||
.stops
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, stop1)| {
|
||||
let next_stop = route.next_stop(idx);
|
||||
let stop2 = &route.stops[next_stop];
|
||||
let path = VecDeque::from(
|
||||
map_model::pathfind(map, stop1.driving_lane, stop2.driving_lane).expect(
|
||||
&format!("No route between bus stops {:?} and {:?}", stop1, stop2),
|
||||
),
|
||||
);
|
||||
(next_stop, stop1.dist_along, path)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn bus_created(&mut self, bus: CarID, route: RouteID, 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: &CarView,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
) -> (bool, Option<VecDeque<LaneID>>) {
|
||||
let route = &self.routes[&self.buses[&view.id].route];
|
||||
match self.buses[&view.id].state {
|
||||
BusState::DrivingToStop(stop_idx) => {
|
||||
let stop = &route.stops[stop_idx];
|
||||
assert_eq!(stop.driving_lane, view.on.as_lane());
|
||||
if stop.dist_along == view.dist_along {
|
||||
// TODO constant for stop time
|
||||
self.buses.get_mut(&view.id).unwrap().state =
|
||||
BusState::AtStop(stop_idx, time + 10.0 * si::S);
|
||||
events.push(Event::BusArrivedAtStop(view.id, stop.clone()));
|
||||
if view.debug {
|
||||
println!("{} arrived at stop {:?}, now waiting", view.id, stop);
|
||||
}
|
||||
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_lane, view.on.as_lane());
|
||||
assert_eq!(stop.dist_along, view.dist_along);
|
||||
|
||||
if time == wait_until {
|
||||
let next_stop = route.next_stop(stop_idx);
|
||||
self.buses.get_mut(&view.id).unwrap().state =
|
||||
BusState::DrivingToStop(next_stop);
|
||||
events.push(Event::BusDepartedFromStop(view.id, stop.clone()));
|
||||
if view.debug {
|
||||
println!("{} departing from stop {:?}", view.id, stop);
|
||||
}
|
||||
|
||||
let mut new_path = VecDeque::from(
|
||||
map_model::pathfind(
|
||||
map,
|
||||
stop.driving_lane,
|
||||
route.stops[next_stop].driving_lane,
|
||||
).expect(&format!(
|
||||
"No route between bus stops {:?} and {:?}",
|
||||
stop, route.stops[next_stop]
|
||||
)),
|
||||
);
|
||||
new_path.pop_front();
|
||||
|
||||
return (true, Some(new_path));
|
||||
}
|
||||
|
||||
(true, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dist_to_stop_at(&self, bus: CarID, driving_lane: LaneID) -> Distance {
|
||||
match self.buses[&bus].state {
|
||||
BusState::DrivingToStop(stop_idx) => {
|
||||
let stop = &self.routes[&self.buses[&bus].route].stops[stop_idx];
|
||||
assert_eq!(stop.driving_lane, driving_lane);
|
||||
stop.dist_along
|
||||
}
|
||||
BusState::AtStop(_, _) => {
|
||||
panic!("Shouldn't ask where to stop if the bus is already at a stop")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,18 +14,19 @@ fn bus_reaches_stops() {
|
||||
|
||||
let stop1 = map.get_l(map_model::LaneID(309)).bus_stops[0].clone();
|
||||
let stop2 = map.get_l(map_model::LaneID(840)).bus_stops[0].clone();
|
||||
let bus = sim.seed_bus(vec![stop1.clone(), stop2.clone()], &map)
|
||||
.unwrap();
|
||||
let buses = sim.seed_bus_route(vec![stop1.clone(), stop2.clone()], &map);
|
||||
let (bus1, bus2) = (buses[0], buses[1]);
|
||||
|
||||
sim::init::run_until_expectations_met(
|
||||
&mut sim,
|
||||
&map,
|
||||
&control_map,
|
||||
// TODO assert stuff about bus2 as well, although the timing is a little unclear
|
||||
vec![
|
||||
sim::Event::BusArrivedAtStop(bus, stop2.clone()),
|
||||
sim::Event::BusDepartedFromStop(bus, stop2),
|
||||
sim::Event::BusArrivedAtStop(bus, stop1.clone()),
|
||||
sim::Event::BusDepartedFromStop(bus, stop1),
|
||||
sim::Event::BusArrivedAtStop(bus1, stop2.clone()),
|
||||
sim::Event::BusDepartedFromStop(bus1, stop2),
|
||||
sim::Event::BusArrivedAtStop(bus1, stop1.clone()),
|
||||
sim::Event::BusDepartedFromStop(bus1, stop1),
|
||||
],
|
||||
sim::Tick::from_minutes(10),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user