mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
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:
parent
39ab06df7d
commit
d8011a90f4
@ -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);
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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[¤tly_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!(),
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user