use crate::agents_types::AgentType;
use crate::agents::{VehicleID, Vehicle};
use crate::grid::road_network::GridRoads;
use crate::grid::cell::CellID;
use crate::grid::zones::ZoneType;
use crate::maneuver::LaneChangeType;
use crate::geom::get_bearing;
use crate::verbose::*;
use indexmap::IndexMap;
use std::fmt;
#[derive(Debug, Clone)]
pub enum MovementError {
CellNotFound {
cell_id: CellID,
vehicle_id: VehicleID
},
}
impl fmt::Display for MovementError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MovementError::CellNotFound { cell_id, vehicle_id } => {
write!(f, "movement(): Can't find cell {} in the network for vehicle with ID {}", cell_id, vehicle_id)
}
}
}
}
impl std::error::Error for MovementError {}
pub fn movement(
net: &GridRoads,
vehicles: &mut IndexMap<VehicleID, Vehicle>,
verbose: &LocalLogger,
) -> Result<(), MovementError> {
if verbose.is_at_least(VerboseLevel::Main) {
verbose.log_with_fields(
EVENT_MOVEMENT,
"Start movement process",
&[("vehicles_num", &vehicles.len())]
);
}
let mut vehicles_to_remove = Vec::new();
for (vehicle_id, vehicle) in vehicles.iter_mut() {
if verbose.is_at_least(VerboseLevel::Additional) {
verbose.log_with_fields(
EVENT_MOVEMENT_VEHICLE,
"Moving vehicle",
&[
("vehicle_id", &vehicle.id),
("current_cell", &vehicle.cell_id),
("next_cell", &vehicle.intention.intention_cell_id),
("intermediate_cells", &format!("{:?}", vehicle.intention.intermediate_cells)),
]
);
}
vehicle.apply_intention();
vehicle.is_conflict_participant = false;
if vehicle.cell_id != vehicle.intention.intention_cell_id {
let cell_from = net.get_cell(&vehicle.cell_id)
.ok_or(MovementError::CellNotFound {
cell_id: vehicle.cell_id,
vehicle_id: vehicle.id
})?;
let cell_to = net.get_cell(&vehicle.intention.intention_cell_id)
.ok_or(MovementError::CellNotFound {
cell_id: vehicle.intention.intention_cell_id,
vehicle_id: vehicle.id
})?;
let pt_from = cell_from.get_point();
let pt_to = cell_to.get_point();
vehicle.bearing = get_bearing(pt_from, pt_to);
if vehicle.timer_non_acceleration > 0 {
vehicle.timer_non_acceleration -= 1;
}
if vehicle.timer_non_maneuvers > 0 {
vehicle.timer_non_maneuvers -= 1;
}
if vehicle.timer_non_slowdown > 0 {
vehicle.timer_non_slowdown -= 1;
}
}
if vehicle.cell_id != vehicle.intention.intention_cell_id {
let tail_size = vehicle.tail_cells.len();
if tail_size > 0 {
let tail_intention = vehicle.intention.tail_intention_cells.clone();
vehicle.tail_cells = tail_intention;
}
}
if vehicle.intention.intention_maneuver == LaneChangeType::ChangeLeft ||
vehicle.intention.intention_maneuver == LaneChangeType::ChangeRight {
let tail_size = vehicle.tail_cells.len() as i64;
vehicle.timer_non_acceleration = tail_size;
vehicle.timer_non_maneuvers = tail_size;
vehicle.timer_non_slowdown = tail_size;
}
let final_cell = if vehicle.get_relax_countdown() > 0 {
vehicle.relax_countdown_dec();
vehicle.cell_id } else {
vehicle.intention.intention_cell_id
};
vehicle.cell_id = final_cell;
let cell = net.get_cell(&vehicle.cell_id)
.ok_or(MovementError::CellNotFound {
cell_id: vehicle.cell_id,
vehicle_id: vehicle.id
})?;
if verbose.is_at_least(VerboseLevel::Additional) {
verbose.log_with_fields(
EVENT_MOVEMENT_VEHICLE,
"Done movement for vehicle",
&[
("vehicle_id", &vehicle.id),
("current_cell", &vehicle.cell_id),
("next_cell", &vehicle.intention.intention_cell_id),
("intermediate_cells", &format!("{:?}", vehicle.intention.intermediate_cells)),
]
);
}
let zone_type = cell.get_zone_type();
if vehicle.vehicle_type == AgentType::Bus && zone_type == ZoneType::Transit && vehicle.cell_id == vehicle.destination {
vehicle.transits_made_inc();
let transits_made = vehicle.get_transits_made();
if (transits_made as usize) < vehicle.transit_cells.len() && vehicle.get_relax_countdown() == 0 {
vehicle.destination = vehicle.transit_cells[transits_made as usize];
vehicle.relax_countdown_reset();
}
}
vehicle.travel_time += 1;
if zone_type == ZoneType::Death && vehicle.cell_id != vehicle.destination {
if verbose.is_at_least(VerboseLevel::Main) {
verbose.log_with_fields(
EVENT_MOVEMENT_DEAD_END,
"Vehicle done movement due going to dead-end",
&[("vehicle_id", &vehicle.id)]
);
}
vehicles_to_remove.push(*vehicle_id);
}
if vehicle.cell_id == vehicle.destination {
if verbose.is_at_least(VerboseLevel::Main) {
verbose.log_with_fields(
EVENT_MOVEMENT_DESTINATION,
"Vehicle done movement due reaching destination",
&[("vehicle_id", &vehicle.id)]
);
}
vehicles_to_remove.push(*vehicle_id);
}
}
for vehicle_id in vehicles_to_remove {
vehicles.swap_remove(&vehicle_id);
}
Ok(())
}