Skip to content

Commit 1ede6d0

Browse files
committed
editoast: stdcm: add arrival time parameters
1 parent 6e5bd0a commit 1ede6d0

File tree

6 files changed

+94
-14
lines changed

6 files changed

+94
-14
lines changed

core/kt-osrd-utils/src/main/kotlin/fr/sncf/osrd/utils/json/UnitJsonAdapters.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ class DurationAdapter : JsonAdapter<Duration?>() {
7575
*/
7676
class DateAdapter : JsonAdapter<ZonedDateTime>() {
7777
@FromJson
78-
override fun fromJson(reader: JsonReader): ZonedDateTime {
78+
override fun fromJson(reader: JsonReader): ZonedDateTime? {
79+
if (reader.peek() == JsonReader.Token.NULL) {
80+
reader.skipValue()
81+
return null
82+
}
7983
return ZonedDateTime.parse(reader.nextString())
8084
}
8185

core/src/main/kotlin/fr/sncf/osrd/api/api_v2/stdcm/STDCMRequestV2.kt

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class STDCMRequestV2(
6060
class STDCMPathItem(
6161
val locations: List<TrackLocation>,
6262
@Json(name = "stop_duration") val stopDuration: Duration?,
63+
@Json(name = "arrival_time") val arrivalTime: ZonedDateTime?,
64+
@Json(name = "arrival_time_tolerance_before") val arrivalTimeToleranceBefore: Duration?,
65+
@Json(name = "arrival_time_tolerance_after") val arrivalTimeToleranceAfter: Duration?,
6366
)
6467

6568
class TrackOffset(val track: String, val offset: Offset<TrackSection>)

editoast/openapi.yaml

+21-2
Original file line numberDiff line numberDiff line change
@@ -5202,6 +5202,23 @@ components:
52025202
type: object
52035203
PathfindingItem:
52045204
properties:
5205+
arrival_time:
5206+
description: Time at which the train should arrive at the location, if specified
5207+
format: date-time
5208+
nullable: true
5209+
type: string
5210+
arrival_time_tolerance_after:
5211+
description: The train may arrive up to this duration after the expected arrival time
5212+
format: int64
5213+
minimum: 0
5214+
nullable: true
5215+
type: integer
5216+
arrival_time_tolerance_before:
5217+
description: The train may arrive up to this duration before the expected arrival time
5218+
format: int64
5219+
minimum: 0
5220+
nullable: true
5221+
type: integer
52055222
duration:
52065223
description: The stop duration in milliseconds, None if the train does not stop.
52075224
format: int64
@@ -6894,7 +6911,9 @@ components:
68946911
nullable: true
68956912
type: string
68966913
start_time:
6914+
description: Deprecated, first step arrival time should be used instead
68976915
format: date-time
6916+
nullable: true
68986917
type: string
68996918
steps:
69006919
items:
@@ -6919,7 +6938,6 @@ components:
69196938
minimum: 0
69206939
type: integer
69216940
required:
6922-
- start_time
69236941
- steps
69246942
- rolling_stock_id
69256943
- comfort
@@ -12451,7 +12469,9 @@ paths:
1245112469
nullable: true
1245212470
type: string
1245312471
start_time:
12472+
description: Deprecated, first step arrival time should be used instead
1245412473
format: date-time
12474+
nullable: true
1245512475
type: string
1245612476
steps:
1245712477
items:
@@ -12476,7 +12496,6 @@ paths:
1247612496
minimum: 0
1247712497
type: integer
1247812498
required:
12479-
- start_time
1248012499
- steps
1248112500
- rolling_stock_id
1248212501
- comfort

editoast/src/core/v2/stdcm.rs

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ pub struct STDCMPathItem {
6464
pub locations: Vec<TrackOffset>,
6565
/// Stop duration in milliseconds. None if the train does not stop at this path item.
6666
pub stop_duration: Option<u64>,
67+
/// Time the train should arrive at this point, if specified
68+
pub arrival_time: Option<DateTime<Utc>>,
69+
/// Tolerance for the arrival time, when it arrives before the expected time, in ms
70+
pub arrival_time_tolerance_before: Option<u64>,
71+
/// Tolerance for the arrival time, when it arrives after the expected time, in ms
72+
pub arrival_time_tolerance_after: Option<u64>,
6773
}
6874

6975
/// Lighter description of a work schedule, only contains what's relevant

editoast/src/views/v2/timetable/stdcm.rs

+51-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use actix_web::web::Json;
44
use actix_web::web::Path;
55
use actix_web::web::Query;
66
use chrono::Utc;
7-
use chrono::{DateTime, NaiveDateTime, TimeZone};
7+
use chrono::{DateTime, Duration, NaiveDateTime, TimeZone};
88
use editoast_derive::EditoastError;
99
use editoast_schemas::train_schedule::MarginValue;
1010
use editoast_schemas::train_schedule::PathItemLocation;
@@ -70,7 +70,8 @@ enum STDCMError {
7070
/// An STDCM request
7171
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
7272
pub struct STDCMRequestPayload {
73-
start_time: DateTime<Utc>,
73+
/// Deprecated, first step arrival time should be used instead
74+
start_time: Option<DateTime<Utc>>,
7475
steps: Vec<PathfindingItem>,
7576
rolling_stock_id: i64,
7677
comfort: Comfort,
@@ -106,6 +107,12 @@ struct PathfindingItem {
106107
duration: Option<u64>,
107108
/// The associated location
108109
location: PathItemLocation,
110+
/// Time at which the train should arrive at the location, if specified
111+
arrival_time: Option<DateTime<Utc>>,
112+
/// The train may arrive up to this duration before the expected arrival time
113+
arrival_time_tolerance_before: Option<u64>,
114+
/// The train may arrive up to this duration after the expected arrival time
115+
arrival_time_tolerance_after: Option<u64>,
109116
}
110117

111118
const TWO_HOURS_IN_MILLISECONDS: u64 = 2 * 60 * 60 * 60;
@@ -209,6 +216,8 @@ async fn stdcm(
209216
}
210217
};
211218

219+
let departure_time = get_earliest_departure_time(&data, maximum_run_time);
220+
212221
// 3. Parse stdcm path items
213222
let path_items = parse_stdcm_steps(conn, &data, &infra).await?;
214223

@@ -223,7 +232,7 @@ async fn stdcm(
223232
.clone(),
224233
comfort: data.comfort,
225234
path_items,
226-
start_time: data.start_time,
235+
start_time: departure_time,
227236
trains_requirements,
228237
maximum_departure_delay: Some(data.maximum_departure_delay),
229238
maximum_run_time,
@@ -234,7 +243,7 @@ async fn stdcm(
234243
time_step: Some(2000),
235244
work_schedules: build_work_schedules(
236245
conn,
237-
data.start_time,
246+
departure_time,
238247
data.maximum_departure_delay,
239248
maximum_run_time,
240249
)
@@ -246,6 +255,34 @@ async fn stdcm(
246255
Ok(Json(stdcm_response))
247256
}
248257

258+
/// Returns the earliest time at which the train may start
259+
fn get_earliest_departure_time(data: &STDCMRequestPayload, maximum_run_time: u64) -> DateTime<Utc> {
260+
// Prioritize: start time, or first step time, or (first specified time - max run time)
261+
data.start_time.unwrap_or(
262+
data.steps
263+
.first()
264+
.and_then(|step| step.arrival_time)
265+
.unwrap_or(
266+
get_earliest_step_time(data) - Duration::milliseconds(maximum_run_time as i64),
267+
),
268+
)
269+
}
270+
271+
/// Returns the earliest time that has been set on any step
272+
fn get_earliest_step_time(data: &STDCMRequestPayload) -> DateTime<Utc> {
273+
// Get the earliest time that has been specified for any step
274+
*data
275+
.start_time
276+
.as_ref()
277+
.or_else(|| {
278+
data.steps
279+
.iter()
280+
.flat_map(|step| step.arrival_time.iter())
281+
.next()
282+
})
283+
.expect("No time specified for stdcm request")
284+
}
285+
249286
/// get the maximum run time, compute it if unspecified.
250287
/// returns an enum with either the result or a SimulationResponse if it failed
251288
async fn get_maximum_run_time(
@@ -263,13 +300,16 @@ async fn get_maximum_run_time(
263300
});
264301
}
265302

303+
// Doesn't matter for now, but eventually it will affect tmp speed limits
304+
let approx_start_time = get_earliest_step_time(data);
305+
266306
let train_schedule = TrainSchedule {
267307
id: 0,
268308
train_name: "".to_string(),
269309
labels: vec![],
270310
rolling_stock_name: rolling_stock.name.clone(),
271311
timetable_id,
272-
start_time: data.start_time,
312+
start_time: approx_start_time,
273313
schedule: vec![],
274314
margins: build_single_margin(data.margin),
275315
initial_speed: 0.0,
@@ -372,21 +412,22 @@ async fn parse_stdcm_steps(
372412
) -> Result<Vec<STDCMPathItem>> {
373413
let path_items = data.steps.clone();
374414
let mut locations = Vec::with_capacity(path_items.len());
375-
let mut durations = Vec::with_capacity(path_items.len());
376415
for item in path_items {
377416
locations.push(item.location);
378-
durations.push(item.duration);
379417
}
380418

381419
let track_offsets = extract_location_from_path_items(conn, infra.id, &locations).await?;
382420
let track_offsets = track_offsets.map_err::<STDCMError, _>(|err| err.into())?;
383421

384422
Ok(track_offsets
385423
.iter()
386-
.zip(durations)
387-
.map(|(track_offset, duration)| STDCMPathItem {
388-
stop_duration: duration,
424+
.zip(&data.steps)
425+
.map(|(track_offset, path_item)| STDCMPathItem {
426+
stop_duration: path_item.duration,
389427
locations: track_offset.to_vec(),
428+
arrival_time: path_item.arrival_time,
429+
arrival_time_tolerance_before: path_item.arrival_time_tolerance_before,
430+
arrival_time_tolerance_after: path_item.arrival_time_tolerance_after,
390431
})
391432
.collect())
392433
}

front/src/common/api/generatedEditoastApi.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1760,7 +1760,8 @@ export type PostV2TimetableByIdStdcmApiArg = {
17601760
rolling_stock_id: number;
17611761
/** Train categories for speed limits */
17621762
speed_limit_tags?: string | null;
1763-
start_time: string;
1763+
/** Deprecated, first step arrival time should be used instead */
1764+
start_time?: string | null;
17641765
steps: PathfindingItem[];
17651766
/** Margin after the train passage in milliseconds
17661767
@@ -3709,6 +3710,12 @@ export type PathItemLocation =
37093710
uic: number;
37103711
};
37113712
export type PathfindingItem = {
3713+
/** Time at which the train should arrive at the location, if specified */
3714+
arrival_time?: string | null;
3715+
/** The train may arrive up to this duration after the expected arrival time */
3716+
arrival_time_tolerance_after?: number | null;
3717+
/** The train may arrive up to this duration before the expected arrival time */
3718+
arrival_time_tolerance_before?: number | null;
37123719
/** The stop duration in milliseconds, None if the train does not stop. */
37133720
duration?: number | null;
37143721
location: PathItemLocation;

0 commit comments

Comments
 (0)