From 2e37d673b862f0072e43a0855b9d954a965b9ec6 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sat, 23 Feb 2019 14:49:30 -0800 Subject: [PATCH] port scheduler. use it for spawning peds. --- editor/src/plugins/sim/even_simpler_model.rs | 1 - .../sim/new_des_model/mechanics/driving.rs | 14 ++- .../sim/new_des_model/mechanics/walking.rs | 41 +++---- editor/src/plugins/sim/new_des_model/mod.rs | 25 +++- .../src/plugins/sim/new_des_model/router.rs | 5 +- .../plugins/sim/new_des_model/scheduler.rs | 107 ++++++++++++++++++ editor/src/plugins/sim/new_des_model/sim.rs | 34 ++++-- geom/src/units.rs | 8 ++ 8 files changed, 198 insertions(+), 37 deletions(-) create mode 100644 editor/src/plugins/sim/new_des_model/scheduler.rs diff --git a/editor/src/plugins/sim/even_simpler_model.rs b/editor/src/plugins/sim/even_simpler_model.rs index 4460a2c0c4..4e10b2694b 100644 --- a/editor/src/plugins/sim/even_simpler_model.rs +++ b/editor/src/plugins/sim/even_simpler_model.rs @@ -341,7 +341,6 @@ fn random_ped_near( new_des_model::SidewalkSpot::bike_rack(pos1, map), new_des_model::SidewalkSpot::bike_rack(pos2, map), path, - map, ); *counter += 1 } diff --git a/editor/src/plugins/sim/new_des_model/mechanics/driving.rs b/editor/src/plugins/sim/new_des_model/mechanics/driving.rs index c17cec4ce3..e942aca2dc 100644 --- a/editor/src/plugins/sim/new_des_model/mechanics/driving.rs +++ b/editor/src/plugins/sim/new_des_model/mechanics/driving.rs @@ -1,8 +1,8 @@ use crate::plugins::sim::new_des_model::mechanics::car::{Car, CarState}; use crate::plugins::sim::new_des_model::mechanics::queue::Queue; use crate::plugins::sim::new_des_model::{ - ActionAtEnd, IntersectionSimState, ParkedCar, ParkingSimState, Router, TimeInterval, Vehicle, - FOLLOWING_DISTANCE, MAX_VEHICLE_LENGTH, + ActionAtEnd, CreateCar, IntersectionSimState, ParkedCar, ParkingSimState, Router, TimeInterval, + Vehicle, FOLLOWING_DISTANCE, MAX_VEHICLE_LENGTH, }; use ezgui::{Color, GfxCtx}; use geom::{Distance, Duration}; @@ -127,6 +127,16 @@ impl DrivingSimState { } } + pub fn start_car_on_lane( + &mut self, + time: Duration, + map: &Map, + params: CreateCar, + intersections: &IntersectionSimState, + ) -> bool { + false + } + pub fn spawn_car( &mut self, vehicle: Vehicle, diff --git a/editor/src/plugins/sim/new_des_model/mechanics/walking.rs b/editor/src/plugins/sim/new_des_model/mechanics/walking.rs index 3f046acfc5..8fc8b2861f 100644 --- a/editor/src/plugins/sim/new_des_model/mechanics/walking.rs +++ b/editor/src/plugins/sim/new_des_model/mechanics/walking.rs @@ -1,5 +1,6 @@ use crate::plugins::sim::new_des_model::{ - DistanceInterval, IntersectionSimState, SidewalkPOI, SidewalkSpot, TimeInterval, + CreatePedestrian, DistanceInterval, IntersectionSimState, SidewalkPOI, SidewalkSpot, + TimeInterval, }; use abstutil::{deserialize_multimap, serialize_multimap}; use geom::{Distance, Duration, Speed}; @@ -31,46 +32,40 @@ impl WalkingSimState { } } - pub fn spawn_ped( - &mut self, - id: PedestrianID, - start_time: Duration, - start: SidewalkSpot, - goal: SidewalkSpot, - path: Path, - map: &Map, - ) { - let start_lane = start.sidewalk_pos.lane(); + pub fn spawn_ped(&mut self, time: Duration, params: CreatePedestrian, map: &Map) { + let start_lane = params.start.sidewalk_pos.lane(); assert_eq!( - path.current_step().as_traversable(), + params.path.current_step().as_traversable(), Traversable::Lane(start_lane) ); assert_eq!( - path.last_step().as_traversable(), - Traversable::Lane(goal.sidewalk_pos.lane()) + params.path.last_step().as_traversable(), + Traversable::Lane(params.goal.sidewalk_pos.lane()) ); let mut ped = Pedestrian { - id, + id: params.id, // Temporary bogus thing state: PedState::Crossing( DistanceInterval::new_walking(Distance::ZERO, Distance::meters(1.0)), TimeInterval::new(Duration::ZERO, Duration::seconds(1.0)), true, ), - path, - goal, + path: params.path, + goal: params.goal, }; - ped.state = match start.connection { + ped.state = match params.start.connection { SidewalkPOI::BikeRack => { - ped.crossing_state(start.sidewalk_pos.dist_along(), start_time, map) + ped.crossing_state(params.start.sidewalk_pos.dist_along(), time, map) } - _ => panic!("Don't support {:?} yet", start.connection), + _ => panic!("Don't support {:?} yet", params.start.connection), }; - self.peds.insert(id, ped); - self.peds_per_traversable - .insert(Traversable::Lane(start.sidewalk_pos.lane()), id); + self.peds.insert(params.id, ped); + self.peds_per_traversable.insert( + Traversable::Lane(params.start.sidewalk_pos.lane()), + params.id, + ); } pub fn get_all_draw_peds(&self, time: Duration, map: &Map) -> Vec { diff --git a/editor/src/plugins/sim/new_des_model/mod.rs b/editor/src/plugins/sim/new_des_model/mod.rs index 449bf8359b..d46aeef0e9 100644 --- a/editor/src/plugins/sim/new_des_model/mod.rs +++ b/editor/src/plugins/sim/new_des_model/mod.rs @@ -1,5 +1,6 @@ mod mechanics; mod router; +mod scheduler; mod sim; mod trips; @@ -7,11 +8,12 @@ pub use self::mechanics::{ DrivingSimState, IntersectionSimState, ParkingSimState, WalkingSimState, }; pub use self::router::{ActionAtEnd, Router}; +pub use self::scheduler::{Command, Scheduler}; pub use self::sim::Sim; pub use self::trips::TripManager; -use ::sim::{CarID, VehicleType}; +use ::sim::{CarID, PedestrianID, TripID, VehicleType}; use geom::{Distance, Duration, Speed}; -use map_model::{BuildingID, BusStopID, IntersectionID, LaneID, LaneType, Map, Position}; +use map_model::{BuildingID, BusStopID, IntersectionID, LaneID, LaneType, Map, Path, Position}; use serde_derive::{Deserialize, Serialize}; pub const MIN_VEHICLE_LENGTH: Distance = Distance::const_meters(2.0); @@ -202,3 +204,22 @@ impl DistanceInterval { (self.end - self.start).abs() } } + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct CreatePedestrian { + pub id: PedestrianID, + pub start: SidewalkSpot, + pub goal: SidewalkSpot, + pub path: Path, + pub trip: TripID, +} + +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +pub struct CreateCar { + pub vehicle: Vehicle, + pub router: Router, + pub start_time: Duration, + pub start_dist: Distance, + pub maybe_parked_car: Option, + pub trip: TripID, +} diff --git a/editor/src/plugins/sim/new_des_model/router.rs b/editor/src/plugins/sim/new_des_model/router.rs index e23d9ea78e..5c32486531 100644 --- a/editor/src/plugins/sim/new_des_model/router.rs +++ b/editor/src/plugins/sim/new_des_model/router.rs @@ -1,9 +1,10 @@ use crate::plugins::sim::new_des_model::{ParkingSimState, ParkingSpot, Vehicle}; use geom::Distance; use map_model::{BuildingID, LaneType, Map, Position, Traversable}; +use serde_derive::{Deserialize, Serialize}; use std::collections::VecDeque; -#[derive(Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Router { // Front is always the current step path: VecDeque, @@ -16,7 +17,7 @@ pub enum ActionAtEnd { GotoLaneEnd, } -#[derive(Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] enum Goal { // Spot and distance along the last driving lane // TODO Right now, the building is ignored. diff --git a/editor/src/plugins/sim/new_des_model/scheduler.rs b/editor/src/plugins/sim/new_des_model/scheduler.rs new file mode 100644 index 0000000000..530a76afeb --- /dev/null +++ b/editor/src/plugins/sim/new_des_model/scheduler.rs @@ -0,0 +1,107 @@ +use crate::plugins::sim::new_des_model::{ + CreateCar, CreatePedestrian, DrivingSimState, IntersectionSimState, ParkingSimState, + TripManager, WalkingSimState, +}; +use geom::Duration; +use map_model::Map; +use serde_derive::{Deserialize, Serialize}; +use sim::AgentID; + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub enum Command { + SpawnCar(Duration, CreateCar), + SpawnPed(Duration, CreatePedestrian), +} + +impl Command { + fn at(&self) -> Duration { + match self { + Command::SpawnCar(at, _) => *at, + Command::SpawnPed(at, _) => *at, + } + } +} + +#[derive(Serialize, Deserialize, PartialEq)] +pub struct Scheduler { + // Ordered descending by time + commands: Vec, +} + +impl Scheduler { + pub fn new() -> Scheduler { + Scheduler { + commands: Vec::new(), + } + } + + pub fn step_if_needed( + &mut self, + now: Duration, + map: &Map, + parking_sim: &mut ParkingSimState, + walking_sim: &mut WalkingSimState, + driving_sim: &mut DrivingSimState, + intersections: &IntersectionSimState, + trips: &mut TripManager, + ) { + let mut this_tick_commands: Vec = Vec::new(); + loop { + if self + .commands + .last() + // TODO >= just to handle the fact that we dont step on 0 + .and_then(|cmd| Some(now >= cmd.at())) + .unwrap_or(false) + { + this_tick_commands.push(self.commands.pop().unwrap()); + } else { + break; + } + } + if this_tick_commands.is_empty() { + return; + } + + for cmd in this_tick_commands.into_iter() { + match cmd { + Command::SpawnCar(_, create_car) => { + if driving_sim.start_car_on_lane(now, map, create_car.clone(), intersections) { + trips.agent_starting_trip_leg( + AgentID::Car(create_car.vehicle.id), + create_car.trip, + ); + if let Some(parked_car) = create_car.maybe_parked_car { + parking_sim.remove_parked_car(parked_car); + } + } else { + self.enqueue_command(Command::SpawnCar( + now + Duration::EPSILON, + create_car, + )); + } + } + Command::SpawnPed(_, create_ped) => { + // Do the order a bit backwards so we don't have to clone the CreatePedestrian. + // spawn_ped can't fail. + trips.agent_starting_trip_leg( + AgentID::Pedestrian(create_ped.id), + create_ped.trip, + ); + walking_sim.spawn_ped(now, create_ped, map); + } + }; + } + } + + pub fn is_done(&self) -> bool { + self.commands.is_empty() + } + + pub fn enqueue_command(&mut self, cmd: Command) { + // TODO Use some kind of priority queue that's serializable + self.commands.push(cmd); + self.commands.sort_by_key(|cmd| cmd.at()); + self.commands.reverse(); + } +} diff --git a/editor/src/plugins/sim/new_des_model/sim.rs b/editor/src/plugins/sim/new_des_model/sim.rs index 7edf9d8ea2..20755bb1dd 100644 --- a/editor/src/plugins/sim/new_des_model/sim.rs +++ b/editor/src/plugins/sim/new_des_model/sim.rs @@ -1,11 +1,11 @@ use crate::plugins::sim::new_des_model::{ - DrivingSimState, IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot, Router, - SidewalkSpot, TripManager, Vehicle, WalkingSimState, + Command, CreatePedestrian, DrivingSimState, IntersectionSimState, ParkedCar, ParkingSimState, + ParkingSpot, Router, Scheduler, SidewalkSpot, TripManager, Vehicle, WalkingSimState, }; use ezgui::GfxCtx; use geom::{Distance, Duration}; use map_model::{LaneID, Map, Path, Position, Traversable}; -use sim::{DrawCarInput, DrawPedestrianInput, PedestrianID}; +use sim::{DrawCarInput, DrawPedestrianInput, PedestrianID, TripID}; pub struct Sim { driving: DrivingSimState, @@ -13,6 +13,7 @@ pub struct Sim { walking: WalkingSimState, intersections: IntersectionSimState, trips: TripManager, + scheduler: Scheduler, } impl Sim { @@ -23,6 +24,7 @@ impl Sim { walking: WalkingSimState::new(), intersections: IntersectionSimState::new(map), trips: TripManager::new(), + scheduler: Scheduler::new(), } } @@ -63,6 +65,7 @@ impl Sim { self.walking.get_draw_peds(time, on, map) } + // TODO Many of these should go away pub fn spawn_car( &mut self, vehicle: Vehicle, @@ -89,11 +92,17 @@ impl Sim { start: SidewalkSpot, goal: SidewalkSpot, path: Path, - map: &Map, ) { - let start_time = Duration::ZERO; - self.walking - .spawn_ped(id, start_time, start, goal, path, map); + self.scheduler.enqueue_command(Command::SpawnPed( + Duration::ZERO, + CreatePedestrian { + id, + start, + goal, + path, + trip: TripID(0), + }, + )); } pub fn get_free_spots(&self, l: LaneID) -> Vec { @@ -122,5 +131,16 @@ impl Sim { .step_if_needed(time, map, &mut self.parking, &mut self.intersections); self.walking .step_if_needed(time, map, &mut self.intersections); + + // Spawn stuff at the end, so we can see the correct state of everything else at this time. + self.scheduler.step_if_needed( + time, + map, + &mut self.parking, + &mut self.walking, + &mut self.driving, + &self.intersections, + &mut self.trips, + ); } } diff --git a/geom/src/units.rs b/geom/src/units.rs index 31f356b6d2..04ee451c70 100644 --- a/geom/src/units.rs +++ b/geom/src/units.rs @@ -155,6 +155,14 @@ impl ops::Div for Distance { #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] pub struct Duration(f64); +// By construction, Duration is a finite f64 with trimmed precision. +impl Eq for Duration {} +impl Ord for Duration { + fn cmp(&self, other: &Duration) -> cmp::Ordering { + self.partial_cmp(other).unwrap() + } +} + impl Duration { pub const ZERO: Duration = Duration::const_seconds(0.0); pub const EPSILON: Duration = Duration::const_seconds(0.0001);