Skip to content

Commit 4bdbdb8

Browse files
committed
core: stdcm: use spacing requirements instead of route occupancies
1 parent d23d219 commit 4bdbdb8

File tree

22 files changed

+285
-275
lines changed

22 files changed

+285
-275
lines changed

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/InterlockingInfra.kt

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,22 @@ import fr.sncf.osrd.utils.units.OffsetList
1818
sealed interface Zone
1919
typealias ZoneId = StaticIdx<Zone>
2020

21+
@Suppress("INAPPLICABLE_JVM_NAME")
2122
interface LocationInfra : TrackNetworkInfra, TrackInfra, TrackProperties {
2223
val zones: StaticIdxSpace<Zone>
2324
fun getMovableElements(zone: ZoneId): StaticIdxSortedSet<TrackNode>
2425
fun getZoneBounds(zone: ZoneId): List<DirDetectorId>
26+
@JvmName("getZoneName")
27+
fun getZoneName(zone: ZoneId): String
28+
@JvmName("getZoneFromName")
29+
fun getZoneFromName(name: String): ZoneId
2530

2631
val detectors: StaticIdxSpace<Detector>
2732
fun getNextZone(dirDet: DirDetectorId): ZoneId?
2833
fun getPreviousZone(dirDet: DirDetectorId): ZoneId?
2934
fun getDetectorName(det: DetectorId): String?
3035
}
3136

32-
fun LocationInfra.getZoneName(zone: ZoneId): String {
33-
return "zone.${getZoneBounds(zone).map { "${getDetectorName(it.value)}:${it.direction}" }.minOf { it }}"
34-
}
35-
3637
fun LocationInfra.isBufferStop(detector: StaticIdx<Detector>): Boolean {
3738
return getNextZone(detector.increasing) == null || getNextZone(detector.decreasing) == null
3839
}
@@ -60,6 +61,7 @@ interface ReservationInfra : LocationInfra {
6061
fun getZonePathChunks(zonePath: ZonePathId): DirStaticIdxList<TrackChunk>
6162
}
6263

64+
@JvmName("getZonePathZone")
6365
fun ReservationInfra.getZonePathZone(zonePath: ZonePathId): ZoneId {
6466
return getNextZone(getZonePathEntry(zonePath))!!
6567
}

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/LoadedSignalingInfra.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,17 @@ interface BlockInfra {
5858
val blocks: StaticIdxSpace<Block>
5959
@JvmName("getBlockPath")
6060
fun getBlockPath(block: BlockId): StaticIdxList<ZonePath>
61+
@JvmName("getBlocksInZone")
62+
fun getBlocksInZone(zone: ZoneId): StaticIdxList<Block>
6163
fun getBlockSignals(block: BlockId): StaticIdxList<LogicalSignal>
6264
fun blockStartAtBufferStop(block: BlockId): Boolean
6365
fun blockStopAtBufferStop(block: BlockId): Boolean
6466

6567
fun getBlockSignalingSystem(block: BlockId): SignalingSystemId
66-
@JvmName("getBlocksAtDetector")
67-
fun getBlocksAtDetector(detector: DirDetectorId): StaticIdxList<Block>
68+
@JvmName("getBlocksStartingAtDetector")
69+
fun getBlocksStartingAtDetector(detector: DirDetectorId): StaticIdxList<Block>
70+
@JvmName("getBlocksEndingAtDetector")
71+
fun getBlocksEndingAtDetector(detector: DirDetectorId): StaticIdxList<Block>
6872
fun getBlocksAtSignal(signal: LogicalSignalId): StaticIdxList<Block>
6973
fun getSignalsPositions(block: BlockId): OffsetList<Block>
7074
@JvmName("getBlocksFromTrackChunk")

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/BlockInfraImpl.kt

+20-4
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,25 @@ class BlockInfraImpl(
4141
private val rawInfra: RawInfra,
4242
) : BlockInfra {
4343
private val blockEntryDetectorMap = IdxMap<DirDetectorId, MutableStaticIdxList<Block>>()
44+
private val blockExitDetectorMap = IdxMap<DirDetectorId, MutableStaticIdxList<Block>>()
4445
private val blockEntrySignalMap = IdxMap<LogicalSignalId, MutableStaticIdxList<Block>>()
4546
private val trackChunkToBlockMap = IdxMap<DirStaticIdx<TrackChunk>, MutableStaticIdxArraySet<Block>>()
4647
private val blockToTrackChunkMap = IdxMap<StaticIdx<Block>, MutableDirStaticIdxList<TrackChunk>>()
48+
private val zoneToBlockMap = IdxMap<ZoneId, MutableStaticIdxList<Block>>()
4749

4850
init {
4951
for (blockId in blockPool.space()) {
5052
val block = blockPool[blockId]
5153
val entryZonePath = block.path[0]
54+
val exitZonePath = block.path[block.path.size - 1]
5255

5356
// Update blockEntryDetectorMap
5457
val entryDirDet = rawInfra.getZonePathEntry(entryZonePath)
55-
val detList = blockEntryDetectorMap.getOrPut(entryDirDet) { mutableStaticIdxArrayListOf() }
56-
detList.add(blockId)
58+
val exitDirDet = rawInfra.getZonePathExit(exitZonePath)
59+
val entryDetList = blockEntryDetectorMap.getOrPut(entryDirDet) { mutableStaticIdxArrayListOf() }
60+
val exitDetList = blockExitDetectorMap.getOrPut(exitDirDet) { mutableStaticIdxArrayListOf() }
61+
entryDetList.add(blockId)
62+
exitDetList.add(blockId)
5763

5864
// Update blockEntrySignalMap
5965
if (!block.startAtBufferStop) {
@@ -62,7 +68,7 @@ class BlockInfraImpl(
6268
sigList.add(blockId)
6369
}
6470

65-
// Update trackChunkToBlockMap and blockToTrackChunkMap
71+
// Update trackChunkToBlockMap, blockToTrackChunkMap, and zoneToBlockMap
6672
for (zonePath in getBlockPath(blockId)) {
6773
val trackChunks = rawInfra.getZonePathChunks(zonePath)
6874
val blockTrackChunks = blockToTrackChunkMap.getOrPut(blockId) { mutableDirStaticIdxArrayListOf() }
@@ -71,6 +77,8 @@ class BlockInfraImpl(
7177
val chunkBlocks = trackChunkToBlockMap.getOrPut(trackChunk) { mutableStaticIdxArraySetOf() }
7278
chunkBlocks.add(blockId)
7379
}
80+
zoneToBlockMap.getOrPut(rawInfra.getZonePathZone(zonePath)) { mutableStaticIdxArrayListOf() }
81+
.add(blockId)
7482
}
7583
}
7684
}
@@ -82,6 +90,10 @@ class BlockInfraImpl(
8290
return blockPool[block].path
8391
}
8492

93+
override fun getBlocksInZone(zone: ZoneId): StaticIdxList<Block> {
94+
return zoneToBlockMap[zone]!!
95+
}
96+
8597
override fun getBlockSignals(block: BlockId): StaticIdxList<LogicalSignal> {
8698
return blockPool[block].signals
8799
}
@@ -98,10 +110,14 @@ class BlockInfraImpl(
98110
return loadedSignalInfra.getSignalingSystem(blockPool[block].signals[0])
99111
}
100112

101-
override fun getBlocksAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
113+
override fun getBlocksStartingAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
102114
return blockEntryDetectorMap[detector] ?: mutableStaticIdxArrayListOf()
103115
}
104116

117+
override fun getBlocksEndingAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
118+
return blockExitDetectorMap[detector] ?: mutableStaticIdxArrayListOf()
119+
}
120+
105121
override fun getBlocksAtSignal(signal: LogicalSignalId): StaticIdxList<Block> {
106122
return blockEntrySignalMap[signal] ?: mutableStaticIdxArrayListOf()
107123
}

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/RawInfraBuilder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ class RawInfraBuilderImpl : RawInfraBuilder {
447447
makeTrackNameMap(),
448448
makeRouteNameMap(),
449449
makeDetEntryToRouteMap(),
450-
makeDetExitToRouteMap()
450+
makeDetExitToRouteMap(),
451451
)
452452
}
453453

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/RawInfraImpl.kt

+23-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ class TrackChunkDescriptor(
9191
val speedSections: DirectionalMap<DistanceRangeMap<SpeedSection>>
9292
)
9393

94-
@JvmInline
95-
value class ZoneDescriptor(val movableElements: StaticIdxSortedSet<TrackNode>)
94+
class ZoneDescriptor(
95+
val movableElements: StaticIdxSortedSet<TrackNode>,
96+
var name: String = "",
97+
)
9698

9799
interface RouteDescriptor {
98100
val name: String?
@@ -186,7 +188,8 @@ class RawInfraImpl(
186188
val trackSectionNameMap: Map<String, TrackSectionId>,
187189
val routeNameMap: Map<String, RouteId>,
188190
val dirDetEntryToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>,
189-
val dirDetExitToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>
191+
val dirDetExitToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>,
192+
val zoneNameMap: HashMap<String, ZoneId> = HashMap(),
190193
) : RawInfra {
191194
override val trackNodes: StaticIdxSpace<TrackNode>
192195
get() = trackNodePool.space()
@@ -307,6 +310,15 @@ class RawInfraImpl(
307310
zoneDetectors[prevZone]!!.add(detector.decreasing)
308311
}
309312

313+
// initialize zone names
314+
for (zone in zonePool) {
315+
val name = getZoneBounds(zone)
316+
.sortedBy { id -> id.index }
317+
.map { "${getDetectorName(it.value)}:${it.direction}" }
318+
zonePool[zone].name = "zone.${name}"
319+
zoneNameMap[zonePool[zone].name] = zone
320+
}
321+
310322
// initialize the physical signal to logical signal map
311323
for (physicalSignal in physicalSignalPool)
312324
for (child in physicalSignalPool[physicalSignal].logicalSignals)
@@ -360,6 +372,14 @@ class RawInfraImpl(
360372
return zoneDetectors[zone]!!
361373
}
362374

375+
override fun getZoneName(zone: ZoneId): String {
376+
return zonePool[zone].name
377+
}
378+
379+
override fun getZoneFromName(name: String): ZoneId {
380+
return zoneNameMap[name]!!
381+
}
382+
363383
override val detectors: StaticIdxSpace<Detector>
364384
get() = detectorPool.space()
365385

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/utils/BlockRecovery.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private fun findRouteBlocks(
111111
// initialize with the BlockPathElements which are acceptable at the start of the route
112112
if (previousPaths == null) {
113113
val currentDet = signalingInfra.getZonePathEntry(routePath[0])
114-
val blocks = blockInfra.getBlocksAtDetector(currentDet)
114+
val blocks = blockInfra.getBlocksStartingAtDetector(currentDet)
115115
val blocksOnRoute = filterBlocks(allowedSignalingSystems, blockInfra, blocks, routePath, 0)
116116
for (block in blocksOnRoute) {
117117
val blockPath = blockInfra.getBlockPath(block)

core/kt-osrd-sncf-signaling/src/test/kotlin/fr/sncf/osrd/signaling/bal/TestBALtoBAL.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ class TestBALtoBAL {
117117
val loadedSignalInfra = simulator.loadSignals(infra)
118118
val blockInfra = simulator.buildBlocks(infra, loadedSignalInfra)
119119
val fullPath = mutableStaticIdxArrayListOf<Block>()
120-
fullPath.add(blockInfra.getBlocksAtDetector(detectorU.increasing).first())
121-
fullPath.add(blockInfra.getBlocksAtDetector(detectorV.increasing).first())
120+
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorU.increasing).first())
121+
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorV.increasing).first())
122122
val zoneStates = mutableListOf(ZoneStatus.CLEAR, ZoneStatus.CLEAR, ZoneStatus.CLEAR)
123123
val res = simulator.evaluate(infra, loadedSignalInfra, blockInfra, fullPath, 0, fullPath.size, zoneStates, ZoneStatus.INCOMPATIBLE)
124124
assertEquals("A", res[loadedSignalInfra.getLogicalSignals(signalV).first()]!!.getEnum("aspect"))

core/kt-osrd-sncf-signaling/src/test/kotlin/fr/sncf/osrd/signaling/bal/TestBAPRtoBAL.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ class TestBAPRtoBAL {
9898
val loadedSignalInfra = simulator.loadSignals(infra)
9999
val blockInfra = simulator.buildBlocks(infra, loadedSignalInfra)
100100
val fullPath = mutableStaticIdxArrayListOf<Block>()
101-
fullPath.add(blockInfra.getBlocksAtDetector(detectorW.increasing).first())
102-
fullPath.add(blockInfra.getBlocksAtDetector(detectorX.increasing).first())
103-
fullPath.add(blockInfra.getBlocksAtDetector(detectorY.increasing).first())
101+
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorW.increasing).first())
102+
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorX.increasing).first())
103+
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorY.increasing).first())
104104
val zoneStates = mutableListOf(ZoneStatus.CLEAR, ZoneStatus.CLEAR, ZoneStatus.INCOMPATIBLE)
105105
val res = simulator.evaluate(infra, loadedSignalInfra, blockInfra, fullPath, 0, fullPath.size, zoneStates, ZoneStatus.INCOMPATIBLE)
106106
val logicalSignals = listOf(signalm, signalM, signaln, signalN).map{loadedSignalInfra.getLogicalSignals(it).first()}

core/openapi.yaml

+2-12
Original file line numberDiff line numberDiff line change
@@ -754,20 +754,10 @@ components:
754754
example: "infraID"
755755
rolling_stocks:
756756
$ref: "#/components/schemas/RollingStock"
757-
route_occupancies:
757+
spacing_requirements:
758758
type: array
759759
items:
760-
type: object
761-
properties:
762-
id:
763-
type: string
764-
description: Route ID
765-
start_occupancy_time:
766-
type: number
767-
format: double
768-
end_occupancy_time:
769-
type: number
770-
format: double
760+
$ref: "#/components/schemas/SpacingRequirement"
771761
start_time:
772762
type: number
773763
format: double

core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java

+2-28
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package fr.sncf.osrd.api.stdcm;
22

3-
import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList;
4-
53
import fr.sncf.osrd.api.ExceptionHandler;
64
import fr.sncf.osrd.api.FullInfra;
75
import fr.sncf.osrd.api.InfraManager;
@@ -14,8 +12,6 @@
1412
import fr.sncf.osrd.reporting.exceptions.ErrorType;
1513
import fr.sncf.osrd.reporting.exceptions.OSRDError;
1614
import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl;
17-
import fr.sncf.osrd.sim_infra.api.InterlockingInfraKt;
18-
import fr.sncf.osrd.sim_infra.api.RawSignalingInfra;
1915
import fr.sncf.osrd.standalone_sim.ScheduleMetadataExtractor;
2016
import fr.sncf.osrd.standalone_sim.result.ResultEnvelopePoint;
2117
import fr.sncf.osrd.standalone_sim.result.StandaloneSimResult;
@@ -35,8 +31,6 @@
3531
import org.takes.rs.RsWithBody;
3632
import org.takes.rs.RsWithStatus;
3733
import java.util.ArrayList;
38-
import java.util.Collection;
39-
import java.util.HashSet;
4034
import java.util.List;
4135

4236
public class STDCMEndpoint implements Take {
@@ -70,7 +64,6 @@ public Response act(Request req) throws OSRDError {
7064
final var comfort = RJSRollingStockParser.parseComfort(request.comfort);
7165
final var steps = parseSteps(infra, request.steps);
7266
final String tag = request.speedLimitComposition;
73-
var occupancies = request.routeOccupancies;
7467
AllowanceValue standardAllowance = null;
7568
if (request.standardAllowance != null)
7669
standardAllowance = RJSStandaloneTrainScheduleParser.parseAllowanceValue(
@@ -81,11 +74,10 @@ public Response act(Request req) throws OSRDError {
8174

8275
// Build the unavailable space
8376
// temporary workaround, to remove with new signaling
84-
occupancies = addWarningOccupancies(infra.rawInfra(), occupancies);
8577
var unavailableSpace = UnavailableSpaceBuilder.computeUnavailableSpace(
8678
infra.rawInfra(),
8779
infra.blockInfra(),
88-
occupancies,
80+
request.spacingRequirements,
8981
rollingStock,
9082
request.gridMarginAfterSTDCM,
9183
request.gridMarginBeforeSTDCM
@@ -140,26 +132,8 @@ private static List<STDCMStep> parseSteps(FullInfra infra, List<STDCMRequest.STD
140132
.toList();
141133
}
142134

143-
/** The inputs only contains occupied blocks, we need to add the warning in the previous one (assuming BAL).
144-
* To be removed with new signaling. */
145-
private static Collection<STDCMRequest.RouteOccupancy> addWarningOccupancies(
146-
RawSignalingInfra rawInfra,
147-
Collection<STDCMRequest.RouteOccupancy> occupancies
148-
) {
149-
var warningOccupancies = new HashSet<>(occupancies);
150-
for (var occupancy : occupancies) {
151-
var route = rawInfra.getRouteFromName(occupancy.id);
152-
var previousRoutes = toIntList(rawInfra.getRoutesEndingAtDet(
153-
InterlockingInfraKt.getRouteEntry(rawInfra, route)));
154-
for (var previousRoute : previousRoutes)
155-
warningOccupancies.add(new STDCMRequest.RouteOccupancy(rawInfra.getRouteName(previousRoute),
156-
occupancy.startOccupancyTime, occupancy.endOccupancyTime));
157-
}
158-
return warningOccupancies;
159-
}
160-
161135
/** Generate a train schedule matching the envelope and rolling stock, with one stop at the end */
162-
private static StandaloneTrainSchedule makeTrainSchedule(
136+
public static StandaloneTrainSchedule makeTrainSchedule(
163137
double endPos,
164138
RollingStock rollingStock,
165139
RollingStock.Comfort comfort,

core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMRequest.java

+6-31
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import fr.sncf.osrd.railjson.schema.rollingstock.RJSRollingStock;
1212
import fr.sncf.osrd.railjson.schema.schedule.RJSAllowance;
1313
import fr.sncf.osrd.railjson.schema.schedule.RJSAllowanceValue;
14+
import fr.sncf.osrd.standalone_sim.result.ResultTrain;
1415
import java.util.Collection;
1516
import java.util.List;
1617

@@ -46,10 +47,10 @@ public final class STDCMRequest {
4647
public RJSComfortType comfort;
4748

4849
/**
49-
* Route occupancies in the given timetable
50+
* Spacing requirements for any train in the timetable
5051
*/
51-
@Json(name = "route_occupancies")
52-
public Collection<RouteOccupancy> routeOccupancies;
52+
@Json(name = "spacing_requirements")
53+
public Collection<ResultTrain.SpacingRequirement> spacingRequirements;
5354

5455
/** A list of steps on the path. A step is a set of location with a stop duration.
5556
* The path only has to go through a single point per location.
@@ -142,7 +143,7 @@ public STDCMRequest(
142143
String infra,
143144
String expectedVersion,
144145
RJSRollingStock rollingStock,
145-
Collection<RouteOccupancy> routeOccupancies,
146+
Collection<ResultTrain.SpacingRequirement> spacingRequirements,
146147
List<STDCMStep> steps,
147148
double startTime,
148149
double endTime,
@@ -154,7 +155,7 @@ public STDCMRequest(
154155
this.infra = infra;
155156
this.expectedVersion = expectedVersion;
156157
this.rollingStock = rollingStock;
157-
this.routeOccupancies = routeOccupancies;
158+
this.spacingRequirements = spacingRequirements;
158159
this.steps = steps;
159160
this.startTime = startTime;
160161
this.endTime = endTime;
@@ -184,30 +185,4 @@ public STDCMStep(double stopDuration, boolean stop, Collection<PathfindingWaypoi
184185
this.waypoints = waypoints;
185186
}
186187
}
187-
188-
public static class RouteOccupancy {
189-
/**
190-
* ID of the occupied route
191-
*/
192-
public String id;
193-
194-
/**
195-
* Time at which the route starts being occupied
196-
*/
197-
@Json(name = "start_occupancy_time")
198-
public double startOccupancyTime;
199-
200-
/**
201-
* Time at which the route ends being occupied
202-
*/
203-
@Json(name = "end_occupancy_time")
204-
public double endOccupancyTime;
205-
206-
/** Creates a new route occupancy */
207-
public RouteOccupancy(String id, double startOccupancyTime, double endOccupancyTime) {
208-
this.id = id;
209-
this.startOccupancyTime = startOccupancyTime;
210-
this.endOccupancyTime = endOccupancyTime;
211-
}
212-
}
213188
}

0 commit comments

Comments
 (0)