putting together a spawn layer. not using it yet.

This commit is contained in:
Dustin Carlino 2019-02-23 17:50:59 -08:00
parent 0c9b173683
commit aea11b72e3
8 changed files with 239 additions and 86 deletions

View File

@ -138,6 +138,8 @@ Sim layering is bit tricky...
- something above this layer takes some constraints to spawn things, uses RNG to populate unfilled things, and then calls spawn layer.
- this is called by interactive UI and tests and scenario (which is a more succinct description of lots of these commands)
Actually, get rid of the deferred nature of the spawn layer for now. pass it (almost) fully-specified stuff to do and let it parallelize. Trip layer can't precompute paths because start is unknown. Could be hard to batch there.
## IDs
Should LaneID have LaneType bundled in for convenience? CarID and VehicleType?

View File

@ -23,6 +23,7 @@ quick-xml = "0.13.1"
# TODO Just for the DES model prototype
rand = "0.6.5"
rand_xorshift = "0.1.1"
rayon = "1.0"
serde = "1.0.87"
serde_derive = "1.0.87"
sim = { path = "../sim" }

View File

@ -180,7 +180,7 @@ impl DrivingSimState {
state: CarState::Queued,
last_steps: VecDeque::new(),
};
if let Some(parked_car) = params.maybe_parked_car {
if params.maybe_parked_car.is_some() {
car.state = CarState::Unparking(
params.start_dist,
TimeInterval::new(time, time + TIME_TO_UNPARK),

View File

@ -131,6 +131,10 @@ impl ParkingSimState {
&& !self.reserved_spots.contains(&spot)
}
pub fn get_car_at_spot(&self, spot: ParkingSpot) -> CarID {
self.lanes[spot.lane.0].occupants[spot.idx].unwrap()
}
pub fn get_first_free_spot(
&self,
parking_pos: Position,

View File

@ -2,6 +2,7 @@ mod mechanics;
mod router;
mod scheduler;
mod sim;
mod spawn;
mod trips;
pub use self::mechanics::{
@ -10,7 +11,7 @@ pub use self::mechanics::{
pub use self::router::{ActionAtEnd, Router};
pub use self::scheduler::{Command, Scheduler};
pub use self::sim::Sim;
pub use self::trips::TripManager;
pub use self::trips::{TripLeg, TripManager};
use ::sim::{CarID, PedestrianID, TripID, VehicleType};
use geom::{Distance, Duration, Speed};
use map_model::{BuildingID, BusStopID, IntersectionID, LaneID, LaneType, Map, Path, Position};
@ -24,11 +25,29 @@ pub const FOLLOWING_DISTANCE: Distance = Distance::const_meters(1.0);
pub struct Vehicle {
pub id: CarID,
pub vehicle_type: VehicleType,
pub length: Distance,
pub max_speed: Option<Speed>,
}
// TODO Dedupe...
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct VehicleSpec {
pub vehicle_type: VehicleType,
pub length: Distance,
pub max_speed: Option<Speed>,
}
impl VehicleSpec {
pub fn make(self, id: CarID) -> Vehicle {
Vehicle {
id,
vehicle_type: self.vehicle_type,
length: self.length,
max_speed: self.max_speed,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ParkingSpot {
pub lane: LaneID,

View File

@ -0,0 +1,183 @@
use crate::plugins::sim::new_des_model::{
Command, CreateCar, CreatePedestrian, DrivingGoal, ParkingSimState, ParkingSpot, Router,
Scheduler, SidewalkSpot, TripLeg, TripManager, VehicleSpec,
};
use abstutil::Timer;
use geom::Duration;
use map_model::{Map, Path, PathRequest, Pathfinder, Position};
use sim::{CarID, PedestrianID, VehicleType};
use std::collections::BTreeSet;
#[derive(Debug)]
pub enum TripSpec {
// Can be used to spawn from a border or anywhere for interactive debugging.
CarAppearing(Position, VehicleSpec, DrivingGoal),
// TODO The only starts that really make sense are building or border...
UsingParkedCar(SidewalkSpot, ParkingSpot, DrivingGoal),
// TODO The only starts that really make sense are building or border...
//UsingBike(SidewalkSpot, VehicleSpec, DrivingGoal),
JustWalking(SidewalkSpot, SidewalkSpot),
// TODO Transit
}
pub struct TripSpawner {
car_id_counter: usize,
ped_id_counter: usize,
parked_cars_claimed: BTreeSet<CarID>,
}
impl TripSpawner {
pub fn new() -> TripSpawner {
TripSpawner {
car_id_counter: 0,
ped_id_counter: 0,
parked_cars_claimed: BTreeSet::new(),
}
}
pub fn schedule_trip(
&mut self,
start_time: Duration,
spec: TripSpec,
path: Path,
map: &Map,
parking: &ParkingSimState,
trips: &mut TripManager,
scheduler: &mut Scheduler,
) {
match spec {
TripSpec::CarAppearing(start_pos, vehicle_spec, goal) => {
let car_id = CarID::tmp_new(self.car_id_counter, VehicleType::Car);
self.car_id_counter += 1;
let ped_id = PedestrianID::tmp_new(self.ped_id_counter);
self.ped_id_counter += 1;
let mut legs = vec![TripLeg::Drive(car_id, goal.clone())];
let router = match goal {
DrivingGoal::ParkNear(b) => {
legs.push(TripLeg::Walk(SidewalkSpot::building(b, map)));
Router::park_near(path.convert_to_traversable_list(), b)
}
DrivingGoal::Border(_, last_lane) => Router::stop_suddenly(
path.convert_to_traversable_list(),
map.get_l(last_lane).length(),
map,
),
};
let trip = trips.new_trip(start_time, Some(ped_id), legs);
scheduler.enqueue_command(Command::SpawnCar(
start_time,
CreateCar {
vehicle: vehicle_spec.make(car_id),
router,
start_dist: start_pos.dist_along(),
maybe_parked_car: None,
trip,
},
));
}
TripSpec::UsingParkedCar(start, spot, goal) => {
let ped_id = PedestrianID::tmp_new(self.ped_id_counter);
self.ped_id_counter += 1;
let car_id = parking.get_car_at_spot(spot);
if self.parked_cars_claimed.contains(&car_id) {
panic!(
"A TripSpec wants to use {}, which is already claimed",
car_id
);
}
self.parked_cars_claimed.insert(car_id);
//assert_eq!(parked.owner, Some(start_bldg));
let parking_spot = SidewalkSpot::parking_spot(spot, map, parking);
let mut legs = vec![
TripLeg::Walk(parking_spot.clone()),
TripLeg::Drive(car_id, goal.clone()),
];
match goal {
DrivingGoal::ParkNear(b) => {
legs.push(TripLeg::Walk(SidewalkSpot::building(b, map)));
}
DrivingGoal::Border(_, _) => {}
}
let trip = trips.new_trip(start_time, Some(ped_id), legs);
scheduler.enqueue_command(Command::SpawnPed(
start_time,
CreatePedestrian {
id: ped_id,
start,
goal: parking_spot,
path,
trip,
},
));
}
TripSpec::JustWalking(start, goal) => {
let ped_id = PedestrianID::tmp_new(self.ped_id_counter);
self.ped_id_counter += 1;
let trip =
trips.new_trip(start_time, Some(ped_id), vec![TripLeg::Walk(goal.clone())]);
scheduler.enqueue_command(Command::SpawnPed(
start_time,
CreatePedestrian {
id: ped_id,
start,
goal,
path,
trip,
},
));
}
}
}
}
impl TripSpec {
fn get_pathfinding_request(&self, map: &Map, parking: &ParkingSimState) -> PathRequest {
match self {
TripSpec::CarAppearing(start, vehicle_spec, goal) => {
let goal_lane = match goal {
DrivingGoal::ParkNear(b) => map.find_driving_lane_near_building(*b),
DrivingGoal::Border(_, l) => *l,
};
PathRequest {
start: *start,
end: Position::new(goal_lane, map.get_l(goal_lane).length()),
can_use_bus_lanes: vehicle_spec.vehicle_type == VehicleType::Bus,
can_use_bike_lanes: vehicle_spec.vehicle_type == VehicleType::Bike,
}
}
TripSpec::UsingParkedCar(start, spot, _) => PathRequest {
start: start.sidewalk_pos,
end: SidewalkSpot::parking_spot(*spot, map, parking).sidewalk_pos,
can_use_bike_lanes: false,
can_use_bus_lanes: false,
},
TripSpec::JustWalking(start, goal) => PathRequest {
start: start.sidewalk_pos,
end: goal.sidewalk_pos,
can_use_bike_lanes: false,
can_use_bus_lanes: false,
},
}
}
}
fn calculate_paths(map: &Map, requests: Vec<PathRequest>, timer: &mut Timer) -> Vec<Option<Path>> {
use rayon::prelude::*;
timer.start(&format!("calculate {} paths", requests.len()));
let paths: Vec<Option<Path>> = requests
.into_par_iter()
.map(|req| Pathfinder::shortest_distance(map, req))
.collect();
timer.stop(&format!("calculate {} paths", paths.len()));
paths
}

View File

@ -1,8 +1,9 @@
use crate::plugins::sim::new_des_model::{DrivingGoal, ParkedCar, SidewalkSpot, Vehicle};
use crate::plugins::sim::new_des_model::{DrivingGoal, SidewalkSpot, Vehicle};
use abstutil::{deserialize_btreemap, serialize_btreemap};
use geom::Duration;
use map_model::{BusRouteID, BusStopID};
use serde_derive::{Deserialize, Serialize};
use sim::{AgentID, CarID, PedestrianID, ScoreSummary, Tick, TripID};
use sim::{AgentID, CarID, PedestrianID, TripID};
use std::collections::{BTreeMap, VecDeque};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -37,8 +38,7 @@ impl TripManager {
let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
match trip.legs.pop_front().unwrap() {
TripLeg::Drive(parked, _) => assert_eq!(car, parked.vehicle.id),
TripLeg::DriveFromBorder(id, _) => assert_eq!(car, id),
TripLeg::Drive(id, _) => assert_eq!(car, id),
x => panic!(
"First trip leg {:?} doesn't match car_reached_parking_spot",
x
@ -107,7 +107,7 @@ impl TripManager {
(trip.id, trip.ped.unwrap(), walk_to.clone())
}
pub fn ped_reached_building_or_border(&mut self, ped: PedestrianID, now: Tick) {
pub fn ped_reached_building_or_border(&mut self, ped: PedestrianID, now: Duration) {
let trip = &mut self.trips[self
.active_trip_mode
.remove(&AgentID::Pedestrian(ped))
@ -126,11 +126,10 @@ impl TripManager {
}
// Or bike
pub fn car_reached_border(&mut self, car: CarID, now: Tick) {
pub fn car_reached_border(&mut self, car: CarID, now: Duration) {
let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(car)).unwrap().0];
match trip.legs.pop_front().unwrap() {
TripLeg::Drive(_, _) => {}
TripLeg::DriveFromBorder(_, _) => {}
TripLeg::Bike(_, _) => {}
x => panic!("Last trip leg {:?} doesn't match car_reached_border", x),
};
@ -193,7 +192,7 @@ impl TripManager {
// Creation from the interactive part of spawner
pub fn new_trip(
&mut self,
spawned_at: Tick,
spawned_at: Duration,
ped: Option<PedestrianID>,
legs: Vec<TripLeg>,
) -> TripID {
@ -208,7 +207,6 @@ impl TripManager {
ped,
uses_car: legs.iter().any(|l| match l {
TripLeg::Drive(_, _) => true,
TripLeg::DriveFromBorder(_, _) => true,
TripLeg::ServeBusRoute(_, _) => true,
_ => false,
}),
@ -217,57 +215,6 @@ impl TripManager {
id
}
// Query from spawner
pub fn get_trip_using_car(&self, car: CarID) -> Option<TripID> {
self.trips
.iter()
.find(|t| t.legs.iter().any(|l| l.uses_car(car)))
.map(|t| t.id)
}
pub fn get_score(&self, now: Tick) -> ScoreSummary {
let mut summary = ScoreSummary {
pending_walking_trips: 0,
total_walking_trips: 0,
total_walking_trip_time: Tick::zero(),
pending_driving_trips: 0,
total_driving_trips: 0,
total_driving_trip_time: Tick::zero(),
completion_time: None,
};
// TODO or would it make more sense to aggregate events as they happen?
for t in &self.trips {
// Don't count transit
if t.ped.is_none() {
continue;
}
if t.uses_car {
if let Some(at) = t.finished_at {
summary.total_driving_trip_time += at - t.spawned_at;
} else {
summary.pending_driving_trips += 1;
if now >= t.spawned_at {
summary.total_driving_trip_time += now - t.spawned_at;
}
}
summary.total_driving_trips += 1;
} else {
if let Some(at) = t.finished_at {
summary.total_walking_trip_time += at - t.spawned_at;
} else {
summary.pending_walking_trips += 1;
if now >= t.spawned_at {
summary.total_walking_trip_time += now - t.spawned_at;
}
}
summary.total_walking_trips += 1;
}
}
summary
}
pub fn active_agents(&self) -> Vec<AgentID> {
self.active_trip_mode.keys().cloned().collect()
}
@ -276,8 +223,7 @@ impl TripManager {
let trip = self.trips.get(id.0)?;
match trip.legs.get(0)? {
TripLeg::Walk(_) => Some(AgentID::Pedestrian(trip.ped.unwrap())),
TripLeg::Drive(ref parked, _) => Some(AgentID::Car(parked.vehicle.id)),
TripLeg::DriveFromBorder(id, _) => Some(AgentID::Car(*id)),
TripLeg::Drive(id, _) => Some(AgentID::Car(*id)),
TripLeg::Bike(vehicle, _) => Some(AgentID::Car(vehicle.id)),
// TODO Should be the bus, but apparently transit sim tracks differently?
TripLeg::RideBus(_, _) => Some(AgentID::Pedestrian(trip.ped.unwrap())),
@ -308,8 +254,8 @@ impl TripManager {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Trip {
id: TripID,
spawned_at: Tick,
finished_at: Option<Tick>,
spawned_at: Duration,
finished_at: Option<Duration>,
// TODO also uses_bike, so we can track those stats differently too
uses_car: bool,
// If none, then this is a bus. The trip will never end.
@ -317,29 +263,13 @@ struct Trip {
legs: VecDeque<TripLeg>,
}
// Except for Drive (which has to say what car to drive), these don't say where the leg starts.
// That's because it might be unknown -- like when we drive and don't know where we'll wind up
// parking.
// These don't specify where the leg starts, since it might be unknown -- like when we drive and
// don't know where we'll wind up parking.
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub enum TripLeg {
Walk(SidewalkSpot),
// TODO Can maybe collapse Drive and DriveFromBorder by acting like Bike and doing Vehicle,
// which has CarID
Drive(ParkedCar, DrivingGoal),
DriveFromBorder(CarID, DrivingGoal),
Drive(CarID, DrivingGoal),
Bike(Vehicle, DrivingGoal),
RideBus(BusRouteID, BusStopID),
ServeBusRoute(CarID, BusRouteID),
}
impl TripLeg {
fn uses_car(&self, id: CarID) -> bool {
match self {
TripLeg::Drive(parked, _) => parked.vehicle.id == id,
TripLeg::DriveFromBorder(car, _) => *car == id,
TripLeg::Bike(vehicle, _) => vehicle.id == id,
TripLeg::ServeBusRoute(car, _) => *car == id,
_ => false,
}
}
}

View File

@ -261,6 +261,20 @@ impl Path {
pub fn get_steps(&self) -> &VecDeque<PathStep> {
&self.steps
}
// Only valid for driving paths
pub fn convert_to_traversable_list(self) -> Vec<Traversable> {
self.steps
.into_iter()
.map(|step| match step {
PathStep::Lane(l) => Traversable::Lane(l),
PathStep::ContraflowLane(_) => {
panic!("Can't convert_to_traversable_list with ContraflowLane")
}
PathStep::Turn(t) => Traversable::Turn(t),
})
.collect()
}
}
#[derive(Clone)]