Skip to content

Commit 3ef7876

Browse files
committed
editoast: refactor STDCMRequestPayload methods for encapsulation
- Moved utility functions `get_earliest_step_time`, `get_earliest_step_tolerance_window`, `get_total_stop_time`, `get_maximum_departure_delay`, `get_earliest_departure_time` and `get_latest_simulation_end` into the `STDCMRequestPayload` struct as methods. - Updated the `stdcm` function to utilize these new methods for better readability and maintainability. - Removed redundant function definitions and improved code organization. Signed-off-by: hamz2a <[email protected]>
1 parent e395f40 commit 3ef7876

File tree

3 files changed

+205
-189
lines changed

3 files changed

+205
-189
lines changed

editoast/src/views/timetable.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod stdcm;
2+
pub mod stdcm_request_payload;
23

34
use std::collections::HashMap;
45

@@ -57,7 +58,7 @@ crate::routes! {
5758
editoast_common::schemas! {
5859
TimetableResult,
5960
TimetableDetailedResult,
60-
stdcm::schemas(),
61+
stdcm_request_payload::schemas(),
6162
}
6263

6364
#[derive(Debug, Error, EditoastError)]

editoast/src/views/timetable/stdcm.rs

+22-188
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use editoast_derive::EditoastError;
1010
use editoast_models::DbConnection;
1111
use editoast_models::DbConnectionPoolV2;
1212
use editoast_schemas::primitives::PositiveDuration;
13-
use editoast_schemas::train_schedule::PathItemLocation;
13+
use editoast_schemas::train_schedule::MarginValue;
14+
use editoast_schemas::train_schedule::Margins;
1415
use editoast_schemas::train_schedule::ReceptionSignal;
15-
use editoast_schemas::train_schedule::{Comfort, Margins, PathItem};
16-
use editoast_schemas::train_schedule::{MarginValue, ScheduleItem};
16+
use editoast_schemas::train_schedule::ScheduleItem;
1717
use itertools::Itertools;
1818
use serde::Deserialize;
1919
use serde::Serialize;
@@ -24,14 +24,15 @@ use thiserror::Error;
2424
use utoipa::IntoParams;
2525
use utoipa::ToSchema;
2626

27+
use super::stdcm_request_payload::convert_steps;
28+
use super::stdcm_request_payload::STDCMRequestPayload;
2729
use super::SelectionSettings;
2830
use crate::core::conflict_detection::ConflictDetectionRequest;
2931
use crate::core::conflict_detection::TrainRequirements;
3032
use crate::core::conflict_detection::WorkSchedulesRequest;
3133
use crate::core::pathfinding::InvalidPathItem;
3234
use crate::core::pathfinding::PathfindingResult;
3335
use crate::core::simulation::PhysicsRollingStock;
34-
use crate::core::simulation::SimulationParameters;
3536
use crate::core::simulation::{RoutingRequirement, SimulationResponse, SpacingRequirement};
3637
use crate::core::stdcm::STDCMPathItem;
3738
use crate::core::stdcm::STDCMRequest;
@@ -61,12 +62,6 @@ crate::routes! {
6162
"/stdcm" => stdcm,
6263
}
6364

64-
editoast_common::schemas! {
65-
STDCMRequestPayload,
66-
PathfindingItem,
67-
StepTimingData,
68-
}
69-
7065
#[derive(Debug, Error, EditoastError, Serialize)]
7166
#[editoast_error(base_id = "stdcm_v2")]
7267
enum STDCMError {
@@ -81,80 +76,6 @@ enum STDCMError {
8176
InvalidPathItems { items: Vec<InvalidPathItem> },
8277
}
8378

84-
/// An STDCM request
85-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
86-
pub struct STDCMRequestPayload {
87-
/// Deprecated, first step arrival time should be used instead
88-
start_time: Option<DateTime<Utc>>,
89-
steps: Vec<PathfindingItem>,
90-
rolling_stock_id: i64,
91-
electrical_profile_set_id: Option<i64>,
92-
work_schedule_group_id: Option<i64>,
93-
temporary_speed_limit_group_id: Option<i64>,
94-
comfort: Comfort,
95-
/// By how long we can shift the departure time in milliseconds
96-
/// Deprecated, first step data should be used instead
97-
maximum_departure_delay: Option<u64>,
98-
/// Specifies how long the total run time can be in milliseconds
99-
/// Deprecated, first step data should be used instead
100-
maximum_run_time: Option<u64>,
101-
/// Train categories for speed limits
102-
// TODO: rename the field and its description
103-
speed_limit_tags: Option<String>,
104-
/// Margin before the train passage in seconds
105-
///
106-
/// Enforces that the path used by the train should be free and
107-
/// available at least that many milliseconds before its passage.
108-
#[serde(default)]
109-
time_gap_before: u64,
110-
/// Margin after the train passage in milliseconds
111-
///
112-
/// Enforces that the path used by the train should be free and
113-
/// available at least that many milliseconds after its passage.
114-
#[serde(default)]
115-
time_gap_after: u64,
116-
/// Can be a percentage `X%`, a time in minutes per 100 kilometer `Xmin/100km`
117-
#[serde(default)]
118-
#[schema(value_type = Option<String>, example = json!(["5%", "2min/100km"]))]
119-
margin: Option<MarginValue>,
120-
/// Total mass of the consist in kg
121-
total_mass: Option<f64>,
122-
/// Total length of the consist in meters
123-
total_length: Option<f64>,
124-
/// Maximum speed of the consist in km/h
125-
max_speed: Option<f64>,
126-
}
127-
128-
impl STDCMRequestPayload {
129-
pub fn simulation_parameters(&self) -> SimulationParameters {
130-
SimulationParameters {
131-
total_mass: self.total_mass,
132-
total_length: self.total_length,
133-
max_speed: self.max_speed,
134-
}
135-
}
136-
}
137-
138-
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
139-
struct PathfindingItem {
140-
/// The stop duration in milliseconds, None if the train does not stop.
141-
duration: Option<u64>,
142-
/// The associated location
143-
location: PathItemLocation,
144-
/// Time at which the train should arrive at the location, if specified
145-
timing_data: Option<StepTimingData>,
146-
}
147-
148-
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
149-
struct StepTimingData {
150-
/// Time at which the train should arrive at the location
151-
arrival_time: DateTime<Utc>,
152-
/// The train may arrive up to this duration before the expected arrival time
153-
arrival_time_tolerance_before: u64,
154-
/// The train may arrive up to this duration after the expected arrival time
155-
arrival_time_tolerance_after: u64,
156-
}
157-
15879
#[derive(Debug, Default, Clone, Serialize, Deserialize, IntoParams, ToSchema)]
15980
struct InfraIdQueryParam {
16081
infra: i64,
@@ -257,20 +178,9 @@ async fn stdcm(
257178
}
258179
};
259180

260-
let earliest_step_tolerance_window = get_earliest_step_tolerance_window(&stdcm_request);
261-
let maximum_departure_delay = get_maximum_departure_delay(
262-
&stdcm_request,
263-
simulation_run_time,
264-
earliest_step_tolerance_window,
265-
);
266-
// Maximum duration between train departure and arrival, including all stops
267-
let maximum_run_time = stdcm_request
268-
.maximum_run_time
269-
.unwrap_or(2 * simulation_run_time + get_total_stop_time(&stdcm_request));
270-
271-
let earliest_departure_time = get_earliest_departure_time(&stdcm_request, maximum_run_time);
272-
let latest_simulation_end = earliest_departure_time
273-
+ Duration::milliseconds((maximum_run_time + earliest_step_tolerance_window) as i64);
181+
let earliest_departure_time = stdcm_request.get_earliest_departure_time(simulation_run_time);
182+
let maximum_run_time = stdcm_request.get_maximum_run_time(simulation_run_time);
183+
let latest_simulation_end = stdcm_request.get_latest_simulation_end(simulation_run_time);
274184

275185
// 3. Get scheduled train requirements
276186
let trains_requirements = build_train_requirements(
@@ -319,7 +229,7 @@ async fn stdcm(
319229
path_items,
320230
start_time: earliest_departure_time,
321231
trains_requirements: trains_requirements.clone(),
322-
maximum_departure_delay,
232+
maximum_departure_delay: stdcm_request.get_maximum_departure_delay(simulation_run_time),
323233
maximum_run_time,
324234
speed_limit_tag: stdcm_request.speed_limit_tags,
325235
time_gap_before: stdcm_request.time_gap_before,
@@ -340,18 +250,18 @@ async fn stdcm(
340250
// 8. Handle PathNotFound response of STDCM
341251
if let STDCMResponse::PathNotFound = stdcm_response {
342252
let stdcm_response = handle_path_not_found(
343-
virtual_train_schedule,
253+
core_client,
344254
train_schedules,
345255
simulations,
256+
&work_schedules,
257+
virtual_train_schedule,
346258
virtual_train_sim_result,
347259
virtual_train_pathfinding_result,
348260
earliest_departure_time,
349261
maximum_run_time,
350262
latest_simulation_end,
351-
&work_schedules,
352263
infra_id,
353264
infra.version,
354-
core_client,
355265
)
356266
.await?;
357267

@@ -363,18 +273,18 @@ async fn stdcm(
363273

364274
#[allow(clippy::too_many_arguments)]
365275
async fn handle_path_not_found(
366-
virtual_train_schedule: TrainSchedule,
276+
core_client: Arc<CoreClient>,
367277
train_schedules: Vec<TrainSchedule>,
368278
simulations: Vec<(SimulationResponse, PathfindingResult)>,
279+
work_schedules: &[WorkSchedule],
280+
virtual_train_schedule: TrainSchedule,
369281
virtual_train_sim_result: SimulationResponse,
370282
virtual_train_pathfinding_result: PathfindingResult,
371283
earliest_departure_time: DateTime<Utc>,
372284
maximum_run_time: u64,
373285
latest_simulation_end: DateTime<Utc>,
374-
work_schedules: &[WorkSchedule],
375286
infra_id: i64,
376287
infra_version: String,
377-
core_client: Arc<CoreClient>,
378288
) -> Result<STDCMResponse> {
379289
let virtual_train_id = virtual_train_schedule.id;
380290

@@ -518,63 +428,6 @@ fn is_resource_in_range(
518428
abs_resource_start_time <= latest_sim_time && abs_resource_end_time >= earliest_sim_time
519429
}
520430

521-
// Returns the maximum departure delay for the train.
522-
fn get_maximum_departure_delay(
523-
data: &STDCMRequestPayload,
524-
simulation_run_time: u64,
525-
earliest_step_tolerance_window: u64,
526-
) -> u64 {
527-
data.maximum_departure_delay
528-
.unwrap_or(simulation_run_time + earliest_step_tolerance_window)
529-
}
530-
531-
/// Returns the earliest time at which the train may start
532-
fn get_earliest_departure_time(data: &STDCMRequestPayload, maximum_run_time: u64) -> DateTime<Utc> {
533-
// Prioritize: start time, or first step time, or (first specified time - max run time)
534-
data.start_time.unwrap_or(
535-
data.steps
536-
.first()
537-
.and_then(|step| step.timing_data.clone())
538-
.and_then(|data| {
539-
Option::from(
540-
data.arrival_time
541-
- Duration::milliseconds(data.arrival_time_tolerance_before as i64),
542-
)
543-
})
544-
.unwrap_or(
545-
get_earliest_step_time(data) - Duration::milliseconds(maximum_run_time as i64),
546-
),
547-
)
548-
}
549-
550-
/// Returns the earliest time that has been set on any step
551-
fn get_earliest_step_time(data: &STDCMRequestPayload) -> DateTime<Utc> {
552-
// Get the earliest time that has been specified for any step
553-
data.start_time
554-
.or_else(|| {
555-
data.steps
556-
.iter()
557-
.flat_map(|step| step.timing_data.iter())
558-
.map(|data| {
559-
data.arrival_time
560-
- Duration::milliseconds(data.arrival_time_tolerance_before as i64)
561-
})
562-
.next()
563-
})
564-
.expect("No time specified for stdcm request")
565-
}
566-
567-
/// Returns the earliest tolerance window that has been set on any step
568-
fn get_earliest_step_tolerance_window(data: &STDCMRequestPayload) -> u64 {
569-
// Get the earliest time window that has been specified for any step, if maximum_run_time is not none
570-
data.steps
571-
.iter()
572-
.flat_map(|step| step.timing_data.iter())
573-
.map(|data| data.arrival_time_tolerance_before + data.arrival_time_tolerance_after)
574-
.next()
575-
.unwrap_or(0)
576-
}
577-
578431
/// Returns a `Result` containing:
579432
/// * `TrainSchedule` - The generated train schedule based on the provided data.
580433
/// * `SimulationResponse` - Simulation response.
@@ -583,15 +436,15 @@ async fn simulate_train_run(
583436
db_pool: Arc<DbConnectionPoolV2>,
584437
valkey_client: Arc<ValkeyClient>,
585438
core_client: Arc<CoreClient>,
586-
data: &STDCMRequestPayload,
439+
stdcm_request: &STDCMRequestPayload,
587440
infra: &Infra,
588441
rolling_stock: &RollingStockModel,
589442
timetable_id: i64,
590443
) -> Result<(TrainSchedule, SimulationResponse, PathfindingResult)> {
591444
// Doesn't matter for now, but eventually it will affect tmp speed limits
592-
let approx_start_time = get_earliest_step_time(data);
445+
let approx_start_time = stdcm_request.get_earliest_step_time();
593446

594-
let path = convert_steps(&data.steps);
447+
let path = convert_steps(&stdcm_request.steps);
595448
let last_step = path.last().expect("empty step list");
596449

597450
let train_schedule = TrainSchedule {
@@ -609,12 +462,12 @@ async fn simulate_train_run(
609462
reception_signal: ReceptionSignal::Open,
610463
locked: false,
611464
}],
612-
margins: build_single_margin(data.margin),
465+
margins: build_single_margin(stdcm_request.margin),
613466
initial_speed: 0.0,
614-
comfort: data.comfort,
467+
comfort: stdcm_request.comfort,
615468
path,
616469
constraint_distribution: Default::default(),
617-
speed_limit_tag: data.speed_limit_tags.clone(),
470+
speed_limit_tag: stdcm_request.speed_limit_tags.clone(),
618471
power_restrictions: vec![],
619472
options: Default::default(),
620473
};
@@ -632,26 +485,6 @@ async fn simulate_train_run(
632485
Ok((train_schedule, sim_result, pathfinding_result))
633486
}
634487

635-
/// Returns the request's total stop time
636-
fn get_total_stop_time(data: &STDCMRequestPayload) -> u64 {
637-
data.steps
638-
.iter()
639-
.map(|step: &PathfindingItem| step.duration.unwrap_or_default())
640-
.sum()
641-
}
642-
643-
/// Convert the list of pathfinding items into a list of path item
644-
fn convert_steps(steps: &[PathfindingItem]) -> Vec<PathItem> {
645-
steps
646-
.iter()
647-
.map(|step| PathItem {
648-
id: Default::default(),
649-
deleted: false,
650-
location: step.location.clone(),
651-
})
652-
.collect()
653-
}
654-
655488
/// Build a margins object with one margin value covering the entire range
656489
fn build_single_margin(margin: Option<MarginValue>) -> Margins {
657490
match margin {
@@ -800,6 +633,7 @@ mod tests {
800633
use crate::core::simulation::CompleteReportTrain;
801634
use crate::core::simulation::ElectricalProfiles;
802635
use crate::core::simulation::ReportTrain;
636+
use crate::core::simulation::SimulationParameters;
803637
use crate::core::simulation::SimulationResponse;
804638
use crate::core::simulation::SpeedLimitProperties;
805639
use crate::core::stdcm::STDCMResponse;

0 commit comments

Comments
 (0)