Skip to content

Commit

Permalink
core: add supported_signaling_systems and add signalingSystemConstrai…
Browse files Browse the repository at this point in the history
…nts for the pathfinding
  • Loading branch information
anisometropie committed Feb 8, 2024
1 parent 8f490e7 commit edd8cb5
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,17 @@ public enum ErrorType {
),
PathfindingGaugeError(
"no_path_found:gauge",
"No path could be found after loading Gauge constraints",
"No path could be found with compatible Gauge",
ErrorCause.USER
),
PathfindingElectrificationError(
"no_path_found:electrification",
"No path could be found after loading Electrification constraints",
"No path could be found with compatible electrification",
ErrorCause.USER
),
PathfindingSignalisationSystemError(
"no_path_found:signalisation_system",
"No path could be found with a compatible signaling system",
ErrorCause.USER
),
PathfindingTimeoutError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import fr.sncf.osrd.api.FullInfra
import fr.sncf.osrd.api.InfraManager
import fr.sncf.osrd.api.pathfinding.constraints.ElectrificationConstraints
import fr.sncf.osrd.api.pathfinding.constraints.LoadingGaugeConstraints
import fr.sncf.osrd.api.pathfinding.constraints.SignalingSystemConstraints
import fr.sncf.osrd.api.pathfinding.constraints.makeSignalingSystemConstraints
import fr.sncf.osrd.api.pathfinding.request.PathfindingRequest
import fr.sncf.osrd.api.pathfinding.request.PathfindingWaypoint
import fr.sncf.osrd.api.pathfinding.response.PathWaypointResult
import fr.sncf.osrd.api.pathfinding.response.PathfindingResult
import fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator
import fr.sncf.osrd.graph.*
import fr.sncf.osrd.graph.Pathfinding.EdgeLocation
import fr.sncf.osrd.infra.api.Direction
Expand Down Expand Up @@ -79,6 +79,8 @@ class PathfindingBlocksEndpoint
ErrorType.PathfindingGaugeError
constraintErrors[ElectrificationConstraints::class.java] =
ErrorType.PathfindingElectrificationError
constraintErrors[SignalingSystemConstraints::class.java] =
ErrorType.PathfindingSignalisationSystemError
}
}
}
Expand Down Expand Up @@ -203,8 +205,13 @@ fun runPathfinding(
infra.blockInfra, infra.rawInfra,
rollingStocks
)
val signalisationSystemConstraints = makeSignalingSystemConstraints(
infra.blockInfra,
infra.signalingSimulator,
rollingStocks
)
val constraints = listOf(
loadingGaugeConstraints, electrificationConstraints
loadingGaugeConstraints, electrificationConstraints, signalisationSystemConstraints
)
val remainingDistanceEstimators = makeHeuristics(infra, waypoints)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import fr.sncf.osrd.utils.units.Distance
import fr.sncf.osrd.utils.units.Offset
import java.util.stream.Collectors

@JvmRecord
data class ElectrificationConstraints(
val blockInfra: BlockInfra,
val rawInfra: RawSignalingInfra,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import fr.sncf.osrd.utils.DistanceRangeMap
import fr.sncf.osrd.utils.units.Offset
import java.util.stream.Collectors

@JvmRecord
data class LoadingGaugeConstraints(
val blockInfra: BlockInfra,
val infra: RawSignalingInfra,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fr.sncf.osrd.api.pathfinding.constraints

import fr.sncf.osrd.graph.EdgeToRangesId
import fr.sncf.osrd.graph.Pathfinding
import fr.sncf.osrd.signaling.SignalingSimulator
import fr.sncf.osrd.sim_infra.api.*
import fr.sncf.osrd.train.RollingStock
import fr.sncf.osrd.utils.units.Offset
import fr.sncf.osrd.utils.units.meters

data class SignalingSystemConstraints(
val blockInfra: BlockInfra,
val rollingStocksSupportedSigSystems: List<List<SignalingSystemId>>
) : EdgeToRangesId<Block> {
override fun apply(edge: BlockId): MutableCollection<Pathfinding.Range<Block>> {
val res = HashSet<Pathfinding.Range<Block>>()
for (rollingStocksigSystems in rollingStocksSupportedSigSystems) {
val edgeBlockedRanges = getBlockedRanges(edge, blockInfra, rollingStocksigSystems)
if (edgeBlockedRanges.isNotEmpty()) {
res.addAll(edgeBlockedRanges)
break // if this edge is blocked for 2 RS, we will have the same exact range (the full edge range) twice
}
}
return res
}

/**
* Returns the sections of the given block that can't be used by the given rolling stock
*/
private fun getBlockedRanges(
edge: BlockId,
blockInfra: BlockInfra,
rollingStockSigSystems: List<SignalingSystemId>
): Set<Pathfinding.Range<Block>> {
val blockSigSystem = blockInfra.getBlockSignalingSystem(edge)
val isRSCompatibleWithBlock = rollingStockSigSystems.contains(blockSigSystem)
if (isRSCompatibleWithBlock) {
return setOf()
}
return setOf(Pathfinding.Range(
Offset(0.meters),
blockInfra.getBlockLength(edge))
)
}
}

fun makeSignalingSystemConstraints(
blockInfra: BlockInfra,
signalingSimulator: SignalingSimulator,
rollingStocks: Collection<RollingStock>,
): SignalingSystemConstraints {
val rsSupportedSigSystems = rollingStocks.map { stock ->
stock.supportedSignalingSystems.mapNotNull { s -> try {
signalingSimulator.sigModuleManager.findSignalingSystem(s)
} catch (e: Exception) {
null
} }
}
return SignalingSystemConstraints(
blockInfra,
rsSupportedSigSystems
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.sncf.osrd.stdcm.graph
import fr.sncf.osrd.api.FullInfra
import fr.sncf.osrd.api.pathfinding.constraints.ElectrificationConstraints
import fr.sncf.osrd.api.pathfinding.constraints.LoadingGaugeConstraints
import fr.sncf.osrd.api.pathfinding.constraints.makeSignalingSystemConstraints
import fr.sncf.osrd.api.pathfinding.makeHeuristics
import fr.sncf.osrd.envelope_sim.allowances.utils.AllowanceValue
import fr.sncf.osrd.graph.*
Expand Down Expand Up @@ -58,6 +59,10 @@ fun findPath(
fullInfra.blockInfra, fullInfra.rawInfra,
listOf(rollingStock)
)
val signalingSystemConstraints = makeSignalingSystemConstraints(
fullInfra.blockInfra, fullInfra.signalingSimulator,
listOf(rollingStock)
)

// Initialize the A* heuristic
val locations = steps.stream()
Expand All @@ -70,6 +75,7 @@ fun findPath(
.setEdgeToLength { edge -> edge.length.cast() }
.addBlockedRangeOnEdges { edge: STDCMEdge? -> convertRanges(loadingGaugeConstraints.apply(edge!!.block)) }
.addBlockedRangeOnEdges { edge: STDCMEdge? -> convertRanges(electrificationConstraints.apply(edge!!.block)) }
.addBlockedRangeOnEdges { edge: STDCMEdge? -> convertRanges(signalingSystemConstraints.apply(edge!!.block)) }
.setTotalCostUntilEdgeLocation { range ->
totalCostUntilEdgeLocation(
range,
Expand Down
25 changes: 23 additions & 2 deletions core/src/test/java/fr/sncf/osrd/train/TestTrains.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class TestTrains {

public static final RollingStock CONSTANT_POWER_TRAIN;

public static final RollingStock TRAIN_WITHOUT_TVM;

public static final double MAX_SPEED = 300 / 3.6;

private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves(
Expand Down Expand Up @@ -132,7 +134,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
);

REALISTIC_FAST_TRAIN = new RollingStock(
"fast train",
"realistic fast train",
400, trainMass, 1.05, (0.65 * trainMass) / 100,
((0.008 * trainMass) / 100) * 3.6,
(((0.00012 * trainMass) / 100) * 3.6) * 3.6,
Expand Down Expand Up @@ -189,7 +191,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
);

FAST_ELECTRIC_TRAIN = new RollingStock(
"fast train",
"fast electric train",
400, trainMass, 1.05, (0.65 * trainMass) / 100,
((0.008 * trainMass) / 100) * 3.6,
(((0.00012 * trainMass) / 100) * 3.6) * 3.6,
Expand Down Expand Up @@ -225,6 +227,25 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
"1",
new String[]{"BAL", "BAPR", "TVM300", "TVM430"}
);

TRAIN_WITHOUT_TVM = new RollingStock(
"train without tvm",
100, trainMass, 1.05, (0.65 * trainMass) / 100,
((0.008 * trainMass) / 100) * 3.6,
(((0.00012 * trainMass) / 100) * 3.6) * 3.6,
44,
20,
0.05,
0.25,
0.5,
PhysicsRollingStock.GammaType.CONST,
RJSLoadingGaugeType.G1,
createModeEffortCurves(44, CurveShape.HYPERBOLIC,
Map.of("thermal", new RollingStock.EffortCurveConditions[0])),
"thermal",
"1",
new String[]{"BAL", "BAPR"}
);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package fr.sncf.osrd.pathfinding

import fr.sncf.osrd.api.pathfinding.request.PathfindingWaypoint
import fr.sncf.osrd.api.pathfinding.runPathfinding
import fr.sncf.osrd.graph.Pathfinding
import fr.sncf.osrd.railjson.schema.common.graph.EdgeDirection
import fr.sncf.osrd.reporting.exceptions.ErrorType
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.sim_infra.api.BlockId
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.utils.DummyInfra
import fr.sncf.osrd.utils.units.Offset
import fr.sncf.osrd.utils.units.meters
import org.assertj.core.api.AssertionsForClassTypes
import org.assertj.core.api.Assertions as assertjAssertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import fr.sncf.osrd.sim_infra.api.Block
import org.junit.jupiter.api.BeforeEach

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PathfindingSignalingTest {
private var infra: DummyInfra = DummyInfra()

private fun setSigSystemIds(list: List<BlockId>, signalingSystemName: String) {
val id = infra.fullInfra().signalingSimulator.sigModuleManager.findSignalingSystem(signalingSystemName)
list.forEach {
infra.blockPool[it.index.toInt()].signalingSystemId = id
}
}
@BeforeEach
fun setUp() {
/* c1
^ \
/ v
a --> b d --> e
\ ^
v /
c2
*/
infra = DummyInfra()
infra.addBlock("a", "b")
infra.addBlock("b", "c1")
infra.addBlock("b", "c2")
infra.addBlock("c1", "d")
infra.addBlock("c2", "d")
infra.addBlock("d", "e")
}

@Test
fun balTrainOnTVMBlockShouldThrow() {
setSigSystemIds(listOf(1U, 2U, 3U, 4U).map { BlockId(it) }, "TVM300")
val waypointStart = PathfindingWaypoint(
"a->b",
0.0,
EdgeDirection.START_TO_STOP
)
val waypointEnd = PathfindingWaypoint(
"d->e",
100.0,
EdgeDirection.START_TO_STOP
)
val waypoints = Array(2) { Array(1) { waypointStart } }
waypoints[1][0] = waypointEnd

// Run a pathfinding with a non TVM train, expecting not to find any path
AssertionsForClassTypes.assertThatThrownBy {
runPathfinding(
infra.fullInfra(), waypoints, listOf(TestTrains.TRAIN_WITHOUT_TVM)
)
}
.isExactlyInstanceOf(OSRDError::class.java)
.satisfies({ exception: Throwable? ->
assertjAssertions.assertThat(
(exception as OSRDError?)!!.osrdErrorType
).isEqualTo(ErrorType.PathfindingSignalisationSystemError)
})
}

@Test
fun shouldFindTopPathOnBalBlocksForBalTrain() {
setSigSystemIds(listOf(2U, 4U).map { BlockId(it) }, "TVM300")
val waypointStart = PathfindingWaypoint(
"a->b",
0.0,
EdgeDirection.START_TO_STOP
)
val waypointEnd = PathfindingWaypoint(
"d->e",
100.0,
EdgeDirection.START_TO_STOP
)
val waypoints = Array(2) { Array(1) { waypointStart } }
waypoints[1][0] = waypointEnd

val pathfindingResult = runPathfinding(
infra.fullInfra(), waypoints, listOf(TestTrains.TRAIN_WITHOUT_TVM)
)

AssertionsForClassTypes.assertThat(pathfindingResult.ranges).isEqualTo(arrayListOf(
Pathfinding.EdgeRange(BlockId(0U), Offset<Block>(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(1U), Offset(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(3U), Offset(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(5U), Offset(0.meters), Offset(100.meters))
))
}

@Test
fun shouldFindBottomPathOnBalBlocksForBalTrain() {
setSigSystemIds(listOf(1U, 3U).map { BlockId(it) }, "TVM430")
val waypointStart = PathfindingWaypoint(
"a->b",
0.0,
EdgeDirection.START_TO_STOP
)
val waypointEnd = PathfindingWaypoint(
"d->e",
100.0,
EdgeDirection.START_TO_STOP
)
val waypoints = Array(2) { Array(1) { waypointStart } }
waypoints[1][0] = waypointEnd

val pathfindingResult = runPathfinding(
infra.fullInfra(), waypoints, listOf(TestTrains.TRAIN_WITHOUT_TVM)
)

AssertionsForClassTypes.assertThat(pathfindingResult.ranges).isEqualTo(arrayListOf(
Pathfinding.EdgeRange(BlockId(0U), Offset<Block>(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(2U), Offset(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(4U), Offset(0.meters), Offset(100.meters)),
Pathfinding.EdgeRange(BlockId(5U), Offset(0.meters), Offset(100.meters))
))
}
}
Loading

0 comments on commit edd8cb5

Please sign in to comment.