use crate::behaviour::BehaviourParameters;
use crate::agents_types::AgentType;
use crate::agents::{VehicleID, Vehicle, VehiclesStorage};
use crate::conflict_zones::{ConflictZone, ConflictZoneID};
use crate::grid::cell::{CellID, Cell};
use crate::trips::trip::{Trip, TripID, TripType};
use crate::simulation::grids_storage::{GridsStorage, GridsStorageError};
use crate::geom::{Point, SRID};
use crate::intentions::{IntentionError, prepare_intentions};
use crate::conflicts::{ConflictError, ConflictSolverError, collect_conflicts, solve_conflicts};
use crate::movement::{MovementError, movement};
use crate::simulation::states::{AutomataState, VehicleState};
use crate::traffic_lights::lights::{TrafficLightID, TrafficLight};
use crate::verbose::*;
use std::collections::HashMap;
use uuid::Uuid;
use std::fmt;
use std::time::{SystemTime, UNIX_EPOCH};
use rand::Rng;
#[derive(Debug, Clone)]
pub enum SessionError {
ErrorPlaceholder(String),
GridsStorageError(GridsStorageError),
IntentionError(IntentionError),
ConflictError(ConflictError),
ConflictSolverError(ConflictSolverError),
CellNotFound(CellID),
MovementError(MovementError),
}
impl fmt::Display for SessionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SessionError::ErrorPlaceholder(value) => {
write!(f, "ErrorPlaceholder: {}", value)
},
SessionError::GridsStorageError(err) => {
write!(f, "GridsStorage error: {}", err)
},
SessionError::IntentionError(err) => {
write!(f, "Intention error: {}", err)
},
SessionError::ConflictError(err) => {
write!(f, "Conflict error: {}", err)
},
SessionError::ConflictSolverError(err) => {
write!(f, "Conflict solver error: {}", err)
},
SessionError::CellNotFound(cell_id) => {
write!(f, "Cell with ID {} not found", cell_id)
},
SessionError::MovementError(err) => {
write!(f, "Movement error: {}", err)
},
}
}
}
impl std::error::Error for SessionError {}
impl From<GridsStorageError> for SessionError {
fn from(err: GridsStorageError) -> Self {
SessionError::GridsStorageError(err)
}
}
impl From<IntentionError> for SessionError {
fn from(err: IntentionError) -> Self {
SessionError::IntentionError(err)
}
}
impl From<ConflictError> for SessionError {
fn from(err: ConflictError) -> Self {
SessionError::ConflictError(err)
}
}
impl From<ConflictSolverError> for SessionError {
fn from(err: ConflictSolverError) -> Self {
SessionError::ConflictSolverError(err)
}
}
impl From<MovementError> for SessionError {
fn from(err: MovementError) -> Self {
SessionError::MovementError(err)
}
}
pub struct Session {
current_position: HashMap<CellID, VehicleID>,
grids_storage: GridsStorage,
trips_data: HashMap<TripID, Trip>,
vehicles: VehiclesStorage,
_coordination_cells: HashMap<CellID, CellID>,
conflict_zones: HashMap<ConflictZoneID, ConflictZone>,
cells_conflicts_zones: HashMap<CellID, ConflictZoneID>,
id: Uuid,
steps: i32,
last_vehicle_id: VehicleID,
verbose: LocalLogger,
_updated_at: i64,
_expire_at: i64,
world_srid: SRID,
}
impl Session {
pub fn default(srid: Option<SRID>) -> Self {
let picked_srid = srid.unwrap_or(SRID::Euclidean);
let session_id = Uuid::new_v4();
let verbose = LocalLogger::with_session(VerboseLevel::None, session_id.to_string());
Session {
id: session_id,
last_vehicle_id: 1,
vehicles: VehiclesStorage::new(),
grids_storage: GridsStorage::new().build(),
trips_data: HashMap::new(),
verbose,
_coordination_cells: HashMap::new(),
conflict_zones: HashMap::new(),
cells_conflicts_zones: HashMap::new(),
current_position: HashMap::new(),
_updated_at: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as i64,
_expire_at: 0,
steps: 0,
world_srid: picked_srid,
}
}
pub fn new(grids_storage: GridsStorage, srid: Option<SRID>) -> Self {
let picked_srid = srid.unwrap_or(SRID::Euclidean);
let session_id = Uuid::new_v4();
let verbose = LocalLogger::with_session(VerboseLevel::None, session_id.to_string());
Session {
id: session_id,
last_vehicle_id: 1,
vehicles: VehiclesStorage::new(),
grids_storage,
trips_data: HashMap::new(),
verbose,
_coordination_cells: HashMap::new(),
conflict_zones: HashMap::new(),
cells_conflicts_zones: HashMap::new(),
current_position: HashMap::new(),
_updated_at: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as i64,
_expire_at: 0,
steps: 0,
world_srid: picked_srid,
}
}
pub fn get_id(&self) -> Uuid {
self.id
}
pub fn get_steps(&self) -> i32 {
self.steps
}
pub fn get_last_vehicle_id(&self) -> VehicleID {
self.last_vehicle_id
}
pub fn get_world_srid(&self) -> SRID {
self.world_srid
}
pub fn get_verbose_level(&self) -> VerboseLevel {
self.verbose.level()
}
pub fn set_verbose_level(&mut self, verbose: VerboseLevel) {
self.verbose.set_level(verbose);
}
pub fn get_cell(&self, cell_id: &CellID) -> Option<&Cell> {
self.grids_storage.get_cell(cell_id)
}
pub fn get_tls_ref(&self, ) -> &HashMap<TrafficLightID, TrafficLight> {
self.grids_storage.get_tls_ref()
}
pub fn add_trip(&mut self, trip: Trip) -> TripID {
let mut trip = trip;
if trip.end_time == 0 {
trip.end_time = i32::MAX;
}
if trip.end_time < trip.start_time {
return 0;
}
let trip_id = trip.id;
self.trips_data.insert(trip_id, trip);
trip_id
}
pub fn add_vehicles(&mut self, vehicles: Vec<Vehicle>) {
for vehicle in vehicles {
let vehicle_id = vehicle.id;
self.vehicles.insert(vehicle_id, vehicle);
if vehicle_id >= self.last_vehicle_id {
self.last_vehicle_id = vehicle_id + 1;
}
}
}
pub fn get_vehicles(&self) -> &VehiclesStorage { &self.vehicles }
pub fn add_cells(&mut self, cells_data: Vec<crate::grid::cell::Cell>) {
self.grids_storage.add_cells(cells_data);
}
pub fn reset(&mut self) {
self.verbose.log_with_fields(
EVENT_SIMULATION_RESET,
"Reset simulation",
&[
("step", &self.steps),
("vehicles_num", &self.vehicles.len()),
("trips_num", &self.trips_data.len()),
("tls_num", &self.grids_storage.tls_num()),
]
);
self.vehicles.clear();
self.grids_storage.tls_reset();
self.trips_data.clear();
self.steps = 0;
self.last_vehicle_id = 1;
}
pub fn add_traffic_light(&mut self, tl: crate::traffic_lights::lights::TrafficLight) {
self.grids_storage.add_traffic_light(tl);
}
pub fn add_conflict_zone(&mut self, conflict_zone: ConflictZone) {
let conflict_zone_id = conflict_zone.get_id();
let first_edge = conflict_zone.get_first_edge();
let second_edge = conflict_zone.get_second_edge();
if first_edge.target >= 0 {
self.cells_conflicts_zones.insert(first_edge.target, conflict_zone_id);
}
if second_edge.target >= 0 {
self.cells_conflicts_zones.insert(second_edge.target, conflict_zone_id);
}
self.conflict_zones.insert(conflict_zone_id, conflict_zone);
}
fn generate_vehicle(&self, trip: &Trip, trip_id: TripID) -> Option<Vehicle> {
if self.steps < trip.start_time || self.steps > trip.end_time {
return None;
}
let should_generate = match trip.trip_type {
TripType::Constant => {
if trip.time <= 0 {
false
} else {
self.steps % trip.time == 0
}
}
TripType::Random => {
let mut rng = rand::rng();
let norm_value: f64 = rng.random();
norm_value < trip.probability
}
_ => {
if self.verbose.is_at_least(VerboseLevel::Detailed) {
self.verbose.log_with_fields(
EVENT_GEN_VEHICLE,
"Trip type is not supported",
&[
("trip_id", &trip_id),
("trip_type", &format!("{:?}", trip.trip_type)),
]
);
}
false
}
};
if !should_generate {
return None;
}
let target_node = if trip.allowed_agent_type == AgentType::Bus
&& !trip.transit_cells.is_empty() {
trip.transit_cells[0] } else {
trip.to_node
};
let behaviour_params = BehaviourParameters::from_behaviour_type(trip.allowed_behaviour_type);
let speed_limit = if trip.speed_limit >= 0 {
trip.speed_limit
} else {
behaviour_params.speed_limit()
};
let vehicle = Vehicle::new(self.last_vehicle_id)
.with_type(trip.allowed_agent_type)
.with_behaviour(trip.allowed_behaviour_type)
.with_cell(trip.from_node)
.with_speed(trip.initial_speed)
.with_speed_limit(speed_limit)
.with_slowdown(behaviour_params.slowdown_factor())
.with_min_safe_distance(behaviour_params.min_safe_distance())
.with_aggressive_level(behaviour_params.aggressive_level())
.with_destination(target_node)
.with_trip(trip_id)
.with_tail_size(trip.vehicle_tail_size, vec![]) .with_transit_cells(trip.transit_cells.clone())
.with_relax_time(trip.relax_time)
.build();
Some(vehicle)
}
pub fn generate_vehicles(&mut self) {
if self.verbose.is_at_least(VerboseLevel::Main) {
self.verbose.log_with_fields(
EVENT_GEN_VEHICLES,
"Generate vehicles",
&[
("step", &self.steps),
("vehicles_num", &self.vehicles.len()),
("trips_num", &self.trips_data.len()),
]
);
}
for (trip_id, trip) in &self.trips_data {
let mut create = true;
for vehicle in self.vehicles.values() {
if vehicle.cell_id == trip.from_node {
create = false;
break;
}
}
if !create {
continue;
}
if let Some(generated_vehicle) = self.generate_vehicle(trip, *trip_id) {
if self.verbose.is_at_least(VerboseLevel::Additional) {
self.verbose.log_with_fields(
EVENT_GEN_VEHICLES,
"Generate vehicle for trip",
&[
("step", &self.steps),
("vehicles_num", &self.vehicles.len()),
("trips_num", &self.trips_data.len()),
("trip_id", trip_id),
("vehicle_id", &generated_vehicle.id),
]
);
}
let vehicle_id = generated_vehicle.id;
self.vehicles.insert(vehicle_id, generated_vehicle);
self.last_vehicle_id = vehicle_id + 1; }
}
}
fn update_current_positions(&mut self) {
if self.verbose.is_at_least(VerboseLevel::Main) {
self.verbose.log_with_fields(
EVENT_UPD_POS,
"Update positions",
&[
("step", &self.steps),
("vehicles_num", &self.vehicles.len()),
("trips_num", &self.trips_data.len()),
]
);
}
self.current_position.clear();
for vehicle in self.vehicles.values() {
if self.verbose.is_at_least(VerboseLevel::Detailed) {
self.verbose.log_with_fields(
EVENT_UPD_POS,
"Vehicle position",
&[
("vehicle_id", &vehicle.id),
("cell_id", &vehicle.cell_id),
]
);
for (id, &tail_cell) in vehicle.tail_cells.iter().enumerate() {
if self.verbose.is_at_least(VerboseLevel::All) {
self.verbose.log_with_fields(
EVENT_UPD_POS,
"Vehicle tail position",
&[
("vehicle_id", &vehicle.id),
("tail_idx", &id),
("tail_cell_id", &tail_cell),
]
);
}
}
}
self.current_position.insert(vehicle.cell_id, vehicle.id);
for &tail_cell in &vehicle.tail_cells {
self.current_position.insert(tail_cell, vehicle.id);
}
}
}
pub fn step(&mut self) -> Result<AutomataState, SessionError> {
if self.verbose.is_at_least(VerboseLevel::Main) {
self.verbose.log_with_fields(
EVENT_STEP,
"Run Step",
&[
("step", &self.steps),
("vehicles_num", &self.vehicles.len()),
("trips_num", &self.trips_data.len()),
("tls_num", &self.grids_storage.tls_num()),
]
);
}
self.generate_vehicles();
self.update_current_positions();
let tl_states_dump = self.grids_storage.tick_traffic_lights(&self.verbose)?;
let collected_intentions = prepare_intentions(self.grids_storage.get_vehicles_net_ref(), &self.current_position, &mut self.vehicles, &self.verbose)?;
let conflicts_data = collect_conflicts(
&collected_intentions,
self.grids_storage.get_vehicles_net_ref(),
&self.conflict_zones,
&self.cells_conflicts_zones,
&self.verbose,
&mut self.vehicles,
)?;
solve_conflicts(conflicts_data, &mut self.vehicles, &self.verbose)?;
let vehicles_grid = self.grids_storage.get_vehicles_net_ref();
movement(vehicles_grid, &mut self.vehicles, &self.verbose)?;
let mut states_dump: Vec<VehicleState> = Vec::with_capacity(self.vehicles.len());
for vehicle in self.vehicles.values() {
let pt = vehicles_grid.get_cell(&vehicle.cell_id)
.ok_or(SessionError::CellNotFound(vehicle.cell_id))?
.get_point();
let mut occupied_points: Vec<[f64; 2]> = Vec::with_capacity(vehicle.tail_cells.len());
for &tail_cell_id in &vehicle.tail_cells {
if tail_cell_id > 0 {
if let Some(opt_cell) = vehicles_grid.get_cell(&tail_cell_id) {
let opt_pt = opt_cell.get_point();
occupied_points.push([opt_pt.x(), opt_pt.y()]);
}
}
}
states_dump.push(VehicleState {
occupied_points,
last_point: [pt.x(), pt.y()],
last_cell: vehicle.cell_id,
tail_cells: vehicle.tail_cells.clone(),
last_intermediate_cells: vehicle.intention.intermediate_cells.clone(),
last_speed: vehicle.speed,
last_angle: vehicle.bearing,
vehicle_type: vehicle.vehicle_type,
travel_time: vehicle.travel_time,
id: vehicle.id,
trip_id: vehicle.trip,
});
}
let timestamp = self.steps;
self.steps += 1;
Ok(AutomataState {
timestamp: timestamp,
vehicles: states_dump,
tls: tl_states_dump,
})
}
pub fn get_expire_at(&self) -> i64 {
self._expire_at
}
pub fn set_expire_at(&mut self, expire_at: i64) {
self._expire_at = expire_at;
}
pub fn get_updated_at(&self) -> i64 {
self._updated_at
}
pub fn set_updated_at(&mut self, updated_at: i64) {
self._updated_at = updated_at;
}
}