Handle live edits by just aborting any current trips that cross an

edited road or closed intersection. #312

Along the way, refactor more context plumbing in DrivingSimState.
This commit is contained in:
Dustin Carlino 2020-09-07 12:44:41 -07:00
parent 39ab06df7d
commit d8011a90f4
9 changed files with 146 additions and 108 deletions

View File

@ -472,7 +472,7 @@ impl ContextualActions for Actions {
}
}
ID::Car(_) => {
actions.push((Key::Backspace, "forcibly kill this car".to_string()));
actions.push((Key::Backspace, "forcibly delete this car".to_string()));
actions.push((Key::G, "find front of blockage".to_string()));
}
ID::Area(_) => {
@ -511,8 +511,8 @@ impl ContextualActions for Actions {
objects::ObjectDebugger::dump_debug(id, &app.primary.map, &app.primary.sim);
Transition::Keep
}
(ID::Car(c), "forcibly kill this car") => {
app.primary.sim.kill_stuck_car(c, &app.primary.map);
(ID::Car(c), "forcibly delete this car") => {
app.primary.sim.delete_car(c, &app.primary.map);
app.primary
.sim
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);

View File

@ -95,7 +95,7 @@ impl EditMode {
app.primary
.sim
.handle_live_edited_traffic_signals(&app.primary.map);
// TODO Some other stuff too
app.primary.sim.handle_live_edited_lanes(&app.primary.map);
Transition::Pop
})
}

View File

@ -4,7 +4,7 @@ use crate::helpers::amenity_type;
use crate::layer::{Layer, LayerOutcome};
use abstutil::Counter;
use geom::{Distance, Time};
use map_model::{EditRoad, LaneType};
use map_model::LaneType;
use sim::AgentType;
use widgetry::{
hotkey, Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Panel, Text,
@ -217,22 +217,12 @@ impl Static {
);
let edits = app.primary.map.get_edits();
for r in &edits.changed_roads {
let r = app.primary.map.get_r(*r);
let orig = EditRoad::get_orig_from_osm(r, app.primary.map.get_config().driving_side);
// What exactly changed?
if r.speed_limit != orig.speed_limit
|| r.access_restrictions != orig.access_restrictions
{
colorer.add_r(r.id, "modified road/intersection");
} else {
let lanes = r.lanes_ltr();
for (idx, (lt, dir)) in orig.lanes_ltr.into_iter().enumerate() {
if lanes[idx].1 != dir || lanes[idx].2 != lt {
colorer.add_l(lanes[idx].0, "modified road/intersection");
}
}
}
let (lanes, roads) = edits.changed_lanes(&app.primary.map);
for l in lanes {
colorer.add_l(l, "modified road/intersection");
}
for r in roads {
colorer.add_r(r, "modified road/intersection");
}
for i in edits.original_intersections.keys() {
colorer.add_i(*i, "modified road/intersection");

View File

@ -5,8 +5,8 @@ use crate::make::initial::lane_specs::get_lane_specs_ltr;
use crate::raw::DrivingSide;
use crate::{
connectivity, AccessRestrictions, BusRouteID, ControlStopSign, ControlTrafficSignal, Direction,
IntersectionID, IntersectionType, LaneType, Map, PathConstraints, Pathfinder, Road, RoadID,
TurnID, Zone,
IntersectionID, IntersectionType, LaneID, LaneType, Map, PathConstraints, Pathfinder, Road,
RoadID, TurnID, Zone,
};
use abstutil::{retain_btreemap, retain_btreeset, Timer};
use geom::{Speed, Time};
@ -182,6 +182,30 @@ impl MapEdits {
});
}
}
// Pick apart changed_roads and figure out if an entire road was edited, or just a few lanes.
pub fn changed_lanes(&self, map: &Map) -> (BTreeSet<LaneID>, BTreeSet<RoadID>) {
let mut lanes = BTreeSet::new();
let mut roads = BTreeSet::new();
for r in &self.changed_roads {
let r = map.get_r(*r);
let orig = EditRoad::get_orig_from_osm(r, map.get_config().driving_side);
// What exactly changed?
if r.speed_limit != orig.speed_limit
|| r.access_restrictions != orig.access_restrictions
{
roads.insert(r.id);
} else {
let lanes_ltr = r.lanes_ltr();
for (idx, (lt, dir)) in orig.lanes_ltr.into_iter().enumerate() {
if lanes_ltr[idx].1 != dir || lanes_ltr[idx].2 != lt {
lanes.insert(lanes_ltr[idx].0);
}
}
}
}
(lanes, roads)
}
}
impl std::default::Default for MapEdits {

View File

@ -227,15 +227,7 @@ impl DrivingSimState {
{
self.cars.insert(id, car);
} else {
self.delete_car(
&mut car,
dists,
idx,
now,
ctx.map,
ctx.scheduler,
ctx.intersections,
);
self.delete_car_internal(&mut car, dists, idx, now, ctx);
}
}
}
@ -580,14 +572,7 @@ impl DrivingSimState {
}
}
pub fn kill_stuck_car(
&mut self,
c: CarID,
now: Time,
map: &Map,
scheduler: &mut Scheduler,
intersections: &mut IntersectionSimState,
) -> Vehicle {
pub fn delete_car(&mut self, c: CarID, now: Time, ctx: &mut Ctx) -> Vehicle {
let dists = self.queues[&self.cars[&c].router.head()].get_car_positions(
now,
&self.cars,
@ -602,24 +587,22 @@ impl DrivingSimState {
queue.reserved_length += car.vehicle.length + FOLLOWING_DISTANCE;
}
if let Some(Traversable::Turn(t)) = car.router.maybe_next() {
intersections.cancel_request(AgentID::Car(c), t);
ctx.intersections.cancel_request(AgentID::Car(c), t);
}
self.delete_car(&mut car, dists, idx, now, map, scheduler, intersections);
// delete_car cancels UpdateLaggyHead
scheduler.cancel(Command::UpdateCar(c));
self.delete_car_internal(&mut car, dists, idx, now, ctx);
// delete_car_internal cancels UpdateLaggyHead
ctx.scheduler.cancel(Command::UpdateCar(c));
car.vehicle
}
fn delete_car(
fn delete_car_internal(
&mut self,
car: &mut Car,
dists: Vec<(CarID, Distance)>,
idx: usize,
now: Time,
map: &Map,
scheduler: &mut Scheduler,
intersections: &mut IntersectionSimState,
ctx: &mut Ctx,
) {
{
let queue = self.queues.get_mut(&car.router.head()).unwrap();
@ -627,26 +610,21 @@ impl DrivingSimState {
// trim_last_steps doesn't actually include the current queue!
queue.free_reserved_space(car);
let i = match queue.id {
Traversable::Lane(l) => map.get_l(l).src_i,
Traversable::Lane(l) => ctx.map.get_l(l).src_i,
Traversable::Turn(t) => t.parent,
};
intersections.space_freed(now, i, scheduler, map);
ctx.intersections
.space_freed(now, i, ctx.scheduler, ctx.map);
}
intersections.vehicle_gone(car.vehicle.id);
ctx.intersections.vehicle_gone(car.vehicle.id);
// We might be vanishing while partly clipping into other stuff.
self.trim_last_steps(
car,
now,
car.last_steps.len(),
map,
intersections,
scheduler,
);
self.trim_last_steps(car, now, car.last_steps.len(), ctx);
// We might've scheduled one of those using BLIND_RETRY_TO_CREEP_FORWARDS.
scheduler.cancel(Command::UpdateLaggyHead(car.vehicle.id));
ctx.scheduler
.cancel(Command::UpdateLaggyHead(car.vehicle.id));
// Update the follower so that they don't suddenly jump forwards.
if idx != dists.len() - 1 {
@ -659,8 +637,8 @@ impl DrivingSimState {
CarState::Queued { blocked_since } => {
// Prevent them from jumping forwards.
follower.total_blocked_time += now - blocked_since;
follower.state = follower.crossing_state(follower_dist, now, map);
scheduler.update(
follower.state = follower.crossing_state(follower_dist, now, ctx.map);
ctx.scheduler.update(
follower.state.get_end_time(),
Command::UpdateCar(follower_id),
);
@ -669,8 +647,8 @@ impl DrivingSimState {
// If the follower was still Crossing, they might not've been blocked
// by leader yet. In that case, recalculating their Crossing state is a
// no-op.
follower.state = follower.crossing_state(follower_dist, now, map);
scheduler.update(
follower.state = follower.crossing_state(follower_dist, now, ctx.map);
ctx.scheduler.update(
follower.state.get_end_time(),
Command::UpdateCar(follower_id),
);
@ -684,14 +662,7 @@ impl DrivingSimState {
}
}
pub fn update_laggy_head(
&mut self,
id: CarID,
now: Time,
map: &Map,
intersections: &mut IntersectionSimState,
scheduler: &mut Scheduler,
) {
pub fn update_laggy_head(&mut self, id: CarID, now: Time, ctx: &mut Ctx) {
let currently_on = self.cars[&id].router.head();
let current_dists =
self.queues[&currently_on].get_car_positions(now, &self.cars, &self.queues);
@ -716,12 +687,12 @@ impl DrivingSimState {
num_to_trim = Some(self.cars[&id].last_steps.len() - idx);
break;
}
dist_left_to_cleanup -= step.length(map);
dist_left_to_cleanup -= step.length(ctx.map);
}
if let Some(n) = num_to_trim {
let mut car = self.cars.remove(&id).unwrap();
self.trim_last_steps(&mut car, now, n, map, intersections, scheduler);
self.trim_last_steps(&mut car, now, n, ctx);
self.cars.insert(id, car);
}
@ -737,20 +708,20 @@ impl DrivingSimState {
self.cars[&id].vehicle.length + FOLLOWING_DISTANCE,
),
now,
map,
ctx.map,
)
.get_end_time();
// Sometimes due to rounding, retry_at will be exactly time, but we really need to
// wait a bit longer.
// TODO Smarter retry based on states and stuckness?
if retry_at > now {
scheduler.push(retry_at, Command::UpdateLaggyHead(id));
ctx.scheduler.push(retry_at, Command::UpdateLaggyHead(id));
} else {
// If we look up car positions before this retry happens, weird things can
// happen -- the laggy head could be well clear of the old queue by then. Make
// sure to handle that there. Consequences of this retry being long? A follower
// will wait a bit before advancing.
scheduler.push(
ctx.scheduler.push(
now + BLIND_RETRY_TO_CREEP_FORWARDS,
Command::UpdateLaggyHead(id),
);
@ -759,15 +730,7 @@ impl DrivingSimState {
}
// Caller has to figure out how many steps to trim!
fn trim_last_steps(
&mut self,
car: &mut Car,
now: Time,
n: usize,
map: &Map,
intersections: &mut IntersectionSimState,
scheduler: &mut Scheduler,
) {
fn trim_last_steps(&mut self, car: &mut Car, now: Time, n: usize, ctx: &mut Ctx) {
for i in 0..n {
let on = car.last_steps.pop_back().unwrap();
let old_queue = self.queues.get_mut(&on).unwrap();
@ -775,17 +738,22 @@ impl DrivingSimState {
old_queue.laggy_head = None;
match on {
Traversable::Turn(t) => {
intersections.turn_finished(
ctx.intersections.turn_finished(
now,
AgentID::Car(car.vehicle.id),
t,
scheduler,
map,
ctx.scheduler,
ctx.map,
);
}
Traversable::Lane(l) => {
old_queue.free_reserved_space(car);
intersections.space_freed(now, map.get_l(l).src_i, scheduler, map);
ctx.intersections.space_freed(
now,
ctx.map.get_l(l).src_i,
ctx.scheduler,
ctx.map,
);
}
}
@ -806,11 +774,12 @@ impl DrivingSimState {
if self.recalc_lanechanging {
follower.router.opportunistically_lanechange(
&self.queues,
map,
ctx.map,
self.handle_uber_turns,
);
}
scheduler.push(now, Command::UpdateCar(follower.vehicle.id));
ctx.scheduler
.push(now, Command::UpdateCar(follower.vehicle.id));
}
}
CarState::WaitingToAdvance { .. } => unreachable!(),

View File

@ -208,7 +208,11 @@ impl IntersectionSimState {
}
}
} else {
assert!(map.get_i(i).is_border());
// This could either be a border intersection or an intersection that was just closed
// in the middle of simulation. In either case, there shouldn't be any other turns at
// it.
assert!(protected.is_empty());
assert!(yielding.is_empty());
};
for req in protected {

View File

@ -298,6 +298,13 @@ impl WalkingSimState {
};
}
pub fn delete_ped(&mut self, id: PedestrianID, scheduler: &mut Scheduler) {
let ped = self.peds.remove(&id).unwrap();
self.peds_per_traversable
.remove(ped.path.current_step().as_traversable(), id);
scheduler.cancel(Command::UpdatePed(id));
}
pub fn debug_ped(&self, id: PedestrianID) {
if let Some(ped) = self.peds.get(&id) {
println!("{}", abstutil::to_json(ped));

View File

@ -531,13 +531,7 @@ impl Sim {
);
}
Command::UpdateLaggyHead(car) => {
self.driving.update_laggy_head(
car,
self.time,
map,
&mut self.intersections,
&mut self.scheduler,
);
self.driving.update_laggy_head(car, self.time, &mut ctx);
}
Command::UpdatePed(ped) => {
self.walking.update_ped(
@ -853,6 +847,59 @@ impl Sim {
pub fn handle_live_edited_traffic_signals(&mut self, map: &Map) {
self.intersections.handle_live_edited_traffic_signals(map)
}
pub fn handle_live_edited_lanes(&mut self, map: &Map) {
// TODO Handle changes to access restrictions
// Find every active trip whose path crosses a modified lane or closed intersection
let (edited_lanes, _) = map.get_edits().changed_lanes(map);
let mut closed_intersections = HashSet::new();
for i in map.get_edits().original_intersections.keys() {
if map.get_i(*i).is_closed() {
closed_intersections.insert(*i);
}
}
let mut affected = Vec::new();
for (a, trip) in self.trips.active_agents_and_trips() {
if let Some(path) = self.get_path(*a) {
if path
.get_steps()
.iter()
.any(|step| match step.as_traversable() {
Traversable::Lane(l) => edited_lanes.contains(&l),
Traversable::Turn(t) => closed_intersections.contains(&t.parent),
})
{
affected.push((*a, *trip));
}
}
}
// V1: Just abort every trip crossing an affected area.
// (V2 is probably rerouting everyone, only aborting when that fails)
// TODO If we delete a bus, deal with all its passengers
let mut ctx = Ctx {
parking: &mut self.parking,
intersections: &mut self.intersections,
cap: &mut self.cap,
scheduler: &mut self.scheduler,
map,
};
for (agent, trip) in affected {
match agent {
AgentID::Car(car) => {
let vehicle = self.driving.delete_car(car, self.time, &mut ctx);
self.trips
.abort_trip(self.time, trip, Some(vehicle), &mut ctx);
}
AgentID::Pedestrian(ped) => {
self.walking.delete_ped(ped, ctx.scheduler);
self.trips.abort_trip(self.time, trip, None, &mut ctx);
}
AgentID::BusPassenger(_, _) => unreachable!(),
}
}
}
}
// Queries of all sorts
@ -1200,15 +1247,8 @@ impl Sim {
// Invasive debugging
impl Sim {
pub fn kill_stuck_car(&mut self, id: CarID, map: &Map) {
pub fn delete_car(&mut self, id: CarID, map: &Map) {
if let Some(trip) = self.agent_to_trip(AgentID::Car(id)) {
let vehicle = self.driving.kill_stuck_car(
id,
self.time,
map,
&mut self.scheduler,
&mut self.intersections,
);
let mut ctx = Ctx {
parking: &mut self.parking,
intersections: &mut self.intersections,
@ -1216,6 +1256,7 @@ impl Sim {
scheduler: &mut self.scheduler,
map,
};
let vehicle = self.driving.delete_car(id, self.time, &mut ctx);
self.trips
.abort_trip(self.time, trip, Some(vehicle), &mut ctx);
println!("Forcibly killed {}", id);

View File

@ -820,6 +820,9 @@ impl TripManager {
pub fn get_active_trips(&self) -> Vec<TripID> {
self.active_trip_mode.values().cloned().collect()
}
pub fn active_agents_and_trips(&self) -> &BTreeMap<AgentID, TripID> {
&self.active_trip_mode
}
pub fn num_active_agents(&self) -> usize {
self.active_trip_mode.len()
}