@@ -4,7 +4,7 @@ use actix_web::web::Json;
4
4
use actix_web:: web:: Path ;
5
5
use actix_web:: web:: Query ;
6
6
use chrono:: Utc ;
7
- use chrono:: { DateTime , NaiveDateTime , TimeZone } ;
7
+ use chrono:: { DateTime , Duration , NaiveDateTime , TimeZone } ;
8
8
use editoast_derive:: EditoastError ;
9
9
use editoast_schemas:: train_schedule:: MarginValue ;
10
10
use editoast_schemas:: train_schedule:: PathItemLocation ;
@@ -19,10 +19,10 @@ use utoipa::IntoParams;
19
19
use utoipa:: ToSchema ;
20
20
21
21
use crate :: core:: v2:: simulation:: SimulationResponse ;
22
- use crate :: core:: v2:: stdcm:: STDCMRequest ;
23
22
use crate :: core:: v2:: stdcm:: STDCMResponse ;
24
23
use crate :: core:: v2:: stdcm:: TrainRequirement ;
25
24
use crate :: core:: v2:: stdcm:: { STDCMPathItem , STDCMWorkSchedule , UndirectedTrackRange } ;
25
+ use crate :: core:: v2:: stdcm:: { STDCMRequest , STDCMStepTimingData } ;
26
26
use crate :: core:: AsCoreRequest ;
27
27
use crate :: core:: CoreClient ;
28
28
use crate :: error:: Result ;
@@ -48,6 +48,7 @@ crate::routes! {
48
48
editoast_common:: schemas! {
49
49
STDCMRequestPayload ,
50
50
PathfindingItem ,
51
+ StepTimingData ,
51
52
}
52
53
53
54
#[ derive( Debug , Error , EditoastError , Serialize ) ]
@@ -70,11 +71,13 @@ enum STDCMError {
70
71
/// An STDCM request
71
72
#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , ToSchema ) ]
72
73
pub struct STDCMRequestPayload {
73
- start_time : DateTime < Utc > ,
74
+ /// Deprecated, first step arrival time should be used instead
75
+ start_time : Option < DateTime < Utc > > ,
74
76
steps : Vec < PathfindingItem > ,
75
77
rolling_stock_id : i64 ,
76
78
comfort : Comfort ,
77
79
/// By how long we can shift the departure time in milliseconds
80
+ /// Deprecated, first step data should be used instead
78
81
#[ serde( default = "default_maximum_departure_delay" ) ]
79
82
#[ schema( default = default_maximum_departure_delay) ]
80
83
maximum_departure_delay : u64 ,
@@ -106,6 +109,18 @@ struct PathfindingItem {
106
109
duration : Option < u64 > ,
107
110
/// The associated location
108
111
location : PathItemLocation ,
112
+ /// Time at which the train should arrive at the location, if specified
113
+ timing_data : Option < StepTimingData > ,
114
+ }
115
+
116
+ #[ derive( Debug , Serialize , Deserialize , PartialEq , Clone , ToSchema ) ]
117
+ struct StepTimingData {
118
+ /// Time at which the train should arrive at the location
119
+ arrival_time : DateTime < Utc > ,
120
+ /// The train may arrive up to this duration before the expected arrival time
121
+ arrival_time_tolerance_before : u64 ,
122
+ /// The train may arrive up to this duration after the expected arrival time
123
+ arrival_time_tolerance_after : u64 ,
109
124
}
110
125
111
126
const TWO_HOURS_IN_MILLISECONDS : u64 = 2 * 60 * 60 * 60 ;
@@ -209,6 +224,8 @@ async fn stdcm(
209
224
}
210
225
} ;
211
226
227
+ let departure_time = get_earliest_departure_time ( & data, maximum_run_time) ;
228
+
212
229
// 3. Parse stdcm path items
213
230
let path_items = parse_stdcm_steps ( conn, & data, & infra) . await ?;
214
231
@@ -223,7 +240,7 @@ async fn stdcm(
223
240
. clone ( ) ,
224
241
comfort : data. comfort ,
225
242
path_items,
226
- start_time : data . start_time ,
243
+ start_time : departure_time ,
227
244
trains_requirements,
228
245
maximum_departure_delay : Some ( data. maximum_departure_delay ) ,
229
246
maximum_run_time,
@@ -234,7 +251,7 @@ async fn stdcm(
234
251
time_step : Some ( 2000 ) ,
235
252
work_schedules : build_work_schedules (
236
253
conn,
237
- data . start_time ,
254
+ departure_time ,
238
255
data. maximum_departure_delay ,
239
256
maximum_run_time,
240
257
)
@@ -246,6 +263,34 @@ async fn stdcm(
246
263
Ok ( Json ( stdcm_response) )
247
264
}
248
265
266
+ /// Returns the earliest time at which the train may start
267
+ fn get_earliest_departure_time ( data : & STDCMRequestPayload , maximum_run_time : u64 ) -> DateTime < Utc > {
268
+ // Prioritize: start time, or first step time, or (first specified time - max run time)
269
+ data. start_time . unwrap_or (
270
+ data. steps
271
+ . first ( )
272
+ . and_then ( |step| step. timing_data . clone ( ) )
273
+ . and_then ( |data| Option :: from ( data. arrival_time ) )
274
+ . unwrap_or (
275
+ get_earliest_step_time ( data) - Duration :: milliseconds ( maximum_run_time as i64 ) ,
276
+ ) ,
277
+ )
278
+ }
279
+
280
+ /// Returns the earliest time that has been set on any step
281
+ fn get_earliest_step_time ( data : & STDCMRequestPayload ) -> DateTime < Utc > {
282
+ // Get the earliest time that has been specified for any step
283
+ data. start_time
284
+ . or_else ( || {
285
+ data. steps
286
+ . iter ( )
287
+ . flat_map ( |step| step. timing_data . iter ( ) )
288
+ . map ( |data| data. arrival_time )
289
+ . next ( )
290
+ } )
291
+ . expect ( "No time specified for stdcm request" )
292
+ }
293
+
249
294
/// get the maximum run time, compute it if unspecified.
250
295
/// returns an enum with either the result or a SimulationResponse if it failed
251
296
async fn get_maximum_run_time (
@@ -263,13 +308,16 @@ async fn get_maximum_run_time(
263
308
} ) ;
264
309
}
265
310
311
+ // Doesn't matter for now, but eventually it will affect tmp speed limits
312
+ let approx_start_time = get_earliest_step_time ( data) ;
313
+
266
314
let train_schedule = TrainSchedule {
267
315
id : 0 ,
268
316
train_name : "" . to_string ( ) ,
269
317
labels : vec ! [ ] ,
270
318
rolling_stock_name : rolling_stock. name . clone ( ) ,
271
319
timetable_id,
272
- start_time : data . start_time ,
320
+ start_time : approx_start_time ,
273
321
schedule : vec ! [ ] ,
274
322
margins : build_single_margin ( data. margin ) ,
275
323
initial_speed : 0.0 ,
@@ -372,21 +420,26 @@ async fn parse_stdcm_steps(
372
420
) -> Result < Vec < STDCMPathItem > > {
373
421
let path_items = data. steps . clone ( ) ;
374
422
let mut locations = Vec :: with_capacity ( path_items. len ( ) ) ;
375
- let mut durations = Vec :: with_capacity ( path_items. len ( ) ) ;
376
423
for item in path_items {
377
424
locations. push ( item. location ) ;
378
- durations. push ( item. duration ) ;
379
425
}
380
426
381
427
let track_offsets = extract_location_from_path_items ( conn, infra. id , & locations) . await ?;
382
428
let track_offsets = track_offsets. map_err :: < STDCMError , _ > ( |err| err. into ( ) ) ?;
383
429
384
430
Ok ( track_offsets
385
431
. iter ( )
386
- . zip ( durations )
387
- . map ( |( track_offset, duration ) | STDCMPathItem {
388
- stop_duration : duration,
432
+ . zip ( & data . steps )
433
+ . map ( |( track_offset, path_item ) | STDCMPathItem {
434
+ stop_duration : path_item . duration ,
389
435
locations : track_offset. to_vec ( ) ,
436
+ step_timing_data : path_item. timing_data . as_ref ( ) . map ( |timing_data| {
437
+ STDCMStepTimingData {
438
+ arrival_time : timing_data. arrival_time ,
439
+ arrival_time_tolerance_before : timing_data. arrival_time_tolerance_before ,
440
+ arrival_time_tolerance_after : timing_data. arrival_time_tolerance_after ,
441
+ }
442
+ } ) ,
390
443
} )
391
444
. collect ( ) )
392
445
}
0 commit comments