Skip to content

Commit

Permalink
editoast: refactor path not found handling
Browse files Browse the repository at this point in the history
Signed-off-by: hamz2a <atrari.hamza@gmail.com>
  • Loading branch information
hamz2a committed Oct 23, 2024
1 parent 3ef7876 commit 90fd83e
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 129 deletions.
31 changes: 31 additions & 0 deletions editoast/src/views/timetable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub mod path_not_found_handler;
pub mod stdcm;
pub mod stdcm_request_payload;

use std::cmp::max;
use std::collections::HashMap;

use axum::extract::Json;
Expand All @@ -10,6 +12,10 @@ use axum::extract::State;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
use chrono::DateTime;
use chrono::NaiveDateTime;
use chrono::TimeZone;
use chrono::Utc;
use derivative::Derivative;
use editoast_authz::BuiltinRole;
use editoast_derive::EditoastError;
Expand All @@ -25,13 +31,15 @@ use crate::core::conflict_detection::Conflict;
use crate::core::conflict_detection::ConflictDetectionRequest;
use crate::core::conflict_detection::TrainRequirements;
use crate::core::simulation::SimulationResponse;
use crate::core::stdcm::UndirectedTrackRange;
use crate::core::AsCoreRequest;
use crate::error::Result;
use crate::models::prelude::*;
use crate::models::timetable::Timetable;
use crate::models::timetable::TimetableWithTrains;
use crate::models::train_schedule::TrainSchedule;
use crate::models::train_schedule::TrainScheduleChangeset;
use crate::models::work_schedules::WorkSchedule;
use crate::models::Infra;
use crate::views::train_schedule::train_simulation_batch;
use crate::views::train_schedule::TrainScheduleForm;
Expand Down Expand Up @@ -351,6 +359,29 @@ async fn conflicts(
Ok(Json(conflict_detection_response.conflicts))
}

pub fn filter_core_work_schedule(
ws: &WorkSchedule,
start_time: DateTime<Utc>,
) -> crate::core::stdcm::WorkSchedule {
crate::core::stdcm::WorkSchedule {
start_time: elapsed_since_time_ms(&ws.start_date_time, &start_time),
end_time: elapsed_since_time_ms(&ws.end_date_time, &start_time),
track_ranges: ws
.track_ranges
.iter()
.map(|track| UndirectedTrackRange {
track_section: track.track.to_string(),
begin: (track.begin * 1000.0) as u64,
end: (track.end * 1000.0) as u64,
})
.collect(),
}
}

fn elapsed_since_time_ms(time: &NaiveDateTime, zero: &DateTime<Utc>) -> u64 {
max(0, (Utc.from_utc_datetime(time) - zero).num_milliseconds()) as u64
}

#[cfg(test)]
mod tests {
use axum::http::StatusCode;
Expand Down
119 changes: 119 additions & 0 deletions editoast/src/views/timetable/path_not_found_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::sync::Arc;

use chrono::DateTime;
use chrono::Utc;

use crate::core::conflict_detection::ConflictDetectionRequest;
use crate::core::conflict_detection::WorkSchedulesRequest;
use crate::core::pathfinding::PathfindingResult;
use crate::core::simulation::SimulationResponse;
use crate::core::stdcm::STDCMResponse;
use crate::core::AsCoreRequest;
use crate::core::CoreClient;
use crate::error::Result;
use crate::models::train_schedule::TrainSchedule;
use crate::models::work_schedules::WorkSchedule;

use super::filter_core_work_schedule;
use super::stdcm::build_train_requirements;

pub struct PathNotFoundHandler {
pub core_client: Arc<CoreClient>,
pub infra_id: i64,
pub infra_version: String,
pub train_schedules: Vec<TrainSchedule>,
pub simulations: Vec<(SimulationResponse, PathfindingResult)>,
pub work_schedules: Vec<WorkSchedule>,
pub virtual_train_schedule: TrainSchedule,
pub virtual_train_sim_result: SimulationResponse,
pub virtual_train_pathfinding_result: PathfindingResult,
pub earliest_departure_time: DateTime<Utc>,
pub maximum_run_time: u64,
pub latest_simulation_end: DateTime<Utc>,
}

impl PathNotFoundHandler {
pub async fn handle(self) -> Result<STDCMResponse> {
let virtual_train_id = self.virtual_train_schedule.id;

// Combine the original train schedules with the virtual train schedule.
let train_schedules = [self.train_schedules, vec![self.virtual_train_schedule]].concat();

// Combine the original simulations with the virtual train's simulation results.
let simulations = [
self.simulations,
vec![(
self.virtual_train_sim_result,
self.virtual_train_pathfinding_result.clone(),
)],
]
.concat();

// Build train requirements based on the combined train schedules and simulations
// This prepares the data structure required for conflict detection.
let trains_requirements = build_train_requirements(
train_schedules,
simulations,
self.earliest_departure_time,
self.latest_simulation_end,
);

// Filter the provided work schedules to find those that conflict with the given parameters
// This identifies any work schedules that may overlap with the earliest departure time and maximum run time.
let conflict_work_schedules = filter_conflict_work_schedules(
&self.work_schedules,
self.earliest_departure_time,
self.maximum_run_time,
);

// Prepare the conflict detection request.
let conflict_detection_request = ConflictDetectionRequest {
infra: self.infra_id,
expected_version: self.infra_version,
trains_requirements,
work_schedules: conflict_work_schedules,
};

// Send the conflict detection request and await the response.
let conflict_detection_response =
conflict_detection_request.fetch(&self.core_client).await?;

// Filter the conflicts to find those specifically related to the virtual train.
let conflicts: Vec<_> = conflict_detection_response
.conflicts
.into_iter()
.filter(|conflict| conflict.train_ids.contains(&virtual_train_id))
.map(|mut conflict| {
conflict.train_ids.retain(|id| id != &virtual_train_id);
conflict
})
.collect();

// Return the conflicts found along with the pathfinding result for the virtual train.
Ok(STDCMResponse::Conflicts {
pathfinding_result: self.virtual_train_pathfinding_result,
conflicts,
})
}
}

fn filter_conflict_work_schedules(
work_schedules: &[WorkSchedule],
start_time: DateTime<Utc>,
maximum_run_time: u64,
) -> Option<WorkSchedulesRequest> {
if work_schedules.is_empty() {
return None;
}

let work_schedule_requirements = work_schedules
.iter()
.map(|ws| (ws.id, filter_core_work_schedule(ws, start_time)))
.filter(|(_, ws)| ws.end_time > 0 && ws.start_time < maximum_run_time)
.collect();

Some(WorkSchedulesRequest {
start_time,
work_schedule_requirements,
})
}
Loading

0 comments on commit 90fd83e

Please sign in to comment.