Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdcm: use spacing requirements instead of route occupancies #5163

Merged
merged 2 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@ import fr.sncf.osrd.utils.units.OffsetList
sealed interface Zone
typealias ZoneId = StaticIdx<Zone>

@Suppress("INAPPLICABLE_JVM_NAME")
interface LocationInfra : TrackNetworkInfra, TrackInfra, TrackProperties {
val zones: StaticIdxSpace<Zone>
fun getMovableElements(zone: ZoneId): StaticIdxSortedSet<TrackNode>
fun getZoneBounds(zone: ZoneId): List<DirDetectorId>
@JvmName("getZoneName")
fun getZoneName(zone: ZoneId): String
@JvmName("getZoneFromName")
fun getZoneFromName(name: String): ZoneId

val detectors: StaticIdxSpace<Detector>
fun getNextZone(dirDet: DirDetectorId): ZoneId?
fun getPreviousZone(dirDet: DirDetectorId): ZoneId?
fun getDetectorName(det: DetectorId): String?
}

fun LocationInfra.getZoneName(zone: ZoneId): String {
return "zone.${getZoneBounds(zone).map { "${getDetectorName(it.value)}:${it.direction}" }.minOf { it }}"
}

fun LocationInfra.isBufferStop(detector: StaticIdx<Detector>): Boolean {
return getNextZone(detector.increasing) == null || getNextZone(detector.decreasing) == null
}
Expand Down Expand Up @@ -60,6 +61,7 @@ interface ReservationInfra : LocationInfra {
fun getZonePathChunks(zonePath: ZonePathId): DirStaticIdxList<TrackChunk>
}

@JvmName("getZonePathZone")
fun ReservationInfra.getZonePathZone(zonePath: ZonePathId): ZoneId {
return getNextZone(getZonePathEntry(zonePath))!!
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ interface BlockInfra {
val blocks: StaticIdxSpace<Block>
@JvmName("getBlockPath")
fun getBlockPath(block: BlockId): StaticIdxList<ZonePath>
@JvmName("getBlocksInZone")
fun getBlocksInZone(zone: ZoneId): StaticIdxList<Block>
fun getBlockSignals(block: BlockId): StaticIdxList<LogicalSignal>
fun blockStartAtBufferStop(block: BlockId): Boolean
fun blockStopAtBufferStop(block: BlockId): Boolean

fun getBlockSignalingSystem(block: BlockId): SignalingSystemId
@JvmName("getBlocksAtDetector")
fun getBlocksAtDetector(detector: DirDetectorId): StaticIdxList<Block>
@JvmName("getBlocksStartingAtDetector")
fun getBlocksStartingAtDetector(detector: DirDetectorId): StaticIdxList<Block>
@JvmName("getBlocksEndingAtDetector")
fun getBlocksEndingAtDetector(detector: DirDetectorId): StaticIdxList<Block>
fun getBlocksAtSignal(signal: LogicalSignalId): StaticIdxList<Block>
fun getSignalsPositions(block: BlockId): OffsetList<Block>
@JvmName("getBlocksFromTrackChunk")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,25 @@ class BlockInfraImpl(
private val rawInfra: RawInfra,
) : BlockInfra {
private val blockEntryDetectorMap = IdxMap<DirDetectorId, MutableStaticIdxList<Block>>()
private val blockExitDetectorMap = IdxMap<DirDetectorId, MutableStaticIdxList<Block>>()
private val blockEntrySignalMap = IdxMap<LogicalSignalId, MutableStaticIdxList<Block>>()
private val trackChunkToBlockMap = IdxMap<DirStaticIdx<TrackChunk>, MutableStaticIdxArraySet<Block>>()
private val blockToTrackChunkMap = IdxMap<StaticIdx<Block>, MutableDirStaticIdxList<TrackChunk>>()
private val zoneToBlockMap = IdxMap<ZoneId, MutableStaticIdxList<Block>>()

init {
for (blockId in blockPool.space()) {
val block = blockPool[blockId]
val entryZonePath = block.path[0]
val exitZonePath = block.path[block.path.size - 1]

// Update blockEntryDetectorMap
val entryDirDet = rawInfra.getZonePathEntry(entryZonePath)
val detList = blockEntryDetectorMap.getOrPut(entryDirDet) { mutableStaticIdxArrayListOf() }
detList.add(blockId)
val exitDirDet = rawInfra.getZonePathExit(exitZonePath)
val entryDetList = blockEntryDetectorMap.getOrPut(entryDirDet) { mutableStaticIdxArrayListOf() }
val exitDetList = blockExitDetectorMap.getOrPut(exitDirDet) { mutableStaticIdxArrayListOf() }
entryDetList.add(blockId)
exitDetList.add(blockId)

// Update blockEntrySignalMap
if (!block.startAtBufferStop) {
Expand All @@ -62,7 +68,7 @@ class BlockInfraImpl(
sigList.add(blockId)
}

// Update trackChunkToBlockMap and blockToTrackChunkMap
// Update trackChunkToBlockMap, blockToTrackChunkMap, and zoneToBlockMap
for (zonePath in getBlockPath(blockId)) {
val trackChunks = rawInfra.getZonePathChunks(zonePath)
val blockTrackChunks = blockToTrackChunkMap.getOrPut(blockId) { mutableDirStaticIdxArrayListOf() }
Expand All @@ -71,6 +77,8 @@ class BlockInfraImpl(
val chunkBlocks = trackChunkToBlockMap.getOrPut(trackChunk) { mutableStaticIdxArraySetOf() }
chunkBlocks.add(blockId)
}
zoneToBlockMap.getOrPut(rawInfra.getZonePathZone(zonePath)) { mutableStaticIdxArrayListOf() }
.add(blockId)
}
}
}
Expand All @@ -82,6 +90,10 @@ class BlockInfraImpl(
return blockPool[block].path
}

override fun getBlocksInZone(zone: ZoneId): StaticIdxList<Block> {
return zoneToBlockMap[zone]!!
}

override fun getBlockSignals(block: BlockId): StaticIdxList<LogicalSignal> {
return blockPool[block].signals
}
Expand All @@ -98,10 +110,14 @@ class BlockInfraImpl(
return loadedSignalInfra.getSignalingSystem(blockPool[block].signals[0])
}

override fun getBlocksAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
override fun getBlocksStartingAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
return blockEntryDetectorMap[detector] ?: mutableStaticIdxArrayListOf()
}

override fun getBlocksEndingAtDetector(detector: DirDetectorId): StaticIdxList<Block> {
return blockExitDetectorMap[detector] ?: mutableStaticIdxArrayListOf()
}

override fun getBlocksAtSignal(signal: LogicalSignalId): StaticIdxList<Block> {
return blockEntrySignalMap[signal] ?: mutableStaticIdxArrayListOf()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ class RawInfraBuilderImpl : RawInfraBuilder {
makeTrackNameMap(),
makeRouteNameMap(),
makeDetEntryToRouteMap(),
makeDetExitToRouteMap()
makeDetExitToRouteMap(),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ class TrackChunkDescriptor(
val speedSections: DirectionalMap<DistanceRangeMap<SpeedSection>>
)

@JvmInline
value class ZoneDescriptor(val movableElements: StaticIdxSortedSet<TrackNode>)
class ZoneDescriptor(
val movableElements: StaticIdxSortedSet<TrackNode>,
var name: String = "",
)

interface RouteDescriptor {
val name: String?
Expand Down Expand Up @@ -186,7 +188,8 @@ class RawInfraImpl(
val trackSectionNameMap: Map<String, TrackSectionId>,
val routeNameMap: Map<String, RouteId>,
val dirDetEntryToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>,
val dirDetExitToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>
val dirDetExitToRouteMap: Map<DirDetectorId, StaticIdxList<Route>>,
val zoneNameMap: HashMap<String, ZoneId> = HashMap(),
) : RawInfra {
override val trackNodes: StaticIdxSpace<TrackNode>
get() = trackNodePool.space()
Expand Down Expand Up @@ -307,6 +310,15 @@ class RawInfraImpl(
zoneDetectors[prevZone]!!.add(detector.decreasing)
}

// initialize zone names
for (zone in zonePool) {
val name = getZoneBounds(zone)
.sortedBy { id -> id.index }
.map { "${getDetectorName(it.value)}:${it.direction}" }
zonePool[zone].name = "zone.${name}"
zoneNameMap[zonePool[zone].name] = zone
}

// initialize the physical signal to logical signal map
for (physicalSignal in physicalSignalPool)
for (child in physicalSignalPool[physicalSignal].logicalSignals)
Expand Down Expand Up @@ -360,6 +372,14 @@ class RawInfraImpl(
return zoneDetectors[zone]!!
}

override fun getZoneName(zone: ZoneId): String {
return zonePool[zone].name
}

override fun getZoneFromName(name: String): ZoneId {
return zoneNameMap[name]!!
}

override val detectors: StaticIdxSpace<Detector>
get() = detectorPool.space()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private fun findRouteBlocks(
// initialize with the BlockPathElements which are acceptable at the start of the route
if (previousPaths == null) {
val currentDet = signalingInfra.getZonePathEntry(routePath[0])
val blocks = blockInfra.getBlocksAtDetector(currentDet)
val blocks = blockInfra.getBlocksStartingAtDetector(currentDet)
val blocksOnRoute = filterBlocks(allowedSignalingSystems, blockInfra, blocks, routePath, 0)
for (block in blocksOnRoute) {
val blockPath = blockInfra.getBlockPath(block)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ class TestBALtoBAL {
val loadedSignalInfra = simulator.loadSignals(infra)
val blockInfra = simulator.buildBlocks(infra, loadedSignalInfra)
val fullPath = mutableStaticIdxArrayListOf<Block>()
fullPath.add(blockInfra.getBlocksAtDetector(detectorU.increasing).first())
fullPath.add(blockInfra.getBlocksAtDetector(detectorV.increasing).first())
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorU.increasing).first())
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorV.increasing).first())
val zoneStates = mutableListOf(ZoneStatus.CLEAR, ZoneStatus.CLEAR, ZoneStatus.CLEAR)
val res = simulator.evaluate(infra, loadedSignalInfra, blockInfra, fullPath, 0, fullPath.size, zoneStates, ZoneStatus.INCOMPATIBLE)
assertEquals("A", res[loadedSignalInfra.getLogicalSignals(signalV).first()]!!.getEnum("aspect"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ class TestBAPRtoBAL {
val loadedSignalInfra = simulator.loadSignals(infra)
val blockInfra = simulator.buildBlocks(infra, loadedSignalInfra)
val fullPath = mutableStaticIdxArrayListOf<Block>()
fullPath.add(blockInfra.getBlocksAtDetector(detectorW.increasing).first())
fullPath.add(blockInfra.getBlocksAtDetector(detectorX.increasing).first())
fullPath.add(blockInfra.getBlocksAtDetector(detectorY.increasing).first())
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorW.increasing).first())
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorX.increasing).first())
fullPath.add(blockInfra.getBlocksStartingAtDetector(detectorY.increasing).first())
val zoneStates = mutableListOf(ZoneStatus.CLEAR, ZoneStatus.CLEAR, ZoneStatus.INCOMPATIBLE)
val res = simulator.evaluate(infra, loadedSignalInfra, blockInfra, fullPath, 0, fullPath.size, zoneStates, ZoneStatus.INCOMPATIBLE)
val logicalSignals = listOf(signalm, signalM, signaln, signalN).map{loadedSignalInfra.getLogicalSignals(it).first()}
Expand Down
14 changes: 2 additions & 12 deletions core/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -754,20 +754,10 @@ components:
example: "infraID"
rolling_stocks:
$ref: "#/components/schemas/RollingStock"
route_occupancies:
spacing_requirements:
type: array
items:
type: object
properties:
id:
type: string
description: Route ID
start_occupancy_time:
type: number
format: double
end_occupancy_time:
type: number
format: double
$ref: "#/components/schemas/SpacingRequirement"
start_time:
type: number
format: double
Expand Down
30 changes: 2 additions & 28 deletions core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fr.sncf.osrd.api.stdcm;

import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList;

import fr.sncf.osrd.api.ExceptionHandler;
import fr.sncf.osrd.api.FullInfra;
import fr.sncf.osrd.api.InfraManager;
Expand All @@ -14,8 +12,6 @@
import fr.sncf.osrd.reporting.exceptions.ErrorType;
import fr.sncf.osrd.reporting.exceptions.OSRDError;
import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl;
import fr.sncf.osrd.sim_infra.api.InterlockingInfraKt;
import fr.sncf.osrd.sim_infra.api.RawSignalingInfra;
import fr.sncf.osrd.standalone_sim.ScheduleMetadataExtractor;
import fr.sncf.osrd.standalone_sim.result.ResultEnvelopePoint;
import fr.sncf.osrd.standalone_sim.result.StandaloneSimResult;
Expand All @@ -35,8 +31,6 @@
import org.takes.rs.RsWithBody;
import org.takes.rs.RsWithStatus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

public class STDCMEndpoint implements Take {
Expand Down Expand Up @@ -70,7 +64,6 @@ public Response act(Request req) throws OSRDError {
final var comfort = RJSRollingStockParser.parseComfort(request.comfort);
final var steps = parseSteps(infra, request.steps);
final String tag = request.speedLimitComposition;
var occupancies = request.routeOccupancies;
AllowanceValue standardAllowance = null;
if (request.standardAllowance != null)
standardAllowance = RJSStandaloneTrainScheduleParser.parseAllowanceValue(
Expand All @@ -81,11 +74,10 @@ public Response act(Request req) throws OSRDError {

// Build the unavailable space
// temporary workaround, to remove with new signaling
occupancies = addWarningOccupancies(infra.rawInfra(), occupancies);
var unavailableSpace = UnavailableSpaceBuilder.computeUnavailableSpace(
infra.rawInfra(),
infra.blockInfra(),
occupancies,
request.spacingRequirements,
rollingStock,
request.gridMarginAfterSTDCM,
request.gridMarginBeforeSTDCM
Expand Down Expand Up @@ -140,26 +132,8 @@ private static List<STDCMStep> parseSteps(FullInfra infra, List<STDCMRequest.STD
.toList();
}

/** The inputs only contains occupied blocks, we need to add the warning in the previous one (assuming BAL).
* To be removed with new signaling. */
private static Collection<STDCMRequest.RouteOccupancy> addWarningOccupancies(
RawSignalingInfra rawInfra,
Collection<STDCMRequest.RouteOccupancy> occupancies
) {
var warningOccupancies = new HashSet<>(occupancies);
for (var occupancy : occupancies) {
var route = rawInfra.getRouteFromName(occupancy.id);
var previousRoutes = toIntList(rawInfra.getRoutesEndingAtDet(
InterlockingInfraKt.getRouteEntry(rawInfra, route)));
for (var previousRoute : previousRoutes)
warningOccupancies.add(new STDCMRequest.RouteOccupancy(rawInfra.getRouteName(previousRoute),
occupancy.startOccupancyTime, occupancy.endOccupancyTime));
}
return warningOccupancies;
}

/** Generate a train schedule matching the envelope and rolling stock, with one stop at the end */
private static StandaloneTrainSchedule makeTrainSchedule(
public static StandaloneTrainSchedule makeTrainSchedule(
double endPos,
RollingStock rollingStock,
RollingStock.Comfort comfort,
Expand Down
37 changes: 6 additions & 31 deletions core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import fr.sncf.osrd.railjson.schema.rollingstock.RJSRollingStock;
import fr.sncf.osrd.railjson.schema.schedule.RJSAllowance;
import fr.sncf.osrd.railjson.schema.schedule.RJSAllowanceValue;
import fr.sncf.osrd.standalone_sim.result.ResultTrain;
import java.util.Collection;
import java.util.List;

Expand Down Expand Up @@ -46,10 +47,10 @@ public final class STDCMRequest {
public RJSComfortType comfort;

/**
* Route occupancies in the given timetable
* Spacing requirements for any train in the timetable
*/
@Json(name = "route_occupancies")
public Collection<RouteOccupancy> routeOccupancies;
@Json(name = "spacing_requirements")
public Collection<ResultTrain.SpacingRequirement> spacingRequirements;

/** A list of steps on the path. A step is a set of location with a stop duration.
* The path only has to go through a single point per location.
Expand Down Expand Up @@ -142,7 +143,7 @@ public STDCMRequest(
String infra,
String expectedVersion,
RJSRollingStock rollingStock,
Collection<RouteOccupancy> routeOccupancies,
Collection<ResultTrain.SpacingRequirement> spacingRequirements,
List<STDCMStep> steps,
double startTime,
double endTime,
Expand All @@ -154,7 +155,7 @@ public STDCMRequest(
this.infra = infra;
this.expectedVersion = expectedVersion;
this.rollingStock = rollingStock;
this.routeOccupancies = routeOccupancies;
this.spacingRequirements = spacingRequirements;
this.steps = steps;
this.startTime = startTime;
this.endTime = endTime;
Expand Down Expand Up @@ -184,30 +185,4 @@ public STDCMStep(double stopDuration, boolean stop, Collection<PathfindingWaypoi
this.waypoints = waypoints;
}
}

public static class RouteOccupancy {
/**
* ID of the occupied route
*/
public String id;

/**
* Time at which the route starts being occupied
*/
@Json(name = "start_occupancy_time")
public double startOccupancyTime;

/**
* Time at which the route ends being occupied
*/
@Json(name = "end_occupancy_time")
public double endOccupancyTime;

/** Creates a new route occupancy */
public RouteOccupancy(String id, double startOccupancyTime, double endOccupancyTime) {
this.id = id;
this.startOccupancyTime = startOccupancyTime;
this.endOccupancyTime = endOccupancyTime;
}
}
}
Loading