@@ -67,6 +67,8 @@ pub enum TimetableImportPathLocation {
67
67
TrackOffsetLocation { track_section : String , offset : f64 } ,
68
68
#[ serde( rename = "operational_point" ) ]
69
69
OperationalPointLocation { uic : i64 } ,
70
+ #[ serde( rename = "operational_point_id" ) ]
71
+ OperationalPointIdLocation { id : String } ,
70
72
}
71
73
72
74
#[ derive( Debug , Serialize , ToSchema ) ]
@@ -76,10 +78,19 @@ struct TimetableImportReport {
76
78
77
79
#[ derive( Debug , Clone , Serialize , ToSchema ) ]
78
80
enum TimetableImportError {
79
- RollingStockNotFound { name : String } ,
80
- OperationalPointNotFound { missing_uics : Vec < i64 > } ,
81
- PathfindingError { cause : InternalError } ,
82
- SimulationError { cause : InternalError } ,
81
+ RollingStockNotFound {
82
+ name : String ,
83
+ } ,
84
+ OperationalPointNotFound {
85
+ missing_uics : Vec < i64 > ,
86
+ missing_ids : Vec < String > ,
87
+ } ,
88
+ PathfindingError {
89
+ cause : InternalError ,
90
+ } ,
91
+ SimulationError {
92
+ cause : InternalError ,
93
+ } ,
83
94
}
84
95
85
96
#[ derive( Debug , Clone , Deserialize , ToSchema ) ]
@@ -183,19 +194,21 @@ async fn import_item(
183
194
pf_request. with_rolling_stocks ( & mut vec ! [ rolling_stock. clone( ) ] ) ;
184
195
// List operational points uic needed for this import
185
196
let ops_uic = ops_uic_from_path ( & import_item. path ) ;
197
+ let ops_id = ops_id_from_path ( & import_item. path ) ;
186
198
// Retrieve operational points
187
- let op_id_to_parts = match find_operation_points ( & ops_uic, infra_id, conn) . await ? {
188
- Ok ( op_id_to_parts) => op_id_to_parts,
189
- Err ( err) => {
190
- return Ok ( import_item
191
- . trains
192
- . into_iter ( )
193
- . map ( |train : TimetableImportTrain | ( train. name , err. clone ( ) ) )
194
- . collect ( ) ) ;
195
- }
196
- } ;
199
+ let ( op_uic_to_parts, op_id_to_parts) =
200
+ match find_operation_points ( & ops_uic, & ops_id, infra_id, conn) . await ? {
201
+ Ok ( ( op_uic_to_parts, op_id_to_parts) ) => ( op_uic_to_parts, op_id_to_parts) ,
202
+ Err ( err) => {
203
+ return Ok ( import_item
204
+ . trains
205
+ . into_iter ( )
206
+ . map ( |train : TimetableImportTrain | ( train. name , err. clone ( ) ) )
207
+ . collect ( ) ) ;
208
+ }
209
+ } ;
197
210
// Create waypoints
198
- let mut waypoints = waypoints_from_steps ( & import_item. path , & op_id_to_parts) ;
211
+ let mut waypoints = waypoints_from_steps ( & import_item. path , & op_uic_to_parts , & op_id_to_parts) ;
199
212
pf_request. with_waypoints ( & mut waypoints) ;
200
213
201
214
// Run pathfinding
@@ -295,30 +308,63 @@ async fn import_item(
295
308
296
309
async fn find_operation_points (
297
310
ops_uic : & [ i64 ] ,
311
+ ops_id : & [ String ] ,
298
312
infra_id : i64 ,
299
313
conn : & mut AsyncPgConnection ,
300
- ) -> Result < std:: result:: Result < HashMap < i64 , Vec < OperationalPointPart > > , TimetableImportError > > {
314
+ ) -> Result <
315
+ std:: result:: Result <
316
+ (
317
+ HashMap < i64 , Vec < OperationalPointPart > > ,
318
+ HashMap < String , Vec < OperationalPointPart > > ,
319
+ ) ,
320
+ TimetableImportError ,
321
+ > ,
322
+ > {
301
323
// Retrieve operational points
302
- let ops = OperationalPointModel :: retrieve_from_uic ( conn, infra_id, ops_uic) . await ?;
324
+ let ops_from_uic: Vec < OperationalPointModel > =
325
+ OperationalPointModel :: retrieve_from_uic ( conn, infra_id, ops_uic) . await ?;
326
+ let mut op_uic_to_parts = HashMap :: < _ , Vec < _ > > :: new ( ) ;
327
+ for op in ops_from_uic {
328
+ op_uic_to_parts
329
+ . entry ( op. data . 0 . extensions . identifier . unwrap ( ) . uic )
330
+ . or_default ( )
331
+ . extend ( op. data . 0 . parts ) ;
332
+ }
333
+ let mut missing_uics: Vec < i64 > = vec ! [ ] ;
334
+ // If we didn't find all the operational points, we can't run the pathfinding
335
+ if op_uic_to_parts. len ( ) != ops_uic. len ( ) {
336
+ ops_uic. iter ( ) . for_each ( |uic| {
337
+ if !op_uic_to_parts. contains_key ( uic) {
338
+ missing_uics. push ( * uic)
339
+ }
340
+ } ) ;
341
+ }
342
+
343
+ let ops_from_ids = OperationalPointModel :: retrieve_from_obj_ids ( conn, infra_id, ops_id) . await ?;
303
344
let mut op_id_to_parts = HashMap :: < _ , Vec < _ > > :: new ( ) ;
304
- for op in ops {
345
+ for op in ops_from_ids {
305
346
op_id_to_parts
306
- . entry ( op. data . 0 . extensions . identifier . unwrap ( ) . uic )
347
+ . entry ( op. obj_id )
307
348
. or_default ( )
308
349
. extend ( op. data . 0 . parts ) ;
309
350
}
310
351
// If we didn't find all the operational points, we can't run the pathfinding
311
- if op_id_to_parts. len ( ) != ops_uic. len ( ) {
312
- let missing_uics = ops_uic
313
- . iter ( )
314
- . filter ( |uic| !op_id_to_parts. contains_key ( uic) )
315
- . cloned ( )
316
- . collect ( ) ;
352
+ let mut missing_ids: Vec < String > = vec ! [ ] ;
353
+ if op_id_to_parts. len ( ) != ops_id. len ( ) {
354
+ ops_id. iter ( ) . for_each ( |id| {
355
+ if !op_id_to_parts. contains_key ( id) {
356
+ missing_ids. push ( id. to_string ( ) )
357
+ }
358
+ } ) ;
359
+ }
360
+ if missing_uics. len ( ) + missing_ids. len ( ) > 0 {
317
361
return Ok ( Err ( TimetableImportError :: OperationalPointNotFound {
318
362
missing_uics,
363
+ missing_ids,
319
364
} ) ) ;
320
365
}
321
- Ok ( Ok ( op_id_to_parts) )
366
+
367
+ Ok ( Ok ( ( op_uic_to_parts, op_id_to_parts) ) )
322
368
}
323
369
324
370
fn ops_uic_from_path ( path : & [ TimetableImportPathStep ] ) -> Vec < i64 > {
@@ -335,9 +381,24 @@ fn ops_uic_from_path(path: &[TimetableImportPathStep]) -> Vec<i64> {
335
381
ops_uic
336
382
}
337
383
384
+ fn ops_id_from_path ( path : & [ TimetableImportPathStep ] ) -> Vec < String > {
385
+ let mut ops_id = path
386
+ . iter ( )
387
+ . filter_map ( |step| match & step. location {
388
+ TimetableImportPathLocation :: OperationalPointIdLocation { id } => Some ( id. to_string ( ) ) ,
389
+ _ => None ,
390
+ } )
391
+ . collect :: < Vec < _ > > ( ) ;
392
+ // Remove duplicates
393
+ ops_id. sort ( ) ;
394
+ ops_id. dedup ( ) ;
395
+ ops_id
396
+ }
397
+
338
398
fn waypoints_from_steps (
339
399
path : & Vec < TimetableImportPathStep > ,
340
- op_id_to_parts : & HashMap < i64 , Vec < OperationalPointPart > > ,
400
+ op_uic_to_parts : & HashMap < i64 , Vec < OperationalPointPart > > ,
401
+ op_id_to_parts : & HashMap < String , Vec < OperationalPointPart > > ,
341
402
) -> Vec < Vec < Waypoint > > {
342
403
let mut res = PathfindingWaypoints :: new ( ) ;
343
404
for step in path {
@@ -346,12 +407,18 @@ fn waypoints_from_steps(
346
407
track_section,
347
408
offset,
348
409
} => Vec :: from ( Waypoint :: bidirectional ( track_section, * offset) ) ,
349
- TimetableImportPathLocation :: OperationalPointLocation { uic } => op_id_to_parts
410
+ TimetableImportPathLocation :: OperationalPointLocation { uic } => op_uic_to_parts
350
411
. get ( uic)
351
412
. unwrap ( )
352
413
. iter ( )
353
414
. flat_map ( |op_part| Waypoint :: bidirectional ( & op_part. track , op_part. position ) )
354
415
. collect ( ) ,
416
+ TimetableImportPathLocation :: OperationalPointIdLocation { id } => op_id_to_parts
417
+ . get ( id)
418
+ . unwrap ( )
419
+ . iter ( )
420
+ . flat_map ( |op_part| Waypoint :: bidirectional ( & op_part. track , op_part. position ) )
421
+ . collect ( ) ,
355
422
} ) ;
356
423
}
357
424
res
@@ -438,8 +505,8 @@ mod tests {
438
505
439
506
#[ test]
440
507
fn test_waypoints_from_steps ( ) {
441
- let mut op_id_to_parts = HashMap :: new ( ) ;
442
- op_id_to_parts . insert (
508
+ let mut op_uic_to_parts = HashMap :: new ( ) ;
509
+ op_uic_to_parts . insert (
443
510
1 ,
444
511
vec ! [
445
512
OperationalPointPart {
@@ -455,6 +522,23 @@ mod tests {
455
522
] ,
456
523
) ;
457
524
525
+ let mut op_id_to_parts = HashMap :: new ( ) ;
526
+ op_id_to_parts. insert (
527
+ "a1" . to_string ( ) ,
528
+ vec ! [
529
+ OperationalPointPart {
530
+ track: Identifier ( "E" . to_string( ) ) ,
531
+ position: 0. ,
532
+ ..Default :: default ( )
533
+ } ,
534
+ OperationalPointPart {
535
+ track: Identifier ( "F" . to_string( ) ) ,
536
+ position: 100. ,
537
+ ..Default :: default ( )
538
+ } ,
539
+ ] ,
540
+ ) ;
541
+
458
542
let path = vec ! [
459
543
TimetableImportPathStep {
460
544
location: TimetableImportPathLocation :: TrackOffsetLocation {
@@ -467,11 +551,17 @@ mod tests {
467
551
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
468
552
schedule: HashMap :: new( ) ,
469
553
} ,
554
+ TimetableImportPathStep {
555
+ location: TimetableImportPathLocation :: OperationalPointIdLocation {
556
+ id: "a1" . to_string( ) ,
557
+ } ,
558
+ schedule: HashMap :: new( ) ,
559
+ } ,
470
560
] ;
471
561
472
- let waypoints = waypoints_from_steps ( & path, & op_id_to_parts) ;
562
+ let waypoints = waypoints_from_steps ( & path, & op_uic_to_parts , & op_id_to_parts) ;
473
563
474
- assert_eq ! ( waypoints. len( ) , 2 ) ;
564
+ assert_eq ! ( waypoints. len( ) , 3 ) ;
475
565
assert_eq ! ( waypoints[ 0 ] , Waypoint :: bidirectional( "C" , 50. ) ) ;
476
566
assert_eq ! (
477
567
waypoints[ 1 ] ,
@@ -481,10 +571,18 @@ mod tests {
481
571
]
482
572
. concat( )
483
573
) ;
574
+ assert_eq ! (
575
+ waypoints[ 2 ] ,
576
+ [
577
+ Waypoint :: bidirectional( "E" , 0. ) ,
578
+ Waypoint :: bidirectional( "F" , 100. ) ,
579
+ ]
580
+ . concat( )
581
+ ) ;
484
582
}
485
583
486
584
#[ test]
487
- fn test_ops_uic_from_path ( ) {
585
+ fn test_ops_uic_id_from_path ( ) {
488
586
let path = vec ! [
489
587
TimetableImportPathStep {
490
588
location: TimetableImportPathLocation :: TrackOffsetLocation {
@@ -497,6 +595,12 @@ mod tests {
497
595
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
498
596
schedule: HashMap :: new( ) ,
499
597
} ,
598
+ TimetableImportPathStep {
599
+ location: TimetableImportPathLocation :: OperationalPointIdLocation {
600
+ id: "a1" . to_string( ) ,
601
+ } ,
602
+ schedule: HashMap :: new( ) ,
603
+ } ,
500
604
TimetableImportPathStep {
501
605
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 2 } ,
502
606
schedule: HashMap :: new( ) ,
@@ -508,6 +612,18 @@ mod tests {
508
612
} ,
509
613
schedule: HashMap :: new( ) ,
510
614
} ,
615
+ TimetableImportPathStep {
616
+ location: TimetableImportPathLocation :: OperationalPointIdLocation {
617
+ id: "a2" . to_string( ) ,
618
+ } ,
619
+ schedule: HashMap :: new( ) ,
620
+ } ,
621
+ TimetableImportPathStep {
622
+ location: TimetableImportPathLocation :: OperationalPointIdLocation {
623
+ id: "a1" . to_string( ) ,
624
+ } ,
625
+ schedule: HashMap :: new( ) ,
626
+ } ,
511
627
TimetableImportPathStep {
512
628
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
513
629
schedule: HashMap :: new( ) ,
@@ -519,5 +635,11 @@ mod tests {
519
635
assert_eq ! ( ops_uic. len( ) , 2 ) ;
520
636
assert_eq ! ( ops_uic[ 0 ] , 1 ) ;
521
637
assert_eq ! ( ops_uic[ 1 ] , 2 ) ;
638
+
639
+ let ops_id = ops_id_from_path ( & path) ;
640
+
641
+ assert_eq ! ( ops_id. len( ) , 2 ) ;
642
+ assert_eq ! ( ops_id[ 0 ] , "a1" ) ;
643
+ assert_eq ! ( ops_id[ 1 ] , "a2" ) ;
522
644
}
523
645
}
0 commit comments