Skip to content

Commit 820e055

Browse files
committed
core: paths: migrate most endpoints to new path model
Some parts are too large and haven't been included, such as ScheduleMetadataExtractor. Signed-off-by: Eloi Charpentier <[email protected]>
1 parent c6e03b9 commit 820e055

File tree

10 files changed

+256
-225
lines changed

10 files changed

+256
-225
lines changed

core/kt-osrd-path/src/main/kotlin/fr/sncf/osrd/path/interfaces/GenericLinearRange.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ data class GenericLinearRange<ValueType, OffsetType>(
3030
fun isSingleton() = length == 0.meters
3131

3232
fun offsetFromTrainPath(pathOffset: Offset<TrainPath>): Offset<OffsetType> {
33-
val rangeOffset = pathOffset.distance - pathBegin.distance
34-
return objectBegin + rangeOffset
33+
val objectStart = Offset<TrainPath>(pathBegin.distance - objectBegin.distance)
34+
return Offset(pathOffset.distance - objectStart.distance)
3535
}
3636

3737
fun offsetToTrainPath(objectOffset: Offset<OffsetType>): Offset<TrainPath> {
38-
val rangeOffset = objectOffset.distance - objectBegin.distance
39-
return pathBegin + rangeOffset
38+
val objectStart = Offset<TrainPath>(pathBegin.distance - objectBegin.distance)
39+
return objectStart + objectOffset.distance
4040
}
4141

4242
fun withTruncatedPathRange(
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package fr.sncf.osrd.path
2+
3+
import fr.sncf.osrd.path.interfaces.GenericLinearRange
4+
import fr.sncf.osrd.path.interfaces.mapSubObjects
5+
import fr.sncf.osrd.sim_infra.api.Block
6+
import fr.sncf.osrd.sim_infra.api.BlockId
7+
import fr.sncf.osrd.sim_infra.api.Route
8+
import fr.sncf.osrd.sim_infra.api.RouteId
9+
import fr.sncf.osrd.utils.units.Length
10+
import fr.sncf.osrd.utils.units.Offset
11+
import fr.sncf.osrd.utils.units.meters
12+
import kotlin.test.assertEquals
13+
import org.junit.Test
14+
15+
class GenericLinearRangeTest {
16+
@Test
17+
fun testProjectionFullRange() {
18+
/*
19+
block offset (m) 0 50 100
20+
block: |---------------->
21+
projected offset: | x
22+
path: --------|---------------->
23+
path offset (m) 1_000 1_050 1_100
24+
*/
25+
val range =
26+
GenericLinearRange(
27+
value = BlockId(0U),
28+
objectBegin = Offset<Block>(0.meters),
29+
objectEnd = Offset(100.meters),
30+
pathBegin = Offset(1_000.meters),
31+
pathEnd = Offset(1_100.meters),
32+
)
33+
34+
assertEquals(Offset(50.meters), range.offsetFromTrainPath(Offset(1_050.meters)))
35+
assertEquals(Offset(1_050.meters), range.offsetToTrainPath(Offset(50.meters)))
36+
assertEquals(Offset(1_000.meters), range.getObjectAbsolutePathStart())
37+
assertEquals(Offset(1_100.meters), range.getObjectAbsolutePathEnd(Length(100.meters)))
38+
}
39+
40+
@Test
41+
fun testProjectionPartialRange() {
42+
/*
43+
block offset (m) 0 25 42 75 100
44+
block: |----|------------|------->
45+
x
46+
path: |------------>
47+
path offset (m) 0 17 50
48+
*/
49+
val range =
50+
GenericLinearRange(
51+
value = BlockId(0U),
52+
objectBegin = Offset<Block>(25.meters),
53+
objectEnd = Offset(75.meters),
54+
pathBegin = Offset(0.meters),
55+
pathEnd = Offset(50.meters),
56+
)
57+
58+
assertEquals(Offset(42.meters), range.offsetFromTrainPath(Offset(17.meters)))
59+
assertEquals(Offset(17.meters), range.offsetToTrainPath(Offset(42.meters)))
60+
assertEquals(Offset((-25).meters), range.getObjectAbsolutePathStart())
61+
assertEquals(Offset(75.meters), range.getObjectAbsolutePathEnd(Length(100.meters)))
62+
}
63+
64+
@Test
65+
fun testMapSubRange() {
66+
/*
67+
route offset (m) 0 100 250 400 600 750 800 1000
68+
route: |---+---|----+--------+-----|---+------>
69+
path: | + |----+--------+-----> + +
70+
path offset (m) -250 + 0 150 350 500 550 750
71+
blocks: |--->|--+---->|------->|----+--->|----->
72+
block offsets 0 100 ^ 300 200 150 200 200
73+
block offsets 150
74+
*/
75+
val range =
76+
GenericLinearRange(
77+
value = RouteId(0U),
78+
objectBegin = Offset<Route>(250.meters),
79+
objectEnd = Offset(750.meters),
80+
pathBegin = Offset(0.meters),
81+
pathEnd = Offset(500.meters),
82+
)
83+
val blocks = (0U..4U).map { BlockId(it) }
84+
val blockLengths = listOf(100, 300, 200, 200, 200).map { Length<Block>(it.meters) }
85+
86+
val blockRanges =
87+
mapSubObjects(
88+
listOf(range),
89+
listSubObject = { blocks },
90+
subObjectLength = { blockLengths[it.index.toInt()] },
91+
)
92+
93+
val expectedRanges =
94+
listOf(
95+
GenericLinearRange(
96+
value = BlockId(1U),
97+
objectBegin = Offset<Block>(150.meters),
98+
objectEnd = Offset(300.meters),
99+
pathBegin = Offset(0.meters),
100+
pathEnd = Offset(150.meters),
101+
),
102+
GenericLinearRange(
103+
value = BlockId(2U),
104+
objectBegin = Offset(0.meters),
105+
objectEnd = Offset(200.meters),
106+
pathBegin = Offset(150.meters),
107+
pathEnd = Offset(350.meters),
108+
),
109+
GenericLinearRange(
110+
value = BlockId(3U),
111+
objectBegin = Offset(0.meters),
112+
objectEnd = Offset(150.meters),
113+
pathBegin = Offset(350.meters),
114+
pathEnd = Offset(500.meters),
115+
),
116+
)
117+
assertEquals(expectedRanges, blockRanges)
118+
}
119+
}

core/src/main/kotlin/fr/sncf/osrd/api/etcs/ETCSBrakingCurvesEndpoint.kt

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ import fr.sncf.osrd.envelope.part.EnvelopePart
77
import fr.sncf.osrd.envelope_sim.EnvelopeProfile
88
import fr.sncf.osrd.envelope_sim.EnvelopeSimContext
99
import fr.sncf.osrd.envelope_sim.etcs.*
10-
import fr.sncf.osrd.path.interfaces.BlockPath
10+
import fr.sncf.osrd.path.interfaces.TrainPath
1111
import fr.sncf.osrd.path.interfaces.TravelledPath
12-
import fr.sncf.osrd.path.interfaces.getLegacyBlockPath
13-
import fr.sncf.osrd.path.interfaces.getLegacyChunkPath
14-
import fr.sncf.osrd.path.interfaces.getLegacyRoutePath
1512
import fr.sncf.osrd.signaling.etcs_level2.ETCS_LEVEL2
1613
import fr.sncf.osrd.sim_infra.api.*
17-
import fr.sncf.osrd.standalone_sim.PathOffsetBuilder
1814
import fr.sncf.osrd.standalone_sim.buildSignalingRanges
1915
import fr.sncf.osrd.standalone_sim.getSimStops
2016
import fr.sncf.osrd.standalone_sim.makeETCSContext
@@ -66,9 +62,6 @@ class ETCSBrakingCurvesEndpoint(
6662
// Parse path.
6763
val trainPath =
6864
request.path.toTrainPath(infra.rawInfra, infra.blockInfra, electricalProfileMap)
69-
val chunkPath = trainPath.getLegacyChunkPath()
70-
val routePath = trainPath.getLegacyRoutePath()
71-
val blockPath = trainPath.getLegacyBlockPath()
7265
val powerRestrictionsLegacyMap =
7366
parsePowerRestrictions(request.powerRestrictions).toRangeMap()
7467
val electrificationMap =
@@ -80,22 +73,15 @@ class ETCSBrakingCurvesEndpoint(
8073
)
8174
val curvesAndConditions =
8275
rollingStock.mapTractiveEffortCurves(electrificationMap, request.comfort)
83-
val signalingRanges = buildSignalingRanges(infra, blockPath, chunkPath)
76+
val signalingRanges = buildSignalingRanges(infra, trainPath)
8477
val stops = getSimStops(parseRawSimulationScheduleItems(request.schedule))
8578
val context =
8679
EnvelopeSimContext(
8780
rollingStock,
8881
trainPath,
8982
2.0,
9083
curvesAndConditions.curves,
91-
makeETCSContext(
92-
rollingStock,
93-
infra,
94-
chunkPath,
95-
routePath,
96-
blockPath,
97-
signalingRanges,
98-
),
84+
makeETCSContext(rollingStock, infra, trainPath, signalingRanges),
9985
)
10086

10187
// Parse mrsp.
@@ -115,17 +101,11 @@ class ETCSBrakingCurvesEndpoint(
115101
EoaType.STOP,
116102
)
117103
val stopBrakingCurves = etcsSimulator.computeStopBrakingCurves(mrsp, etcsStops)
118-
// Compute conflict braking curves on each of the path's ETCS signals.
119-
val pathOffsetBuilder =
120-
PathOffsetBuilder(
121-
trainPathBlockOffset(infra.rawInfra, infra.blockInfra, blockPath, chunkPath)
122-
.distance
123-
)
124-
val etcsSignals =
125-
getTravelledPathSignals(infra, blockPath, pathOffsetBuilder, ETCS_LEVEL2.id)
104+
val etcsSignals = getTravelledPathSignals(infra, trainPath, ETCS_LEVEL2.id)
126105
val etcsSignalsOnPath =
127106
etcsSignals.filter {
128-
it.offset.distance > Distance.ZERO && it.offset.distance <= chunkPath.length
107+
it.offset.distance > Distance.ZERO &&
108+
it.offset.distance <= trainPath.getLength()
129109
}
130110
val etcsSignalOffsets = etcsSignalsOnPath.map { it.offset }
131111
val areEtcsSignalRouteDelimiters = etcsSignalsOnPath.map { it.isRouteDelimiter }
@@ -175,26 +155,23 @@ class ETCSBrakingCurvesEndpoint(
175155
*/
176156
private fun getTravelledPathSignals(
177157
fullInfra: FullInfra,
178-
blockPath: List<BlockId>,
179-
pathOffsetBuilder: PathOffsetBuilder,
158+
trainPath: TrainPath,
180159
signalingSystemId: String? = null,
181160
): List<TravelledPathSignal> {
182161
val res = mutableSetOf<TravelledPathSignal>()
183-
var currentOffset = Offset<BlockPath>(Distance.ZERO)
184-
for (block in blockPath) {
162+
for (blockRange in trainPath.getBlocks()) {
163+
val block = blockRange.value
185164
val blockSignalsPositions = fullInfra.blockInfra.getSignalsPositions(block)
186165
val blockSignals = fullInfra.blockInfra.getBlockSignals(block)
187166
assert(blockSignalsPositions.size == blockSignals.size)
188167
for ((signalPosition, signal) in blockSignalsPositions zip blockSignals) {
189168
val signSystemId = fullInfra.rawInfra.getSignalingSystemId(signal)
190169
val isRouteDelimiter = fullInfra.loadedSignalInfra.getSettings(signal).getFlag("Nf")
191170
if (signalingSystemId == null || signSystemId == signalingSystemId) {
192-
val signalOffset =
193-
pathOffsetBuilder.toTravelledPath(currentOffset + signalPosition.distance)
171+
val signalOffset = blockRange.offsetToTrainPath(signalPosition)
194172
res.add(TravelledPathSignal(signalOffset, isRouteDelimiter))
195173
}
196174
}
197-
currentOffset += fullInfra.blockInfra.getBlockLength(block).distance
198175
}
199176
return res.sorted()
200177
}

core/src/main/kotlin/fr/sncf/osrd/api/standalone_sim/SimulationEndpoint.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package fr.sncf.osrd.api.standalone_sim
22

33
import fr.sncf.osrd.api.*
44
import fr.sncf.osrd.path.interfaces.getLegacyBlockPath
5-
import fr.sncf.osrd.path.interfaces.getLegacyChunkPath
6-
import fr.sncf.osrd.path.interfaces.getLegacyRoutePath
75
import fr.sncf.osrd.reporting.exceptions.OSRDError
86
import fr.sncf.osrd.standalone_sim.runStandaloneSimulation
97
import fr.sncf.osrd.utils.*
@@ -59,17 +57,12 @@ class SimulationEndpoint(
5957
// Parse path
6058
val trainPath =
6159
request.path.toTrainPath(infra.rawInfra, infra.blockInfra, electricalProfileMap)
62-
val chunkPath = trainPath.getLegacyChunkPath()
63-
val routePath = trainPath.getLegacyRoutePath()
6460
val blockPath = trainPath.getLegacyBlockPath()
6561

6662
val res =
6763
runStandaloneSimulation(
6864
infra,
6965
trainPath,
70-
chunkPath,
71-
routePath,
72-
blockPath,
7366
rollingStock,
7467
request.comfort,
7568
request.constraintDistribution.toRJS(),

core/src/main/kotlin/fr/sncf/osrd/standalone_sim/SafetySpeed.kt

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ package fr.sncf.osrd.standalone_sim
22

33
import fr.sncf.osrd.api.FullInfra
44
import fr.sncf.osrd.api.standalone_sim.SimulationScheduleItem
5-
import fr.sncf.osrd.path.implementations.ChunkPath
6-
import fr.sncf.osrd.path.interfaces.BlockPath
7-
import fr.sncf.osrd.path.interfaces.TravelledPath
5+
import fr.sncf.osrd.path.interfaces.TrainPath
86
import fr.sncf.osrd.railjson.schema.schedule.RJSTrainStop
97
import fr.sncf.osrd.signaling.etcs_level2.ETCS_LEVEL2
108
import fr.sncf.osrd.sim_infra.api.*
119
import fr.sncf.osrd.utils.DistanceRangeMap
1210
import fr.sncf.osrd.utils.distanceRangeMapOf
13-
import fr.sncf.osrd.utils.getRoutePathStartOffset
14-
import fr.sncf.osrd.utils.indexing.StaticIdx
1511
import fr.sncf.osrd.utils.units.Offset
1612
import fr.sncf.osrd.utils.units.Speed
1713
import fr.sncf.osrd.utils.units.kilometersPerHour
@@ -21,7 +17,7 @@ import fr.sncf.osrd.utils.units.meters
2117
* Simple internal class representing a stop with safety speed. Makes the function logic more
2218
* straightforward.
2319
*/
24-
private data class SafetySpeedStop(val offset: Offset<TravelledPath>, val isShortSlip: Boolean)
20+
private data class SafetySpeedStop(val offset: Offset<TrainPath>, val isShortSlip: Boolean)
2521

2622
/**
2723
* Compute safety speed ranges, areas where the train has a lower speed limit because of a scheduled
@@ -30,15 +26,12 @@ private data class SafetySpeedStop(val offset: Offset<TravelledPath>, val isShor
3026
*/
3127
fun makeSafetySpeedRanges(
3228
infra: FullInfra,
33-
chunkPath: ChunkPath,
34-
routes: List<RouteId>,
29+
trainPath: TrainPath,
3530
schedule: List<SimulationScheduleItem>,
3631
signalingRanges: DistanceRangeMap<String>,
3732
): DistanceRangeMap<Speed> {
3833
val rawInfra = infra.rawInfra
39-
val zonePaths = routes.flatMap { rawInfra.getRoutePath(it) }
40-
val zonePathStartOffset = getRoutePathStartOffset(rawInfra, chunkPath, routes)
41-
val signalOffsets = getSignalOffsets(infra, zonePaths, zonePathStartOffset)
34+
val signalOffsets = getSignalOffsets(infra, trainPath)
4235

4336
val stopsWithSafetySpeed =
4437
schedule
@@ -66,7 +59,7 @@ fun makeSafetySpeedRanges(
6659
ETCS_LEVEL2.id,
6760
)
6861
) {
69-
makeEndOfPathStop(rawInfra, routes, signalOffsets)?.let { stopsWithSafetySpeed.add(it) }
62+
makeEndOfPathStop(rawInfra, trainPath, signalOffsets)?.let { stopsWithSafetySpeed.add(it) }
7063
}
7164

7265
val res = distanceRangeMapOf<Speed>()
@@ -88,12 +81,12 @@ fun makeSafetySpeedRanges(
8881
}
8982
}
9083
// Safety speed areas may extend outside the path
91-
return res.subMap(0.meters, chunkPath.length)
84+
return res.subMap(0.meters, trainPath.getLength())
9285
}
9386

9487
/** Check if a given stop is in a range of a given signaling system. */
9588
private fun isStopInSignalingSystemRange(
96-
stopOffset: Offset<TravelledPath>,
89+
stopOffset: Offset<TrainPath>,
9790
signalingRanges: DistanceRangeMap<String>,
9891
signalingSystem: String,
9992
): Boolean {
@@ -106,41 +99,38 @@ private fun isStopInSignalingSystemRange(
10699
*/
107100
private fun makeEndOfPathStop(
108101
infra: RawSignalingInfra,
109-
routes: List<RouteId>,
110-
signalOffsets: List<Offset<TravelledPath>>,
102+
trainPath: TrainPath,
103+
signalOffsets: List<Offset<TrainPath>>,
111104
): SafetySpeedStop? {
112-
val lastRouteExit = infra.getRouteExit(routes.last())
105+
val lastRouteExit = infra.getRouteExit(trainPath.getRoutes().last().value)
113106
val isBufferStop = infra.isBufferStop(lastRouteExit.value)
114107
if (isBufferStop) return SafetySpeedStop(signalOffsets.last(), true)
115108
return null
116109
}
117110

118111
/** Return the offsets of block-delimiting signals on the path. */
119-
private fun getSignalOffsets(
120-
infra: FullInfra,
121-
zonePaths: List<StaticIdx<ZonePath>>,
122-
pathStartOffset: Offset<BlockPath>,
123-
): List<Offset<TravelledPath>> {
124-
val res = mutableListOf<Offset<TravelledPath>>()
112+
private fun getSignalOffsets(infra: FullInfra, trainPath: TrainPath): List<Offset<TrainPath>> {
113+
val res = mutableListOf<Offset<TrainPath>>()
125114
val rawInfra = infra.rawInfra
126115
val signalingInfra = infra.loadedSignalInfra
127116
var prevZonePathsLength = 0.meters
128-
for (zonePath in zonePaths) {
117+
for (zonePathRange in trainPath.getZonePaths()) {
118+
val zonePath = zonePathRange.value
129119
val signalPositions = rawInfra.getSignalPositions(zonePath)
130120
val signals = rawInfra.getSignals(zonePath)
131121
for ((signal, signalPosition) in signals zip signalPositions) {
132122
val isDelimiter =
133-
signalingInfra.getLogicalSignals(signal).any { signalingInfra.isBlockDelimiter(it) }
123+
signalingInfra.getLogicalSignals(signal).any(signalingInfra::isBlockDelimiter)
134124
if (isDelimiter) {
135-
res.add(
136-
Offset(prevZonePathsLength + signalPosition.distance - pathStartOffset.distance)
137-
)
125+
res.add(zonePathRange.offsetToTrainPath(signalPosition))
138126
}
139127
}
140128
prevZonePathsLength += rawInfra.getZonePathLength(zonePath).distance
141129
}
142130
// Add one "signal" at the end of the last route no matter what.
143131
// There must be either a signal or a buffer stop, on which we may end safety speed ranges.
144-
res.add(Offset(prevZonePathsLength - pathStartOffset.distance))
132+
val lastRouteRange = trainPath.getRoutes().last()
133+
res.add(lastRouteRange.getObjectAbsolutePathEnd(rawInfra.getRouteLength(lastRouteRange.value)))
134+
145135
return res.filter { it.distance >= 0.meters }
146136
}

0 commit comments

Comments
 (0)