@@ -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 ) ]
@@ -77,7 +79,7 @@ struct TimetableImportReport {
77
79
#[ derive( Debug , Clone , Serialize , ToSchema ) ]
78
80
enum TimetableImportError {
79
81
RollingStockNotFound { name : String } ,
80
- OperationalPointNotFound { missing_uics : Vec < i64 > } ,
82
+ OperationalPointNotFound { missing_uics : Vec < i64 > , missing_ids : Vec < String > } ,
81
83
PathfindingError { cause : InternalError } ,
82
84
SimulationError { cause : InternalError } ,
83
85
}
@@ -183,9 +185,10 @@ async fn import_item(
183
185
pf_request. with_rolling_stocks ( & mut vec ! [ rolling_stock. clone( ) ] ) ;
184
186
// List operational points uic needed for this import
185
187
let ops_uic = ops_uic_from_path ( & import_item. path ) ;
188
+ let ops_id = ops_id_from_path ( & import_item. path ) ;
186
189
// 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,
190
+ let ( op_uic_to_parts , op_id_to_parts) = match find_operation_points ( & ops_uic, & ops_id , infra_id, conn) . await ? {
191
+ Ok ( ( op_uic_to_parts , op_id_to_parts) ) => ( op_uic_to_parts , op_id_to_parts) ,
189
192
Err ( err) => {
190
193
return Ok ( import_item
191
194
. trains
@@ -195,7 +198,7 @@ async fn import_item(
195
198
}
196
199
} ;
197
200
// Create waypoints
198
- let mut waypoints = waypoints_from_steps ( & import_item. path , & op_id_to_parts) ;
201
+ let mut waypoints = waypoints_from_steps ( & import_item. path , & op_uic_to_parts , & op_id_to_parts) ;
199
202
pf_request. with_waypoints ( & mut waypoints) ;
200
203
201
204
// Run pathfinding
@@ -295,30 +298,50 @@ async fn import_item(
295
298
296
299
async fn find_operation_points (
297
300
ops_uic : & [ i64 ] ,
301
+ ops_id : & [ String ] ,
298
302
infra_id : i64 ,
299
303
conn : & mut AsyncPgConnection ,
300
- ) -> Result < std:: result:: Result < HashMap < i64 , Vec < OperationalPointPart > > , TimetableImportError > > {
304
+ ) -> Result < std:: result:: Result < ( HashMap < i64 , Vec < OperationalPointPart > > , HashMap < String , Vec < OperationalPointPart > > ) , TimetableImportError > > {
301
305
// Retrieve operational points
302
306
let ops = OperationalPointModel :: retrieve_from_uic ( conn, infra_id, ops_uic) . await ?;
303
- let mut op_id_to_parts = HashMap :: < _ , Vec < _ > > :: new ( ) ;
307
+ let mut op_uic_to_parts = HashMap :: < _ , Vec < _ > > :: new ( ) ;
304
308
for op in ops {
305
- op_id_to_parts
309
+ op_uic_to_parts
306
310
. entry ( op. data . 0 . extensions . identifier . unwrap ( ) . uic )
307
311
. or_default ( )
308
312
. extend ( op. data . 0 . parts ) ;
309
313
}
314
+ let mut missing_uics: Vec < i64 > = vec ! [ ] ;
310
315
// 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
316
+ if op_uic_to_parts . len ( ) != ops_uic. len ( ) {
317
+ ops_uic
313
318
. iter ( )
314
- . filter ( |uic| !op_id_to_parts. contains_key ( uic) )
315
- . cloned ( )
316
- . collect ( ) ;
319
+ . for_each ( |uic| { if !op_uic_to_parts. contains_key ( uic) { missing_uics. push ( * uic) } } ) ;
320
+ }
321
+
322
+ let ops2 = OperationalPointModel :: retrieve_from_ids ( conn, infra_id, ops_id) . await ?;
323
+ let mut op_id_to_parts = HashMap :: < _ , Vec < _ > > :: new ( ) ;
324
+ for op in ops2 {
325
+ op_id_to_parts
326
+ . entry ( op. obj_id )
327
+ . or_default ( )
328
+ . extend ( op. data . 0 . parts ) ;
329
+ }
330
+ // If we didn't find all the operational points, we can't run the pathfinding
331
+ let mut missing_ids: Vec < String > = vec ! [ ] ;
332
+ if op_id_to_parts. len ( ) != ops_id. len ( ) {
333
+ ops_id
334
+ . iter ( )
335
+ . for_each ( |id| { if !op_id_to_parts. contains_key ( id) { missing_ids. push ( id. to_string ( ) ) } } ) ;
336
+ }
337
+ if missing_uics. len ( ) + missing_ids. len ( ) > 0 {
317
338
return Ok ( Err ( TimetableImportError :: OperationalPointNotFound {
318
339
missing_uics,
340
+ missing_ids,
319
341
} ) ) ;
320
342
}
321
- Ok ( Ok ( op_id_to_parts) )
343
+
344
+ Ok ( Ok ( ( op_uic_to_parts, op_id_to_parts) ) )
322
345
}
323
346
324
347
fn ops_uic_from_path ( path : & [ TimetableImportPathStep ] ) -> Vec < i64 > {
@@ -335,9 +358,24 @@ fn ops_uic_from_path(path: &[TimetableImportPathStep]) -> Vec<i64> {
335
358
ops_uic
336
359
}
337
360
361
+ fn ops_id_from_path ( path : & [ TimetableImportPathStep ] ) -> Vec < String > {
362
+ let mut ops_id = path
363
+ . iter ( )
364
+ . filter_map ( |step| match & step. location {
365
+ TimetableImportPathLocation :: OperationalPointIdLocation { id } => Some ( id. to_string ( ) ) ,
366
+ _ => None ,
367
+ } )
368
+ . collect :: < Vec < _ > > ( ) ;
369
+ // Remove duplicates
370
+ ops_id. sort ( ) ;
371
+ ops_id. dedup ( ) ;
372
+ ops_id
373
+ }
374
+
338
375
fn waypoints_from_steps (
339
376
path : & Vec < TimetableImportPathStep > ,
340
- op_id_to_parts : & HashMap < i64 , Vec < OperationalPointPart > > ,
377
+ op_uic_to_parts : & HashMap < i64 , Vec < OperationalPointPart > > ,
378
+ op_id_to_parts : & HashMap < String , Vec < OperationalPointPart > > ,
341
379
) -> Vec < Vec < Waypoint > > {
342
380
let mut res = PathfindingWaypoints :: new ( ) ;
343
381
for step in path {
@@ -346,12 +384,18 @@ fn waypoints_from_steps(
346
384
track_section,
347
385
offset,
348
386
} => Vec :: from ( Waypoint :: bidirectional ( track_section, * offset) ) ,
349
- TimetableImportPathLocation :: OperationalPointLocation { uic } => op_id_to_parts
387
+ TimetableImportPathLocation :: OperationalPointLocation { uic } => op_uic_to_parts
350
388
. get ( uic)
351
389
. unwrap ( )
352
390
. iter ( )
353
391
. flat_map ( |op_part| Waypoint :: bidirectional ( & op_part. track , op_part. position ) )
354
392
. collect ( ) ,
393
+ TimetableImportPathLocation :: OperationalPointIdLocation { id } => op_id_to_parts
394
+ . get ( id)
395
+ . unwrap ( )
396
+ . iter ( )
397
+ . flat_map ( |op_part| Waypoint :: bidirectional ( & op_part. track , op_part. position ) )
398
+ . collect ( ) ,
355
399
} ) ;
356
400
}
357
401
res
@@ -438,8 +482,8 @@ mod tests {
438
482
439
483
#[ test]
440
484
fn test_waypoints_from_steps ( ) {
441
- let mut op_id_to_parts = HashMap :: new ( ) ;
442
- op_id_to_parts . insert (
485
+ let mut op_uic_to_parts = HashMap :: new ( ) ;
486
+ op_uic_to_parts . insert (
443
487
1 ,
444
488
vec ! [
445
489
OperationalPointPart {
@@ -455,6 +499,23 @@ mod tests {
455
499
] ,
456
500
) ;
457
501
502
+ let mut op_id_to_parts = HashMap :: new ( ) ;
503
+ op_id_to_parts. insert (
504
+ "a1" . to_string ( ) ,
505
+ vec ! [
506
+ OperationalPointPart {
507
+ track: Identifier ( "E" . to_string( ) ) ,
508
+ position: 0. ,
509
+ ..Default :: default ( )
510
+ } ,
511
+ OperationalPointPart {
512
+ track: Identifier ( "F" . to_string( ) ) ,
513
+ position: 100. ,
514
+ ..Default :: default ( )
515
+ } ,
516
+ ] ,
517
+ ) ;
518
+
458
519
let path = vec ! [
459
520
TimetableImportPathStep {
460
521
location: TimetableImportPathLocation :: TrackOffsetLocation {
@@ -467,11 +528,15 @@ mod tests {
467
528
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
468
529
schedule: HashMap :: new( ) ,
469
530
} ,
531
+ TimetableImportPathStep {
532
+ location: TimetableImportPathLocation :: OperationalPointIdLocation { id: "a1" . to_string( ) } ,
533
+ schedule: HashMap :: new( ) ,
534
+ } ,
470
535
] ;
471
536
472
- let waypoints = waypoints_from_steps ( & path, & op_id_to_parts) ;
537
+ let waypoints = waypoints_from_steps ( & path, & op_uic_to_parts , & op_id_to_parts) ;
473
538
474
- assert_eq ! ( waypoints. len( ) , 2 ) ;
539
+ assert_eq ! ( waypoints. len( ) , 3 ) ;
475
540
assert_eq ! ( waypoints[ 0 ] , Waypoint :: bidirectional( "C" , 50. ) ) ;
476
541
assert_eq ! (
477
542
waypoints[ 1 ] ,
@@ -481,10 +546,18 @@ mod tests {
481
546
]
482
547
. concat( )
483
548
) ;
549
+ assert_eq ! (
550
+ waypoints[ 2 ] ,
551
+ [
552
+ Waypoint :: bidirectional( "E" , 0. ) ,
553
+ Waypoint :: bidirectional( "F" , 100. ) ,
554
+ ]
555
+ . concat( )
556
+ ) ;
484
557
}
485
558
486
559
#[ test]
487
- fn test_ops_uic_from_path ( ) {
560
+ fn test_ops_uic_id_from_path ( ) {
488
561
let path = vec ! [
489
562
TimetableImportPathStep {
490
563
location: TimetableImportPathLocation :: TrackOffsetLocation {
@@ -497,6 +570,10 @@ mod tests {
497
570
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
498
571
schedule: HashMap :: new( ) ,
499
572
} ,
573
+ TimetableImportPathStep {
574
+ location: TimetableImportPathLocation :: OperationalPointIdLocation { id: "a1" . to_string( ) } ,
575
+ schedule: HashMap :: new( ) ,
576
+ } ,
500
577
TimetableImportPathStep {
501
578
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 2 } ,
502
579
schedule: HashMap :: new( ) ,
@@ -508,6 +585,14 @@ mod tests {
508
585
} ,
509
586
schedule: HashMap :: new( ) ,
510
587
} ,
588
+ TimetableImportPathStep {
589
+ location: TimetableImportPathLocation :: OperationalPointIdLocation { id: "a2" . to_string( ) } ,
590
+ schedule: HashMap :: new( ) ,
591
+ } ,
592
+ TimetableImportPathStep {
593
+ location: TimetableImportPathLocation :: OperationalPointIdLocation { id: "a1" . to_string( ) } ,
594
+ schedule: HashMap :: new( ) ,
595
+ } ,
511
596
TimetableImportPathStep {
512
597
location: TimetableImportPathLocation :: OperationalPointLocation { uic: 1 } ,
513
598
schedule: HashMap :: new( ) ,
@@ -519,5 +604,11 @@ mod tests {
519
604
assert_eq ! ( ops_uic. len( ) , 2 ) ;
520
605
assert_eq ! ( ops_uic[ 0 ] , 1 ) ;
521
606
assert_eq ! ( ops_uic[ 1 ] , 2 ) ;
607
+
608
+ let ops_id = ops_id_from_path ( & path) ;
609
+
610
+ assert_eq ! ( ops_id. len( ) , 2 ) ;
611
+ assert_eq ! ( ops_id[ 0 ] , "a1" ) ;
612
+ assert_eq ! ( ops_id[ 1 ] , "a2" ) ;
522
613
}
523
614
}
0 commit comments