Skip to content

Commit 771c3bd

Browse files
core: expose allowed track sections in the pathfinding api
The pathfinding api did not use any track section constraints whereas the simulation did which means that pathfinding requests might be valid but their corresponding simulation may not have been doable. Signed-off-by: Martin Bourbier <mbourbier28@gmail.com>
1 parent 7809237 commit 771c3bd

4 files changed

Lines changed: 69 additions & 2 deletions

File tree

core/src/main/kotlin/fr/sncf/osrd/api/pathfinding/PathfindingBlockRequest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class PathfindingBlockRequest(
2525

2626
// One set of location by step, each step must be reached in order
2727
@Json(name = "path_items") val pathItems: List<PathItem>,
28+
29+
// Set of authorized track section ids, empty set means no restriction
30+
@Json(name = "allowed_track_sections") val allowedTrackSections: Set<String> = emptySet(),
2831
)
2932

3033
val pathfindingRequestAdapter: JsonAdapter<PathfindingBlockRequest> =

core/src/main/kotlin/fr/sncf/osrd/api/pathfinding/PathfindingBlocksEndpoint.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import fr.sncf.osrd.api.ExceptionHandler
44
import fr.sncf.osrd.api.FullInfra
55
import fr.sncf.osrd.api.InfraProvider
66
import fr.sncf.osrd.api.TrackLocation
7+
import fr.sncf.osrd.api.stdcm.parseTrackSectionIds
78
import fr.sncf.osrd.cli.Request
89
import fr.sncf.osrd.cli.Response
910
import fr.sncf.osrd.cli.RsJson
@@ -121,13 +122,15 @@ fun runPathfinding(infra: FullInfra, request: PathfindingBlockRequest): Pathfind
121122
targets.add(ExplorerStep(allStarts, canBacktrack = step.canBacktrack))
122123
}
123124
if (targets.size < 2) throw NoPathFoundException(NotEnoughPathItems())
125+
val allowedTrackSectionIds = parseTrackSectionIds(infra, request.allowedTrackSections)
124126
val constraints =
125127
initConstraintsFromRSProps(
126128
infra,
127129
request.rollingStockIsThermal,
128130
request.rollingStockLoadingGauge,
129131
request.rollingStockSupportedElectrifications,
130132
request.rollingStockSupportedSignalingSystems,
133+
allowedTrackSectionIds,
131134
)
132135

133136
// Compute the paths from the entry waypoint to the exit waypoint

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,11 @@ private fun parseSimulationScheduleItems(
598598
)
599599
}
600600

601-
fun parseTrackSectionIds(infra: FullInfra, trackSectionName: Set<String>?): Set<TrackSectionId>? {
602-
return trackSectionName?.mapNotNull { infra.rawInfra.getTrackSectionFromName(it) }?.toSet()
601+
fun parseTrackSectionIds(infra: FullInfra, trackSectionNames: Set<String>?): Set<TrackSectionId>? {
602+
return trackSectionNames
603+
?.mapNotNull { infra.rawInfra.getTrackSectionFromName(it) }
604+
?.toSet()
605+
?.takeIf { it.isNotEmpty() }
603606
}
604607

605608
private fun progressCallback(

core/src/test/kotlin/fr/sncf/osrd/pathfinding/PathfindingTest.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ import fr.sncf.osrd.utils.units.Distance
2020
import fr.sncf.osrd.utils.units.Offset
2121
import fr.sncf.osrd.utils.units.OffsetRange
2222
import fr.sncf.osrd.utils.units.meters
23+
import java.util.stream.Stream
2324
import kotlin.test.assertEquals
2425
import org.assertj.core.api.Assertions.assertThat
2526
import org.assertj.core.api.Assertions.assertThatThrownBy
2627
import org.assertj.core.api.AssertionsForClassTypes
2728
import org.junit.jupiter.api.BeforeEach
2829
import org.junit.jupiter.api.Test
2930
import org.junit.jupiter.api.TestInstance
31+
import org.junit.jupiter.params.ParameterizedTest
32+
import org.junit.jupiter.params.provider.Arguments
33+
import org.junit.jupiter.params.provider.MethodSource
3034

3135
fun getPathfindingBlockRequest(
3236
rs: RollingStock,
@@ -610,6 +614,60 @@ class PathfindingTest : ApiTest() {
610614
}
611615
}
612616

617+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
618+
class PathfindingAllowedTrackSectionsTest {
619+
// tiny_infra path: foo_b -> foo_to_bar -> bar_a
620+
private val infra = Helpers.fullInfraFromFile("tiny_infra/infra.json")
621+
private val waypointsStart = listOf(TrackLocation("ne.micro.foo_b", Offset(50.meters)))
622+
private val waypointsEnd = listOf(TrackLocation("ne.micro.bar_a", Offset(100.meters)))
623+
private val pathItems = listOf(PathItem(waypointsStart, false), PathItem(waypointsEnd, false))
624+
625+
private fun makeRequest(allowedTrackSections: Set<String> = emptySet()) =
626+
PathfindingBlockRequest(
627+
rollingStockLoadingGauge = TestTrains.REALISTIC_FAST_TRAIN.loadingGaugeType,
628+
rollingStockIsThermal = TestTrains.REALISTIC_FAST_TRAIN.isThermal,
629+
rollingStockSupportedElectrifications =
630+
TestTrains.REALISTIC_FAST_TRAIN.modeNames.filterNot { it == "thermal" },
631+
rollingStockSupportedSignalingSystems =
632+
TestTrains.REALISTIC_FAST_TRAIN.supportedSignalingSystems.toList(),
633+
rollingStockMaximumSpeed = TestTrains.REALISTIC_FAST_TRAIN.maxSpeed,
634+
rollingStockLength = TestTrains.REALISTIC_FAST_TRAIN.length,
635+
timeout = null,
636+
infra = "tiny_infra/infra.json",
637+
expectedVersion = 1,
638+
pathItems = pathItems,
639+
allowedTrackSections = allowedTrackSections,
640+
)
641+
642+
private fun successCases(): Stream<Arguments> =
643+
Stream.of(
644+
Arguments.of(emptySet<String>()),
645+
Arguments.of(setOf("ne.micro.foo_b", "ne.micro.foo_to_bar", "ne.micro.bar_a")),
646+
)
647+
648+
@ParameterizedTest
649+
@MethodSource("successCases")
650+
fun allowedTrackSectionsSucceeds(allowedTrackSections: Set<String>) {
651+
checkPathfindingSuccess(
652+
runPathfinding(infra, makeRequest(allowedTrackSections)),
653+
10250.meters,
654+
)
655+
}
656+
657+
@Test
658+
fun allowedTrackSectionsFailsWhenRequiredTrackExcluded() {
659+
// omit foo_to_bar
660+
assertThatThrownBy {
661+
runPathfinding(infra, makeRequest(setOf("ne.micro.foo_b", "ne.micro.bar_a")))
662+
}
663+
.isExactlyInstanceOf(NoPathFoundException::class.java)
664+
.satisfies({ e ->
665+
assertThat((e as NoPathFoundException).response)
666+
.isExactlyInstanceOf(NotFoundInBlocks::class.java)
667+
})
668+
}
669+
}
670+
613671
class PathfindingStopsAtEndOfBlock : ApiTest() {
614672
fun callPathfindingEndpoint(
615673
rs: RollingStock,

0 commit comments

Comments
 (0)