mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 01:13:53 +03:00
putting together a spawn layer. not using it yet.
This commit is contained in:
parent
0c9b173683
commit
aea11b72e3
@ -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?
|
||||
|
@ -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" }
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
183
editor/src/plugins/sim/new_des_model/spawn.rs
Normal file
183
editor/src/plugins/sim/new_des_model/spawn.rs
Normal 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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user