Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

editoast: filter train schedules for LMR #10347

Merged
merged 1 commit into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions editoast/src/models/timetable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use diesel::sql_query;
use diesel::sql_types::Array;
use diesel::sql_types::BigInt;
use diesel_async::RunQueryDsl;
use futures_util::stream::TryStreamExt;
use std::ops::DerefMut;

use crate::error::Result;
use crate::models::prelude::*;
use crate::models::train_schedule::TrainSchedule;
use crate::models::Identifiable;
use crate::models::{DeleteStatic, Retrieve};
use crate::Exists;
Expand Down Expand Up @@ -56,6 +59,29 @@ impl Timetable {
.await
.map_err(Into::into)
}

pub async fn schedules_before_date(
self,
conn: &mut DbConnection,
time: DateTime<Utc>,
) -> Result<Vec<TrainSchedule>> {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use editoast_models::tables::train_schedule::dsl;

let train_schedules = dsl::train_schedule
.filter(dsl::start_time.le(time))
.filter(dsl::timetable_id.eq(self.id))
.load_stream::<Row<TrainSchedule>>(conn.write().await.deref_mut())
.await?
.map_ok(|ts| ts.into())
.try_collect::<Vec<TrainSchedule>>()
.await;
match train_schedules {
Ok(train_schedules) => Ok(train_schedules),
Err(err) => Err(err.into()),
}
}
}

#[async_trait::async_trait]
Expand Down
2 changes: 1 addition & 1 deletion editoast/src/models/train_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use editoast_schemas::train_schedule::ScheduleItem;
use editoast_schemas::train_schedule::TrainScheduleBase;
use editoast_schemas::train_schedule::TrainScheduleOptions;

use super::Model as _;
use crate::models::prelude::*;

#[derive(Debug, Default, Clone, Model)]
#[model(table = editoast_models::tables::train_schedule)]
Expand Down
75 changes: 66 additions & 9 deletions editoast/src/views/timetable/stdcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::core::CoreClient;
use crate::error::Result;
use crate::models::prelude::*;
use crate::models::stdcm_log::StdcmLog;
use crate::models::timetable::TimetableWithTrains;
use crate::models::timetable::Timetable;
use crate::models::train_schedule::TrainSchedule;
use crate::models::Infra;
use crate::models::RollingStockModel;
Expand Down Expand Up @@ -149,20 +149,13 @@ async fn stdcm(
let timetable_id = id;
let infra_id = query.infra;

// 1. Retrieve Timetable / Infra / Trains / Simulation / Rolling Stock
let timetable_trains = TimetableWithTrains::retrieve_or_fail(&mut conn, timetable_id, || {
StdcmError::TimetableNotFound { timetable_id }
})
.await?;
// 1. Infra / Timetable / Trains / Simulation / Rolling Stock

let infra = Infra::retrieve_or_fail(&mut conn, infra_id, || StdcmError::InfraNotFound {
infra_id,
})
.await?;

let (train_schedules, _): (Vec<_>, _) =
TrainSchedule::retrieve_batch(&mut conn, timetable_trains.train_ids.clone()).await?;

let rolling_stock =
RollingStockModel::retrieve_or_fail(&mut conn, stdcm_request.rolling_stock_id, || {
StdcmError::RollingStockNotFound {
Expand Down Expand Up @@ -205,6 +198,70 @@ async fn stdcm(
let earliest_departure_time = stdcm_request.get_earliest_departure_time(simulation_run_time);
let latest_simulation_end = stdcm_request.get_latest_simulation_end(simulation_run_time);

let timetable = Timetable::retrieve_or_fail(&mut conn, timetable_id, || {
StdcmError::TimetableNotFound { timetable_id }
})
.await?;

// Filter trains
// The goal is to filter out as many trains as possible whose schedules overlap
// with the LMR train being searched for.
// The diagram below shows an LMR train inserted into a timetable.

// '?': unscheduled arrival times.
// '|': scheduled arrival times.
// tA: earliest_departure_time
// tB: latest_simulation_end
//
// tA tB
// LMR Train |----------------------|
// Train 1 |--------------|
// Train 2 |------------|
// |----------? Train 3
// Train 4 |-------?
// Train 5 |---------?
// |----------? Train 6

// Step 1 (SQL Filter):
// Trains that depart after the latest arrival time of the LMR train are excluded.
// In this example, Train 3 and Train 6 are filtered out.

// It's not easy to write an SQL query to filter trains when the train departure time < latest_simulation_ended
// because there are two cases : when the train departure time > tA (Step 2) and the train departure time < tA (Step 3).

// Step 2 (Rust filter) :
// If the train departure time > LMR train departure (tA), the train is kept (e.g., train_5)
// Step 3 (Rust filter) :
// For trains departing before the LMR train departure (tA):

// If the train's arrival time is unscheduled (?), the train is kept (e.g., Train 4 and Train 5).
// If the train's arrival time is scheduled (|), the train is kept only if its arrival time is after the LMR train's earliest departure time.
// Train 1 is kept and train 2 is filtered out.

// Step 1
let mut train_schedules = timetable
.schedules_before_date(&mut conn, latest_simulation_end)
.await?;

train_schedules.retain(|train_schedule| {
// Step 2 and 3
train_schedule.start_time >= earliest_departure_time
|| train_schedule
.schedule
.last()
.and_then(|last_schedule_item| {
train_schedule.path.last().and_then(|last_path_item| {
(last_schedule_item.at == last_path_item.id).then_some(last_schedule_item)
})
})
.and_then(|last_schedule_item| {
last_schedule_item.arrival.clone().map(|arrival| {
train_schedule.start_time + *arrival > earliest_departure_time
})
})
.unwrap_or(true)
});

// 3. Get scheduled train requirements
let simulations: Vec<_> = train_simulation_batch(
&mut conn,
Expand Down
Loading