make TripSpawner a temporary setup object, not something permanently stored in Sim

This commit is contained in:
Dustin Carlino 2020-03-12 15:00:50 -07:00
parent f20b7912eb
commit d852d69535
4 changed files with 152 additions and 100 deletions

View File

@ -20,7 +20,8 @@ use rand::seq::SliceRandom;
use rand::Rng;
use rand_xorshift::XorShiftRng;
use sim::{
BorderSpawnOverTime, DrivingGoal, OriginDestination, Scenario, SidewalkSpot, Sim, TripSpec,
BorderSpawnOverTime, DrivingGoal, OriginDestination, Scenario, SidewalkSpot, Sim, TripSpawner,
TripSpec,
};
const SMALL_DT: Duration = Duration::const_seconds(0.1);
@ -270,14 +271,16 @@ impl State for AgentSpawner {
if self.maybe_goal.is_some() && app.per_obj.left_click(ctx, "end the agent here") {
let mut rng = app.primary.current_flags.sim_flags.make_rng();
let sim = &mut app.primary.sim;
let mut spawner = sim.make_spawner();
let err = schedule_trip(
&self.from,
self.maybe_goal.take().unwrap().0,
map,
sim,
&mut spawner,
&mut rng,
);
sim.spawn_all_trips(map, &mut Timer::new("spawn trip"), false);
sim.flush_spawner(spawner, map, &mut Timer::new("spawn trip"), false);
sim.normal_step(map, SMALL_DT);
app.recalculate_current_selection(ctx);
if let Some(e) = err {
@ -309,6 +312,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
let map = &app.primary.map;
let sim = &mut app.primary.sim;
let mut rng = app.primary.current_flags.sim_flags.make_rng();
let mut spawner = sim.make_spawner();
if map.all_buildings().is_empty() {
println!("No buildings, can't pick destinations");
@ -332,7 +336,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
if vehicle_spec.length > lane.length() {
continue;
}
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::CarAppearing {
start_pos: Position::new(
@ -346,11 +350,12 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
ped_speed: Scenario::rand_ped_speed(&mut rng),
},
map,
sim,
);
}
} else if lane.is_sidewalk() {
for _ in 0..5 {
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::JustWalking {
start: SidewalkSpot::suddenly_appear(
@ -365,12 +370,13 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
ped_speed: Scenario::rand_ped_speed(&mut rng),
},
map,
sim,
);
}
}
}
sim.spawn_all_trips(map, &mut timer, false);
sim.flush_spawner(spawner, map, &mut timer, false);
sim.normal_step(map, SMALL_DT);
}
@ -379,7 +385,8 @@ fn schedule_trip(
src: &Source,
raw_goal: Goal,
map: &Map,
sim: &mut Sim,
sim: &Sim,
spawner: &mut TripSpawner,
rng: &mut XorShiftRng,
) -> Option<String> {
match src {
@ -406,7 +413,7 @@ fn schedule_trip(
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
println!("Using {} from {} to {}", route, stop1, stop2);
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::UsingTransit {
start,
@ -417,10 +424,11 @@ fn schedule_trip(
ped_speed,
},
map,
sim,
);
} else {
println!("Not using transit");
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::JustWalking {
start,
@ -428,6 +436,7 @@ fn schedule_trip(
ped_speed,
},
map,
sim,
);
}
}
@ -446,7 +455,7 @@ fn schedule_trip(
}
}
};
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::UsingBike {
start: SidewalkSpot::building(*b, map),
@ -455,6 +464,7 @@ fn schedule_trip(
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
}
_ => {
@ -476,7 +486,7 @@ fn schedule_trip(
match src {
Source::Drive(from) => {
if let Some(start_pos) = TripSpec::spawn_car_at(*from, map) {
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::CarAppearing {
start_pos,
@ -485,13 +495,14 @@ fn schedule_trip(
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
} else {
return Some(format!("Can't make a car appear at {:?}", from));
}
}
Source::WalkFromBldgThenMaybeUseCar(b) => {
sim.schedule_trip(
spawner.schedule_trip(
sim.time(),
TripSpec::MaybeUsingParkedCar {
start_bldg: *b,
@ -499,6 +510,7 @@ fn schedule_trip(
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
}
_ => unreachable!(),

View File

@ -1,3 +1,4 @@
use crate::make::TripSpawner;
use crate::{
CarID, DrivingGoal, ParkingSpot, PersonID, SidewalkSpot, Sim, TripSpec, VehicleSpec,
VehicleType, BIKE_LENGTH, MAX_CAR_LENGTH, MIN_CAR_LENGTH,
@ -104,6 +105,8 @@ impl Scenario {
);
}
let mut spawner = sim.make_spawner();
// Don't let two pedestrians starting from one building use the same car.
let mut reserved_cars: HashSet<CarID> = HashSet::new();
@ -115,16 +118,24 @@ impl Scenario {
timer.start_iter("SpawnOverTime each agent", s.num_agents);
for _ in 0..s.num_agents {
timer.next();
s.spawn_agent(rng, sim, &mut reserved_cars, &neighborhoods, map, timer);
s.spawn_agent(
rng,
sim,
&mut spawner,
&mut reserved_cars,
&neighborhoods,
map,
timer,
);
}
}
timer.start_iter("BorderSpawnOverTime", self.border_spawn_over_time.len());
for s in &self.border_spawn_over_time {
timer.next();
s.spawn_peds(rng, sim, &neighborhoods, map, timer);
s.spawn_cars(rng, sim, &neighborhoods, map, timer);
s.spawn_bikes(rng, sim, &neighborhoods, map, timer);
s.spawn_peds(rng, &mut spawner, &neighborhoods, map, sim, timer);
s.spawn_cars(rng, &mut spawner, &neighborhoods, map, sim, timer);
s.spawn_bikes(rng, &mut spawner, &neighborhoods, map, sim, timer);
}
let mut individ_parked_cars: Vec<(BuildingID, usize)> = Vec::new();
@ -140,10 +151,10 @@ impl Scenario {
for t in &self.population.individ_trips {
timer.next();
let spec = t.trip.clone().to_trip_spec(rng);
sim.schedule_trip(t.depart, spec, map);
spawner.schedule_trip(t.depart, spec, map, sim);
}
sim.spawn_all_trips(map, timer, true);
sim.flush_spawner(spawner, map, timer, true);
timer.stop(format!("Instantiating {}", self.scenario_name));
}
@ -305,7 +316,8 @@ impl SpawnOverTime {
fn spawn_agent(
&self,
rng: &mut XorShiftRng,
sim: &mut Sim,
sim: &Sim,
spawner: &mut TripSpawner,
reserved_cars: &mut HashSet<CarID>,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
@ -331,7 +343,7 @@ impl SpawnOverTime {
{
reserved_cars.insert(parked_car.vehicle.id);
let spot = parked_car.spot;
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::UsingParkedCar {
start: SidewalkSpot::building(from_bldg, map),
@ -340,6 +352,7 @@ impl SpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
return;
}
@ -366,7 +379,7 @@ impl SpawnOverTime {
true
};
if ok {
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::UsingBike {
start: SidewalkSpot::building(from_bldg, map),
@ -375,6 +388,7 @@ impl SpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
return;
}
@ -395,7 +409,7 @@ impl SpawnOverTime {
if let Some((stop1, stop2, route)) =
map.should_use_transit(start_spot.sidewalk_pos, goal.sidewalk_pos)
{
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::UsingTransit {
start: start_spot,
@ -406,12 +420,13 @@ impl SpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
return;
}
}
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::JustWalking {
start: start_spot,
@ -419,6 +434,7 @@ impl SpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
return;
}
@ -431,9 +447,10 @@ impl BorderSpawnOverTime {
fn spawn_peds(
&self,
rng: &mut XorShiftRng,
sim: &mut Sim,
spawner: &mut TripSpawner,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
sim: &Sim,
timer: &mut Timer,
) {
if self.num_peds == 0 {
@ -461,7 +478,7 @@ impl BorderSpawnOverTime {
if let Some((stop1, stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::UsingTransit {
start: start.clone(),
@ -472,12 +489,13 @@ impl BorderSpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
continue;
}
}
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::JustWalking {
start: start.clone(),
@ -485,6 +503,7 @@ impl BorderSpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
}
}
@ -493,9 +512,10 @@ impl BorderSpawnOverTime {
fn spawn_cars(
&self,
rng: &mut XorShiftRng,
sim: &mut Sim,
spawner: &mut TripSpawner,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
sim: &Sim,
timer: &mut Timer,
) {
if self.num_cars == 0 {
@ -521,7 +541,7 @@ impl BorderSpawnOverTime {
.pick_driving_goal(PathConstraints::Car, map, &neighborhoods, rng, timer)
{
let vehicle = Scenario::rand_car(rng);
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::CarAppearing {
start_pos: Position::new(*lanes.choose(rng).unwrap(), vehicle.length),
@ -530,6 +550,7 @@ impl BorderSpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
}
}
@ -538,9 +559,10 @@ impl BorderSpawnOverTime {
fn spawn_bikes(
&self,
rng: &mut XorShiftRng,
sim: &mut Sim,
spawner: &mut TripSpawner,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
sim: &Sim,
timer: &mut Timer,
) {
if self.num_bikes == 0 {
@ -566,7 +588,7 @@ impl BorderSpawnOverTime {
.pick_driving_goal(PathConstraints::Bike, map, &neighborhoods, rng, timer)
{
let bike = Scenario::rand_bike(rng);
sim.schedule_trip(
spawner.schedule_trip(
spawn_time,
TripSpec::CarAppearing {
start_pos: Position::new(*lanes.choose(rng).unwrap(), bike.length),
@ -575,6 +597,7 @@ impl BorderSpawnOverTime {
ped_speed: Scenario::rand_ped_speed(rng),
},
map,
sim,
);
}
}

View File

@ -1,7 +1,7 @@
use crate::{
CarID, Command, CreateCar, CreatePedestrian, DrivingGoal, ParkingSimState, ParkingSpot,
PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, TripLeg, TripManager, TripStart,
VehicleSpec, MAX_CAR_LENGTH,
PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, Sim, TripLeg, TripManager, TripStart,
VehicleSpec, VehicleType, MAX_CAR_LENGTH,
};
use abstutil::Timer;
use geom::{Speed, Time, EPSILON_DIST};
@ -50,28 +50,84 @@ pub enum TripSpec {
},
}
#[derive(Serialize, Deserialize, PartialEq, Clone)]
// This structure is created temporarily by a Scenario or to interactively spawn agents.
// TODO The API isn't great. Passing in Sim and having to use friend methods is awkward.
// Alternatives could be somehow consuming Sim temporarily and spitting it back out at the end
// (except the interactive spawner would have to mem::replace with a blank Sim?), or just queueing
// up commands and doing them at the end while holding onto &Sim.
pub struct TripSpawner {
parked_cars_claimed: BTreeSet<CarID>,
trips: Vec<(Time, Option<PedestrianID>, Option<CarID>, TripSpec)>,
pub car_id_counter: usize,
pub ped_id_counter: usize,
}
impl TripSpawner {
pub fn new() -> TripSpawner {
pub fn new(car_id_counter: usize, ped_id_counter: usize) -> TripSpawner {
TripSpawner {
parked_cars_claimed: BTreeSet::new(),
trips: Vec::new(),
car_id_counter,
ped_id_counter,
}
}
pub fn schedule_trip(
&mut self,
start_time: Time,
spec: TripSpec,
map: &Map,
sim: &Sim,
) -> (Option<PedestrianID>, Option<CarID>) {
let (ped_id, car_id) = match spec {
TripSpec::CarAppearing {
ref vehicle_spec,
ref goal,
..
} => {
let car = CarID(self.car_id_counter, vehicle_spec.vehicle_type);
self.car_id_counter += 1;
let ped = match goal {
DrivingGoal::ParkNear(_) => {
let id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
Some(id)
}
_ => None,
};
(ped, Some(car))
}
TripSpec::UsingParkedCar { .. }
| TripSpec::MaybeUsingParkedCar { .. }
| TripSpec::JustWalking { .. }
| TripSpec::UsingTransit { .. } => {
let id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
(Some(id), None)
}
TripSpec::UsingBike { .. } => {
let ped = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
let car = CarID(self.car_id_counter, VehicleType::Bike);
self.car_id_counter += 1;
(Some(ped), Some(car))
}
};
self.inner_schedule_trip(start_time, ped_id, car_id, spec, map, sim);
(ped_id, car_id)
}
// TODO Maybe collapse this in the future
fn inner_schedule_trip(
&mut self,
start_time: Time,
ped_id: Option<PedestrianID>,
car_id: Option<CarID>,
spec: TripSpec,
map: &Map,
parking: &ParkingSimState,
sim: &Sim,
) {
// TODO We'll want to repeat this validation when we spawn stuff later for a second leg...
match &spec {
@ -106,7 +162,12 @@ impl TripSpawner {
}
}
TripSpec::UsingParkedCar { spot, .. } => {
let car_id = parking.get_car_at_spot(*spot).unwrap().vehicle.id;
let car_id = sim
.spawner_parking()
.get_car_at_spot(*spot)
.unwrap()
.vehicle
.id;
if self.parked_cars_claimed.contains(&car_id) {
panic!(
"A TripSpec wants to use {}, which is already claimed",
@ -179,12 +240,12 @@ impl TripSpawner {
self.trips.push((start_time, ped_id, car_id, spec));
}
pub fn spawn_all(
&mut self,
pub fn finalize(
mut self,
map: &Map,
parking: &ParkingSimState,
trips: &mut TripManager,
scheduler: &mut Scheduler,
parking: &ParkingSimState,
timer: &mut Timer,
retry_if_no_room: bool,
) {
@ -469,10 +530,6 @@ impl TripSpawner {
scheduler.finalize_batch();
timer.stop("finalize spawned trips");
}
pub fn is_done(&self) -> bool {
self.trips.is_empty()
}
}
impl TripSpec {

View File

@ -30,7 +30,6 @@ pub struct Sim {
intersections: IntersectionSimState,
transit: TransitSimState,
trips: TripManager,
spawner: TripSpawner,
scheduler: Scheduler,
time: Time,
car_id_counter: usize,
@ -107,7 +106,6 @@ impl Sim {
),
transit: TransitSimState::new(),
trips: TripManager::new(),
spawner: TripSpawner::new(),
scheduler,
time: Time::START_OF_DAY,
car_id_counter: 0,
@ -125,62 +123,31 @@ impl Sim {
}
}
pub fn schedule_trip(
&mut self,
start_time: Time,
spec: TripSpec,
map: &Map,
) -> (Option<PedestrianID>, Option<CarID>) {
let (ped_id, car_id) = match spec {
TripSpec::CarAppearing {
ref vehicle_spec,
ref goal,
..
} => {
let car = CarID(self.car_id_counter, vehicle_spec.vehicle_type);
self.car_id_counter += 1;
let ped = match goal {
DrivingGoal::ParkNear(_) => {
let id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
Some(id)
}
_ => None,
};
(ped, Some(car))
}
TripSpec::UsingParkedCar { .. }
| TripSpec::MaybeUsingParkedCar { .. }
| TripSpec::JustWalking { .. }
| TripSpec::UsingTransit { .. } => {
let id = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
(Some(id), None)
}
TripSpec::UsingBike { .. } => {
let ped = PedestrianID(self.ped_id_counter);
self.ped_id_counter += 1;
let car = CarID(self.car_id_counter, VehicleType::Bike);
self.car_id_counter += 1;
(Some(ped), Some(car))
}
};
self.spawner
.schedule_trip(start_time, ped_id, car_id, spec, map, &self.parking);
(ped_id, car_id)
pub fn make_spawner(&self) -> TripSpawner {
TripSpawner::new(self.car_id_counter, self.ped_id_counter)
}
pub fn spawn_all_trips(&mut self, map: &Map, timer: &mut Timer, retry_if_no_room: bool) {
self.spawner.spawn_all(
pub fn flush_spawner(
&mut self,
spawner: TripSpawner,
map: &Map,
timer: &mut Timer,
retry_if_no_room: bool,
) {
self.car_id_counter = spawner.car_id_counter;
self.ped_id_counter = spawner.ped_id_counter;
spawner.finalize(
map,
&self.parking,
&mut self.trips,
&mut self.scheduler,
&self.parking,
timer,
retry_if_no_room,
);
}
// TODO Friend method pattern :(
pub(crate) fn spawner_parking(&self) -> &ParkingSimState {
&self.parking
}
pub fn get_free_spots(&self, l: LaneID) -> Vec<ParkingSpot> {
self.parking.get_free_spots(l)
@ -372,9 +339,6 @@ impl Sim {
// Advances time as minimally as possible, also limited by max_dt.
fn minimal_step(&mut self, map: &Map, max_dt: Duration) {
self.step_count += 1;
if !self.spawner.is_done() {
panic!("Forgot to call spawn_all_trips");
}
let max_time = if let Some(t) = self.scheduler.peek_next_time() {
if t > self.time + max_dt {
@ -815,10 +779,6 @@ impl Sim {
"- trips: {} bytes",
abstutil::prettyprint_usize(abstutil::serialized_size_bytes(&self.trips))
);
println!(
"- spawner: {} bytes",
abstutil::prettyprint_usize(abstutil::serialized_size_bytes(&self.spawner))
);
println!(
"- scheduler: {} bytes",
abstutil::prettyprint_usize(abstutil::serialized_size_bytes(&self.scheduler))
@ -868,7 +828,7 @@ impl Sim {
}
pub fn is_done(&self) -> bool {
self.spawner.is_done() && self.trips.is_done()
self.trips.is_done()
}
pub fn is_empty(&self) -> bool {