@@ -10,10 +10,10 @@ use editoast_derive::EditoastError;
10
10
use editoast_models:: DbConnection ;
11
11
use editoast_models:: DbConnectionPoolV2 ;
12
12
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 ;
14
15
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 ;
17
17
use itertools:: Itertools ;
18
18
use serde:: Deserialize ;
19
19
use serde:: Serialize ;
@@ -24,14 +24,15 @@ use thiserror::Error;
24
24
use utoipa:: IntoParams ;
25
25
use utoipa:: ToSchema ;
26
26
27
+ use super :: stdcm_request_payload:: convert_steps;
28
+ use super :: stdcm_request_payload:: STDCMRequestPayload ;
27
29
use super :: SelectionSettings ;
28
30
use crate :: core:: conflict_detection:: ConflictDetectionRequest ;
29
31
use crate :: core:: conflict_detection:: TrainRequirements ;
30
32
use crate :: core:: conflict_detection:: WorkSchedulesRequest ;
31
33
use crate :: core:: pathfinding:: InvalidPathItem ;
32
34
use crate :: core:: pathfinding:: PathfindingResult ;
33
35
use crate :: core:: simulation:: PhysicsRollingStock ;
34
- use crate :: core:: simulation:: SimulationParameters ;
35
36
use crate :: core:: simulation:: { RoutingRequirement , SimulationResponse , SpacingRequirement } ;
36
37
use crate :: core:: stdcm:: STDCMPathItem ;
37
38
use crate :: core:: stdcm:: STDCMRequest ;
@@ -61,12 +62,6 @@ crate::routes! {
61
62
"/stdcm" => stdcm,
62
63
}
63
64
64
- editoast_common:: schemas! {
65
- STDCMRequestPayload ,
66
- PathfindingItem ,
67
- StepTimingData ,
68
- }
69
-
70
65
#[ derive( Debug , Error , EditoastError , Serialize ) ]
71
66
#[ editoast_error( base_id = "stdcm_v2" ) ]
72
67
enum STDCMError {
@@ -81,80 +76,6 @@ enum STDCMError {
81
76
InvalidPathItems { items : Vec < InvalidPathItem > } ,
82
77
}
83
78
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
-
158
79
#[ derive( Debug , Default , Clone , Serialize , Deserialize , IntoParams , ToSchema ) ]
159
80
struct InfraIdQueryParam {
160
81
infra : i64 ,
@@ -257,20 +178,9 @@ async fn stdcm(
257
178
}
258
179
} ;
259
180
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) ;
274
184
275
185
// 3. Get scheduled train requirements
276
186
let trains_requirements = build_train_requirements (
@@ -319,7 +229,7 @@ async fn stdcm(
319
229
path_items,
320
230
start_time : earliest_departure_time,
321
231
trains_requirements : trains_requirements. clone ( ) ,
322
- maximum_departure_delay,
232
+ maximum_departure_delay : stdcm_request . get_maximum_departure_delay ( simulation_run_time ) ,
323
233
maximum_run_time,
324
234
speed_limit_tag : stdcm_request. speed_limit_tags ,
325
235
time_gap_before : stdcm_request. time_gap_before ,
@@ -340,18 +250,18 @@ async fn stdcm(
340
250
// 8. Handle PathNotFound response of STDCM
341
251
if let STDCMResponse :: PathNotFound = stdcm_response {
342
252
let stdcm_response = handle_path_not_found (
343
- virtual_train_schedule ,
253
+ core_client ,
344
254
train_schedules,
345
255
simulations,
256
+ & work_schedules,
257
+ virtual_train_schedule,
346
258
virtual_train_sim_result,
347
259
virtual_train_pathfinding_result,
348
260
earliest_departure_time,
349
261
maximum_run_time,
350
262
latest_simulation_end,
351
- & work_schedules,
352
263
infra_id,
353
264
infra. version ,
354
- core_client,
355
265
)
356
266
. await ?;
357
267
@@ -363,18 +273,18 @@ async fn stdcm(
363
273
364
274
#[ allow( clippy:: too_many_arguments) ]
365
275
async fn handle_path_not_found (
366
- virtual_train_schedule : TrainSchedule ,
276
+ core_client : Arc < CoreClient > ,
367
277
train_schedules : Vec < TrainSchedule > ,
368
278
simulations : Vec < ( SimulationResponse , PathfindingResult ) > ,
279
+ work_schedules : & [ WorkSchedule ] ,
280
+ virtual_train_schedule : TrainSchedule ,
369
281
virtual_train_sim_result : SimulationResponse ,
370
282
virtual_train_pathfinding_result : PathfindingResult ,
371
283
earliest_departure_time : DateTime < Utc > ,
372
284
maximum_run_time : u64 ,
373
285
latest_simulation_end : DateTime < Utc > ,
374
- work_schedules : & [ WorkSchedule ] ,
375
286
infra_id : i64 ,
376
287
infra_version : String ,
377
- core_client : Arc < CoreClient > ,
378
288
) -> Result < STDCMResponse > {
379
289
let virtual_train_id = virtual_train_schedule. id ;
380
290
@@ -518,63 +428,6 @@ fn is_resource_in_range(
518
428
abs_resource_start_time <= latest_sim_time && abs_resource_end_time >= earliest_sim_time
519
429
}
520
430
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
-
578
431
/// Returns a `Result` containing:
579
432
/// * `TrainSchedule` - The generated train schedule based on the provided data.
580
433
/// * `SimulationResponse` - Simulation response.
@@ -583,15 +436,15 @@ async fn simulate_train_run(
583
436
db_pool : Arc < DbConnectionPoolV2 > ,
584
437
valkey_client : Arc < ValkeyClient > ,
585
438
core_client : Arc < CoreClient > ,
586
- data : & STDCMRequestPayload ,
439
+ stdcm_request : & STDCMRequestPayload ,
587
440
infra : & Infra ,
588
441
rolling_stock : & RollingStockModel ,
589
442
timetable_id : i64 ,
590
443
) -> Result < ( TrainSchedule , SimulationResponse , PathfindingResult ) > {
591
444
// 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 ( ) ;
593
446
594
- let path = convert_steps ( & data . steps ) ;
447
+ let path = convert_steps ( & stdcm_request . steps ) ;
595
448
let last_step = path. last ( ) . expect ( "empty step list" ) ;
596
449
597
450
let train_schedule = TrainSchedule {
@@ -609,12 +462,12 @@ async fn simulate_train_run(
609
462
reception_signal: ReceptionSignal :: Open ,
610
463
locked: false ,
611
464
} ] ,
612
- margins : build_single_margin ( data . margin ) ,
465
+ margins : build_single_margin ( stdcm_request . margin ) ,
613
466
initial_speed : 0.0 ,
614
- comfort : data . comfort ,
467
+ comfort : stdcm_request . comfort ,
615
468
path,
616
469
constraint_distribution : Default :: default ( ) ,
617
- speed_limit_tag : data . speed_limit_tags . clone ( ) ,
470
+ speed_limit_tag : stdcm_request . speed_limit_tags . clone ( ) ,
618
471
power_restrictions : vec ! [ ] ,
619
472
options : Default :: default ( ) ,
620
473
} ;
@@ -632,26 +485,6 @@ async fn simulate_train_run(
632
485
Ok ( ( train_schedule, sim_result, pathfinding_result) )
633
486
}
634
487
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
-
655
488
/// Build a margins object with one margin value covering the entire range
656
489
fn build_single_margin ( margin : Option < MarginValue > ) -> Margins {
657
490
match margin {
@@ -800,6 +633,7 @@ mod tests {
800
633
use crate :: core:: simulation:: CompleteReportTrain ;
801
634
use crate :: core:: simulation:: ElectricalProfiles ;
802
635
use crate :: core:: simulation:: ReportTrain ;
636
+ use crate :: core:: simulation:: SimulationParameters ;
803
637
use crate :: core:: simulation:: SimulationResponse ;
804
638
use crate :: core:: simulation:: SpeedLimitProperties ;
805
639
use crate :: core:: stdcm:: STDCMResponse ;
0 commit comments