diff --git a/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/PathProperties.kt b/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/PathProperties.kt index bbf9b44e996..d1bf7db3418 100644 --- a/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/PathProperties.kt +++ b/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/PathProperties.kt @@ -1,18 +1,16 @@ package fr.sncf.osrd.sim_infra.api import fr.sncf.osrd.geom.LineString -import fr.sncf.osrd.reporting.exceptions.ErrorType -import fr.sncf.osrd.reporting.exceptions.OSRDError +import fr.sncf.osrd.sim_infra.impl.ChunkPath import fr.sncf.osrd.sim_infra.impl.NeutralSection import fr.sncf.osrd.sim_infra.impl.PathPropertiesImpl +import fr.sncf.osrd.sim_infra.impl.buildChunkPath import fr.sncf.osrd.utils.DistanceRangeMap import fr.sncf.osrd.utils.indexing.DirStaticIdxList import fr.sncf.osrd.utils.indexing.StaticIdx -import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf import fr.sncf.osrd.utils.units.Distance import fr.sncf.osrd.utils.units.Offset import fr.sncf.osrd.utils.units.Speed -import fr.sncf.osrd.utils.units.meters data class IdxWithOffset( @get:JvmName("getValue") @@ -49,10 +47,6 @@ interface PathProperties { @JvmName("getTrackLocationOffset") fun getTrackLocationOffset(location: TrackLocation): Distance? fun getRangeMapFromUndirected(getData: (chunkId: TrackChunkId) -> DistanceRangeMap): DistanceRangeMap - - val chunks: DirStaticIdxList - /** Returns the offset where the train starts (must be located on the first chunk) */ - val beginOffset: Distance } /** Build a Path from chunks and offsets, filtering the chunks outside the offsets */ @@ -63,27 +57,13 @@ fun buildPathPropertiesFrom( pathBeginOffset: Distance, pathEndOffset: Distance, ): PathProperties { - val filteredChunks = mutableDirStaticIdxArrayListOf() - var totalLength = 0.meters - var mutBeginOffset = pathBeginOffset - var mutEndOffset = pathEndOffset - for (dirChunkId in chunks) { - if (totalLength >= pathEndOffset) - break - val length = infra.getTrackChunkLength(dirChunkId.value) - val blockEndOffset = totalLength + length.distance + val chunkPath = buildChunkPath(infra, chunks, pathBeginOffset, pathEndOffset) + return makePathProperties(infra, chunkPath) +} - // if the block ends before the path starts, it can be safely skipped - // If a block ends where the path starts, it can be skipped too - if (pathBeginOffset >= blockEndOffset) { - mutBeginOffset -= length.distance - mutEndOffset -= length.distance - } else { - filteredChunks.add(dirChunkId) - } - totalLength += length.distance - } - return PathPropertiesImpl(infra, filteredChunks, mutBeginOffset, mutEndOffset) +@JvmName("makePathProperties") +fun makePathProperties(infra: TrackProperties, chunkPath: ChunkPath): PathProperties { + return PathPropertiesImpl(infra, chunkPath) } /** For java interoperability purpose */ diff --git a/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/PathPropertiesImpl.kt b/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/PathPropertiesImpl.kt index f9808268826..2b74b5465bc 100644 --- a/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/PathPropertiesImpl.kt +++ b/core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/PathPropertiesImpl.kt @@ -1,31 +1,31 @@ package fr.sncf.osrd.sim_infra.impl import fr.sncf.osrd.geom.LineString -import fr.sncf.osrd.sim_infra.api.DirTrackChunkId -import fr.sncf.osrd.sim_infra.api.IdxWithOffset -import fr.sncf.osrd.sim_infra.api.LoadingGaugeConstraint -import fr.sncf.osrd.sim_infra.api.OperationalPointPart -import fr.sncf.osrd.sim_infra.api.PathProperties -import fr.sncf.osrd.sim_infra.api.TrackChunk -import fr.sncf.osrd.sim_infra.api.TrackChunkId -import fr.sncf.osrd.sim_infra.api.TrackLocation -import fr.sncf.osrd.sim_infra.api.TrackProperties +import fr.sncf.osrd.sim_infra.api.* import fr.sncf.osrd.utils.Direction import fr.sncf.osrd.utils.DistanceRangeMap import fr.sncf.osrd.utils.distanceRangeMapOf import fr.sncf.osrd.utils.indexing.DirStaticIdxList +import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf import fr.sncf.osrd.utils.units.Distance import fr.sncf.osrd.utils.units.Offset import fr.sncf.osrd.utils.units.Speed import fr.sncf.osrd.utils.units.meters -data class PathPropertiesImpl( - val infra: TrackProperties, - override val chunks: DirStaticIdxList, +/** A ChunkPath is a list of directional track chunks which form a path, with beginOffset being the offset + * on the first chunk, and endOffset on the last chunk. **/ +data class ChunkPath( + @get:JvmName("getChunks") + val chunks: DirStaticIdxList, @get:JvmName("getBeginOffset") - override val beginOffset: Distance, + val beginOffset: Distance, @get:JvmName("getEndOffset") - val endOffset: Distance, + val endOffset: Distance +) + +data class PathPropertiesImpl( + val infra: TrackProperties, + val chunkPath: ChunkPath ) : PathProperties { override fun getSlopes(): DistanceRangeMap { return getRangeMap { dirChunkId -> infra.getTrackChunkSlope(dirChunkId) } @@ -67,13 +67,13 @@ data class PathPropertiesImpl( } override fun getLength(): Distance { - return endOffset - beginOffset + return chunkPath.endOffset - chunkPath.beginOffset } override fun getTrackLocationAtOffset(pathOffset: Distance): TrackLocation { - val offset = pathOffset + beginOffset + val offset = pathOffset + chunkPath.beginOffset var lengthPrevChunks = 0.meters - for (chunk in chunks) { + for (chunk in chunkPath.chunks) { val chunkLength = infra.getTrackChunkLength(chunk.value).distance if (lengthPrevChunks + chunkLength >= offset) { val trackId = infra.getTrackFromChunk(chunk.value) @@ -90,10 +90,10 @@ data class PathPropertiesImpl( } override fun getTrackLocationOffset(location: TrackLocation): Distance? { - val offset = getOffsetOfTrackLocationOnChunks(infra, location, chunks) ?: return null - if (offset < beginOffset || offset > endOffset) + val offset = getOffsetOfTrackLocationOnChunks(infra, location, chunkPath.chunks) ?: return null + if (offset < chunkPath.beginOffset || offset > chunkPath.endOffset) return null - return offset - beginOffset + return offset - chunkPath.beginOffset } private fun projectLineString(getData: (chunkId: TrackChunkId) -> LineString): LineString { @@ -119,19 +119,20 @@ data class PathPropertiesImpl( ) } + val chunks = chunkPath.chunks if (chunks.size == 0) return LineString.make(doubleArrayOf(), doubleArrayOf()) if (chunks.size == 1) - return sliceChunkData(chunks.first(), beginOffset, endOffset) + return sliceChunkData(chunks.first(), chunkPath.beginOffset, chunkPath.endOffset) val lineStrings = arrayListOf() - lineStrings.add(sliceChunkData(chunks.first(), beginOffset, null)) + lineStrings.add(sliceChunkData(chunks.first(), chunkPath.beginOffset, null)) var totalChunkDistance = infra.getTrackChunkLength(chunks.first().value).distance for (i in 1 until chunks.size - 1) { lineStrings.add(getDirData(chunks[i])) totalChunkDistance += infra.getTrackChunkLength(chunks[i].value).distance } - lineStrings.add(sliceChunkData(chunks.last(), null, endOffset - totalChunkDistance)) + lineStrings.add(sliceChunkData(chunks.last(), null, chunkPath.endOffset - totalChunkDistance)) return LineString.concatenate(lineStrings) } @@ -141,13 +142,13 @@ data class PathPropertiesImpl( ): DistanceRangeMap { val maps = ArrayList>() val distances = ArrayList() - for (dirChunk in chunks) { + for (dirChunk in chunkPath.chunks) { maps.add(getData.invoke(dirChunk)) distances.add(infra.getTrackChunkLength(dirChunk.value).distance) } val mergedMap = mergeMaps(maps, distances) - mergedMap.truncate(beginOffset, endOffset) - mergedMap.shiftPositions(-beginOffset) + mergedMap.truncate(chunkPath.beginOffset, chunkPath.endOffset) + mergedMap.shiftPositions(-chunkPath.beginOffset) return mergedMap } @@ -178,7 +179,7 @@ data class PathPropertiesImpl( ): List> { val res = ArrayList>() var chunkOffset = 0.meters - for (chunk in chunks) { + for (chunk in chunkPath.chunks) { for ((element, offset) in getData.invoke(chunk)) { val projectedOffset = projectPosition(chunk, offset) res.add(IdxWithOffset(element, chunkOffset + projectedOffset)) @@ -202,9 +203,8 @@ data class PathPropertiesImpl( /** Keeps only the elements that are not outside the path, and shift the offsets to start at 0 */ private fun filterAndShiftElementsOnPath(res: List>): List> { return res - .filter { element -> element.offset >= beginOffset } - .filter { element -> element.offset <= endOffset } - .map { element -> IdxWithOffset(element.value, element.offset - beginOffset) } + .filter { element -> element.offset >= chunkPath.beginOffset && element.offset <= chunkPath.endOffset } + .map { element -> IdxWithOffset(element.value, element.offset - chunkPath.beginOffset) } } /** Merge all the given range maps, offsetting them by the given distances. The list sizes must match. */ @@ -255,3 +255,34 @@ fun getOffsetOfTrackLocationOnChunksOrThrow( ): Distance { return getOffsetOfTrackLocationOnChunks(infra, location, chunks) ?: throw RuntimeException() } + +/** Build chunkPath, which is the subset of the given chunks corresponding to the beginOffset and endOffset. **/ +@JvmName("buildChunkPath") +fun buildChunkPath( + infra: TrackProperties, + chunks: DirStaticIdxList, + pathBeginOffset: Distance, + pathEndOffset: Distance +): ChunkPath { + val filteredChunks = mutableDirStaticIdxArrayListOf() + var totalLength = 0.meters + var mutBeginOffset = pathBeginOffset + var mutEndOffset = pathEndOffset + for (dirChunkId in chunks) { + if (totalLength >= pathEndOffset) + break + val length = infra.getTrackChunkLength(dirChunkId.value) + val blockEndOffset = totalLength + length.distance + + // if the block ends before the path starts, it can be safely skipped + // If a block ends where the path starts, it can be skipped too + if (pathBeginOffset >= blockEndOffset) { + mutBeginOffset -= length.distance + mutEndOffset -= length.distance + } else { + filteredChunks.add(dirChunkId) + } + totalLength += length.distance + } + return ChunkPath(filteredChunks, mutBeginOffset, mutEndOffset) +} diff --git a/core/src/main/java/fr/sncf/osrd/api/SignalProjectionEndpoint.java b/core/src/main/java/fr/sncf/osrd/api/SignalProjectionEndpoint.java index 1688987fd9c..d29beda6005 100644 --- a/core/src/main/java/fr/sncf/osrd/api/SignalProjectionEndpoint.java +++ b/core/src/main/java/fr/sncf/osrd/api/SignalProjectionEndpoint.java @@ -1,6 +1,6 @@ package fr.sncf.osrd.api; -import static fr.sncf.osrd.api.utils.PathPropUtils.makePathProps; +import static fr.sncf.osrd.api.utils.PathPropUtils.makeChunkPath; import com.squareup.moshi.Json; import com.squareup.moshi.JsonAdapter; @@ -13,7 +13,8 @@ import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl; import fr.sncf.osrd.reporting.warnings.Warning; import fr.sncf.osrd.standalone_sim.SignalProjectionKt; -import fr.sncf.osrd.standalone_sim.result.*; +import fr.sncf.osrd.standalone_sim.result.ResultTrain; +import fr.sncf.osrd.standalone_sim.result.SignalUpdate; import org.takes.Request; import org.takes.Response; import org.takes.Take; @@ -57,11 +58,11 @@ public Response act(Request req) throws Exception { var infra = infraManager.getInfra(request.infra, request.expectedVersion, recorder); // Parse trainPath - var trainPath = makePathProps(infra.rawInfra(), request.trainPath); + var chunkPath = makeChunkPath(infra.rawInfra(), request.trainPath); var routePath = request.trainPath.routePath.stream() .map(rjsRoutePath -> infra.rawInfra().getRouteFromName(rjsRoutePath.route)) .toList(); - var result = SignalProjectionKt.project(infra, trainPath, routePath, request.signalSightings, + var result = SignalProjectionKt.project(infra, chunkPath, routePath, request.signalSightings, request.zoneUpdates); result.warnings = recorder.warnings; diff --git a/core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java b/core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java index 78130208e61..5cd11b066a3 100644 --- a/core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java +++ b/core/src/main/java/fr/sncf/osrd/api/stdcm/STDCMEndpoint.java @@ -119,6 +119,7 @@ public Response act(Request req) throws OSRDError { simResult.baseSimulations.add(ScheduleMetadataExtractor.run( res.envelope(), res.trainPath(), + res.chunkPath(), makeTrainSchedule(res.envelope().getEndPos(), rollingStock, comfort, res.stopResults()), infra )); diff --git a/core/src/main/java/fr/sncf/osrd/api/utils/PathPropUtils.java b/core/src/main/java/fr/sncf/osrd/api/utils/PathPropUtils.java index e85eb3ce8c8..0a698dc09fa 100644 --- a/core/src/main/java/fr/sncf/osrd/api/utils/PathPropUtils.java +++ b/core/src/main/java/fr/sncf/osrd/api/utils/PathPropUtils.java @@ -1,7 +1,9 @@ package fr.sncf.osrd.api.utils; import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.buildPathPropertiesFrom; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; import static fr.sncf.osrd.sim_infra.api.TrackInfraKt.getTrackSectionFromNameOrThrow; +import static fr.sncf.osrd.sim_infra.impl.PathPropertiesImplKt.buildChunkPath; import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList; import static fr.sncf.osrd.utils.units.Distance.fromMeters; @@ -10,8 +12,10 @@ import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSDirectionalTrackRange; import fr.sncf.osrd.railjson.schema.schedule.RJSTrainPath; import fr.sncf.osrd.sim_infra.api.BlockInfra; +import fr.sncf.osrd.sim_infra.api.PathProperties; import fr.sncf.osrd.sim_infra.api.RawSignalingInfra; -import fr.sncf.osrd.sim_infra.api.*; +import fr.sncf.osrd.sim_infra.api.TrackChunk; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.utils.Direction; import fr.sncf.osrd.utils.graph.Pathfinding; import fr.sncf.osrd.utils.indexing.DirStaticIdxKt; @@ -58,6 +62,19 @@ public static PathProperties makePathProps(RawSignalingInfra rawInfra, BlockInfr /** Creates a `Path` instance from a list of block ranges */ public static PathProperties makePathProps(RawSignalingInfra rawInfra, BlockInfra blockInfra, List> blockRanges) { + var chunkPath = makeChunkPath(rawInfra, blockInfra, blockRanges); + return makePathProperties(rawInfra, chunkPath); + } + + /** Builds a PathProperties from an RJSTrainPath */ + public static PathProperties makePathProps(RawSignalingInfra rawInfra, RJSTrainPath rjsPath) { + var chunkPath = makeChunkPath(rawInfra, rjsPath); + return makePathProperties(rawInfra, chunkPath); + } + + /** Creates a ChunkPath from a list of block ranges */ + public static ChunkPath makeChunkPath(RawSignalingInfra rawInfra, BlockInfra blockInfra, + List> blockRanges) { assert !blockRanges.isEmpty(); long totalBlockPathLength = 0; var chunks = new MutableDirStaticIdxArrayList(); @@ -73,11 +90,11 @@ public static PathProperties makePathProps(RawSignalingInfra rawInfra, BlockInfr var lastRange = blockRanges.get(blockRanges.size() - 1); var lastBlockLength = blockInfra.getBlockLength(lastRange.edge()); var endOffset = totalBlockPathLength - lastBlockLength + lastRange.end(); - return buildPathPropertiesFrom(rawInfra, chunks, startOffset, endOffset); + return buildChunkPath(rawInfra, chunks, startOffset, endOffset); } - /** Builds a PathProperties from an RJSTrainPath */ - public static PathProperties makePathProps(RawSignalingInfra rawInfra, RJSTrainPath rjsPath) { + /** Builds a ChunkPath from an RJSTrainPath */ + public static ChunkPath makeChunkPath(RawSignalingInfra rawInfra, RJSTrainPath rjsPath) { var trackRanges = new ArrayList(); for (var routePath : rjsPath.routePath) { for (var trackRange : routePath.trackSections) { @@ -115,6 +132,6 @@ public static PathProperties makePathProps(RawSignalingInfra rawInfra, RJSTrainP .mapToDouble(r -> r.end - r.begin) .sum() ); - return buildPathPropertiesFrom(rawInfra, chunks, startOffset, endOffset); + return buildChunkPath(rawInfra, chunks, startOffset, endOffset); } } diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt b/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt index 2150251df0b..9014660ce9a 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt @@ -4,8 +4,11 @@ package fr.sncf.osrd.standalone_sim import fr.sncf.osrd.api.FullInfra -import fr.sncf.osrd.conflicts.* import fr.sncf.osrd.api.pathfinding.PathfindingResultConverter.chunksToRoutes +import fr.sncf.osrd.conflicts.IncrementalRequirementEnvelopeAdapter +import fr.sncf.osrd.conflicts.PathFragment +import fr.sncf.osrd.conflicts.SpacingRequirementAutomaton +import fr.sncf.osrd.conflicts.incrementalPathOf import fr.sncf.osrd.envelope.Envelope import fr.sncf.osrd.envelope.EnvelopePhysics import fr.sncf.osrd.envelope.EnvelopeTimeInterpolate @@ -13,6 +16,7 @@ import fr.sncf.osrd.envelope_sim_infra.EnvelopeTrainPath import fr.sncf.osrd.signaling.SignalingSimulator import fr.sncf.osrd.signaling.ZoneStatus import fr.sncf.osrd.sim_infra.api.* +import fr.sncf.osrd.sim_infra.impl.ChunkPath import fr.sncf.osrd.sim_infra.utils.BlockPathElement import fr.sncf.osrd.sim_infra.utils.recoverBlocks import fr.sncf.osrd.sim_infra.utils.toList @@ -25,18 +29,20 @@ import fr.sncf.osrd.train.RollingStock import fr.sncf.osrd.train.StandaloneTrainSchedule import fr.sncf.osrd.utils.CurveSimplification import fr.sncf.osrd.utils.KtToJavaConverter.toIntList -import fr.sncf.osrd.utils.indexing.* +import fr.sncf.osrd.utils.indexing.IdxMap +import fr.sncf.osrd.utils.indexing.MutableStaticIdxArrayList +import fr.sncf.osrd.utils.indexing.StaticIdxList +import fr.sncf.osrd.utils.indexing.mutableStaticIdxArrayListOf import fr.sncf.osrd.utils.toRouteIdList import fr.sncf.osrd.utils.units.Distance import fr.sncf.osrd.utils.units.MutableDistanceArray -import fr.sncf.osrd.utils.units.Offset import fr.sncf.osrd.utils.units.meters import mu.KotlinLogging -import java.lang.RuntimeException +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.set import kotlin.math.abs -import kotlin.math.absoluteValue import kotlin.math.max -import kotlin.math.min private val logger = KotlinLogging.logger {} @@ -62,17 +68,17 @@ fun recoverBlockPath( /** Use an already computed envelope to extract various metadata about a trip. */ -fun run(envelope: Envelope, trainPath: PathProperties, schedule: StandaloneTrainSchedule, +fun run(envelope: Envelope, trainPath: PathProperties, chunkPath: ChunkPath, schedule: StandaloneTrainSchedule, fullInfra: FullInfra): ResultTrain { assert(envelope.continuous) - val rawInfra = fullInfra.rawInfra as SimInfraAdapter; - val loadedSignalInfra = fullInfra.loadedSignalInfra; - val blockInfra = fullInfra.blockInfra; - val simulator = fullInfra.signalingSimulator; + val rawInfra = fullInfra.rawInfra as SimInfraAdapter + val loadedSignalInfra = fullInfra.loadedSignalInfra + val blockInfra = fullInfra.blockInfra + val simulator = fullInfra.signalingSimulator // get a new generation route path - val routePath = toRouteIdList(chunksToRoutes(rawInfra, toIntList(trainPath.chunks))) + val routePath = toRouteIdList(chunksToRoutes(rawInfra, toIntList(chunkPath.chunks))) // recover blocks from the route paths val detailedBlockPath = recoverBlockPath(simulator, fullInfra, routePath) @@ -102,7 +108,7 @@ fun run(envelope: Envelope, trainPath: PathProperties, schedule: StandaloneTrain } // Compute signal updates - val startOffset = trainPathBlockOffset(rawInfra, blockInfra, blockPath, trainPath) + val startOffset = trainPathBlockOffset(rawInfra, blockInfra, blockPath, chunkPath) var blockPathLength = 0.meters for (block in blockPath) blockPathLength += blockInfra.getBlockLength(block).distance @@ -122,7 +128,7 @@ fun run(envelope: Envelope, trainPath: PathProperties, schedule: StandaloneTrain var sightOffset = max(0.0, (pathSignal.pathOffset - rawInfra.getSignalSightDistance(physicalSignal)).meters) if (i > 0) { val previousSignalOffset = pathSignals[i - 1].pathOffset.meters - sightOffset = max(sightOffset, previousSignalOffset); + sightOffset = max(sightOffset, previousSignalOffset) } signalSightings.add(SignalSighting( rawInfra.getPhysicalSignalName(loadedSignalInfra.getPhysicalSignal(pathSignal.signal)), @@ -500,14 +506,14 @@ private fun pathSignalsInEnvelope( * thus of the envelope */ fun trainPathBlockOffset(infra: RawInfra, blockInfra: BlockInfra, - blockPath: MutableStaticIdxArrayList, trainPath: PathProperties): Distance { - val firstChunk = trainPath.chunks[0] + blockPath: MutableStaticIdxArrayList, chunkPath: ChunkPath): Distance { + val firstChunk = chunkPath.chunks[0] var prevChunksLength = 0.meters for (block in blockPath) { for (zonePath in blockInfra.getBlockPath(block)) { for (dirChunk in infra.getZonePathChunks(zonePath)) { if (dirChunk == firstChunk) - return prevChunksLength + trainPath.beginOffset + return prevChunksLength + chunkPath.beginOffset prevChunksLength += infra.getTrackChunkLength(dirChunk.value).distance } } diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/SignalProjection.kt b/core/src/main/java/fr/sncf/osrd/standalone_sim/SignalProjection.kt index ff4b1a0fff3..15c43d17d2e 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/SignalProjection.kt +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/SignalProjection.kt @@ -5,12 +5,8 @@ import fr.sncf.osrd.api.SignalProjectionEndpoint.SignalProjectionResult import fr.sncf.osrd.reporting.exceptions.OSRDError import fr.sncf.osrd.signaling.SignalingSimulator import fr.sncf.osrd.signaling.ZoneStatus -import fr.sncf.osrd.sim_infra.api.Block -import fr.sncf.osrd.sim_infra.api.BlockInfra -import fr.sncf.osrd.sim_infra.api.LoadedSignalInfra -import fr.sncf.osrd.sim_infra.api.LogicalSignalId -import fr.sncf.osrd.sim_infra.api.PathProperties -import fr.sncf.osrd.sim_infra.api.getZoneName +import fr.sncf.osrd.sim_infra.api.* +import fr.sncf.osrd.sim_infra.impl.ChunkPath import fr.sncf.osrd.sim_infra.utils.recoverBlocks import fr.sncf.osrd.sim_infra_adapter.SimInfraAdapter import fr.sncf.osrd.standalone_sim.result.ResultTrain.SignalSighting @@ -25,7 +21,7 @@ data class SignalAspectChangeEvent(val newAspect: String, val time: Long) fun project( fullInfra: FullInfra, - trainPath: PathProperties, + chunkPath: ChunkPath, routePathIds: List, signalSightings: List, zoneUpdates: List @@ -65,7 +61,7 @@ fun project( } // Compute signal updates - val startOffset = trainPathBlockOffset(fullInfra.rawInfra, fullInfra.blockInfra, blockPath, trainPath) + val startOffset = trainPathBlockOffset(fullInfra.rawInfra, fullInfra.blockInfra, blockPath, chunkPath) val pathSignals = pathSignals(startOffset, blockPath, blockInfra) val signalAspectChangeEvents = computeSignalAspectChangeEvents( diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java b/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java index 54f6fc64f36..5bf1267980f 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java @@ -1,8 +1,10 @@ package fr.sncf.osrd.standalone_sim; +import static fr.sncf.osrd.api.utils.PathPropUtils.makeChunkPath; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; + import fr.sncf.osrd.DriverBehaviour; import fr.sncf.osrd.api.FullInfra; -import fr.sncf.osrd.api.utils.PathPropUtils; import fr.sncf.osrd.envelope.Envelope; import fr.sncf.osrd.envelope_sim.EnvelopeSimContext; import fr.sncf.osrd.envelope_sim.EnvelopeSimPath; @@ -21,11 +23,8 @@ import fr.sncf.osrd.reporting.ErrorContext; import fr.sncf.osrd.reporting.exceptions.OSRDError; import fr.sncf.osrd.sim_infra.api.PathProperties; -import fr.sncf.osrd.standalone_sim.result.ElectrificationRange; -import fr.sncf.osrd.standalone_sim.result.PowerRestrictionRange; -import fr.sncf.osrd.standalone_sim.result.ResultEnvelopePoint; -import fr.sncf.osrd.standalone_sim.result.ResultTrain; -import fr.sncf.osrd.standalone_sim.result.StandaloneSimResult; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; +import fr.sncf.osrd.standalone_sim.result.*; import fr.sncf.osrd.train.RollingStock; import fr.sncf.osrd.train.ScheduledPoint; import fr.sncf.osrd.train.StandaloneTrainSchedule; @@ -44,6 +43,7 @@ public class StandaloneSim { public static StandaloneSimResult run( FullInfra infra, PathProperties trainPath, + ChunkPath chunkPath, EnvelopeSimPath envelopeSimPath, List schedules, double timeStep, @@ -94,6 +94,7 @@ public static StandaloneSimResult run( var simResultTrain = ScheduleMetadataExtractor.run( envelope, trainPath, + chunkPath, trainSchedule, infra ); @@ -107,6 +108,7 @@ public static StandaloneSimResult run( var simEcoResultTrain = ScheduleMetadataExtractor.run( ecoEnvelope, trainPath, + chunkPath, trainSchedule, infra); cacheEco.put(trainSchedule, simEcoResultTrain); @@ -132,7 +134,8 @@ public static StandaloneSimResult runFromRJS( double timeStep ) { // Parse trainPath - var trainPath = PathPropUtils.makePathProps(infra.rawInfra(), rjsTrainPath); + var chunkPath = makeChunkPath(infra.rawInfra(), rjsTrainPath); + var trainPath = makePathProperties(infra.rawInfra(), chunkPath); var envelopePath = EnvelopeTrainPath.from(infra.rawInfra(), trainPath, electricalProfileMap); // Parse train schedules @@ -145,6 +148,7 @@ public static StandaloneSimResult runFromRJS( return StandaloneSim.run( infra, trainPath, + chunkPath, envelopePath, trainSchedules, timeStep, diff --git a/core/src/main/java/fr/sncf/osrd/stdcm/STDCMResult.java b/core/src/main/java/fr/sncf/osrd/stdcm/STDCMResult.java index 8a3f68b321c..56487942bfa 100644 --- a/core/src/main/java/fr/sncf/osrd/stdcm/STDCMResult.java +++ b/core/src/main/java/fr/sncf/osrd/stdcm/STDCMResult.java @@ -3,6 +3,7 @@ import fr.sncf.osrd.envelope.Envelope; import fr.sncf.osrd.envelope_sim.PhysicsPath; import fr.sncf.osrd.sim_infra.api.PathProperties; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.train.TrainStop; import fr.sncf.osrd.utils.graph.Pathfinding; import java.util.List; @@ -14,6 +15,7 @@ public record STDCMResult( Pathfinding.Result blocks, Envelope envelope, PathProperties trainPath, + ChunkPath chunkPath, PhysicsPath physicsPath, double departureTime, List stopResults diff --git a/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMPostProcessing.java b/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMPostProcessing.java index fd3ef4f61e8..7a68eb9c6cb 100644 --- a/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMPostProcessing.java +++ b/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMPostProcessing.java @@ -1,11 +1,12 @@ package fr.sncf.osrd.stdcm.graph; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; import static fr.sncf.osrd.utils.units.Distance.toMeters; -import fr.sncf.osrd.sim_infra.api.RawSignalingInfra; -import fr.sncf.osrd.stdcm.STDCMResult; import fr.sncf.osrd.envelope_sim.allowances.utils.AllowanceValue; import fr.sncf.osrd.envelope_sim_infra.EnvelopeTrainPath; +import fr.sncf.osrd.sim_infra.api.RawSignalingInfra; +import fr.sncf.osrd.stdcm.STDCMResult; import fr.sncf.osrd.stdcm.preprocessing.interfaces.BlockAvailabilityInterface; import fr.sncf.osrd.train.RollingStock; import fr.sncf.osrd.train.TrainStop; @@ -40,7 +41,8 @@ STDCMResult makeResult( var ranges = makeEdgeRange(path); var blockRanges = makeBlockRanges(ranges); var blockWaypoints = makeBlockWaypoints(path); - var trainPath = STDCMUtils.makePathFromRanges(graph, ranges); + var chunkPath = STDCMUtils.makeChunkPathFromRanges(graph, ranges); + var trainPath = makePathProperties(infra, chunkPath); var physicsPath = EnvelopeTrainPath.from(infra, trainPath); var mergedEnvelopes = STDCMUtils.mergeEnvelopeRanges(ranges); var departureTime = computeDepartureTime(ranges, startTime); @@ -62,6 +64,7 @@ STDCMResult makeResult( new Pathfinding.Result<>(blockRanges, blockWaypoints), withAllowance, trainPath, + chunkPath, physicsPath, departureTime, stops diff --git a/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMUtils.java b/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMUtils.java index f566ad6ae14..fce396e7f11 100644 --- a/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMUtils.java +++ b/core/src/main/java/fr/sncf/osrd/stdcm/graph/STDCMUtils.java @@ -1,18 +1,16 @@ package fr.sncf.osrd.stdcm.graph; import static fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator.POSITION_EPSILON; -import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.buildPathPropertiesFrom; +import static fr.sncf.osrd.sim_infra.impl.PathPropertiesImplKt.buildChunkPath; import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList; import static fr.sncf.osrd.utils.units.Distance.toMeters; -import fr.sncf.osrd.api.utils.PathPropUtils; import fr.sncf.osrd.envelope.Envelope; import fr.sncf.osrd.envelope.part.EnvelopePart; -import fr.sncf.osrd.sim_infra.api.PathProperties; import fr.sncf.osrd.sim_infra.api.TrackChunk; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.utils.graph.Pathfinding; import fr.sncf.osrd.utils.indexing.MutableDirStaticIdxArrayList; -import fr.sncf.osrd.utils.units.Distance; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -77,7 +75,7 @@ static Long getStopOnBlock(STDCMGraph graph, int block, long startOffset, int wa } /** Create a TrainPath instance from a list of edge ranges */ - static PathProperties makePathFromRanges(STDCMGraph graph, List> ranges) { + static ChunkPath makeChunkPathFromRanges(STDCMGraph graph, List> ranges) { var blocks = ranges.stream() .map(range -> range.edge().block()) .distinct() @@ -91,6 +89,6 @@ static PathProperties makePathFromRanges(STDCMGraph graph, List toIntList(graph.blockInfra.getTrackChunksFromBlock(block)).stream()) .forEach(chunks::add); - return buildPathPropertiesFrom(graph.rawInfra, chunks, firstOffset, lastOffset); + return buildChunkPath(graph.rawInfra, chunks, firstOffset, lastOffset); } } diff --git a/core/src/test/java/fr/sncf/osrd/Helpers.java b/core/src/test/java/fr/sncf/osrd/Helpers.java index 6f8cc6fd378..8c032406b0c 100644 --- a/core/src/test/java/fr/sncf/osrd/Helpers.java +++ b/core/src/test/java/fr/sncf/osrd/Helpers.java @@ -1,6 +1,7 @@ package fr.sncf.osrd; import static fr.sncf.osrd.api.SignalingSimulatorKt.makeSignalingSimulator; +import static fr.sncf.osrd.sim_infra.impl.PathPropertiesImplKt.buildChunkPath; import static fr.sncf.osrd.sim_infra.utils.BlockRecoveryKt.recoverBlocks; import static fr.sncf.osrd.sim_infra.utils.BlockRecoveryKt.toList; import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList; @@ -15,13 +16,8 @@ import fr.sncf.osrd.railjson.schema.rollingstock.RJSRollingStock; import fr.sncf.osrd.reporting.exceptions.OSRDError; import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl; -import fr.sncf.osrd.sim_infra.api.PathProperties; -import fr.sncf.osrd.sim_infra.api.PathPropertiesKt; -import fr.sncf.osrd.sim_infra.api.RawSignalingInfra; -import fr.sncf.osrd.sim_infra.api.Route; -import fr.sncf.osrd.sim_infra.api.SignalingSystem; -import fr.sncf.osrd.sim_infra.api.TrackChunk; -import fr.sncf.osrd.sim_infra.api.TrackLocation; +import fr.sncf.osrd.sim_infra.api.*; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.sim_infra.impl.PathPropertiesImplKt; import fr.sncf.osrd.sim_infra.utils.BlockPathElement; import fr.sncf.osrd.utils.graph.Pathfinding; @@ -169,7 +165,7 @@ public static Pathfinding.EdgeLocation convertRouteLocation( } /** Creates a path from a list of route names and start/end locations */ - public static PathProperties pathFromRoutes( + public static ChunkPath chunkPathFromRoutes( RawSignalingInfra infra, List routeNames, TrackLocation start, @@ -183,6 +179,6 @@ public static PathProperties pathFromRoutes( } long startOffset = PathPropertiesImplKt.getOffsetOfTrackLocationOnChunksOrThrow(infra, start, chunks); long endOffset = PathPropertiesImplKt.getOffsetOfTrackLocationOnChunksOrThrow(infra, end, chunks); - return PathPropertiesKt.buildPathPropertiesFrom(infra, chunks, startOffset, endOffset); + return buildChunkPath(infra, chunks, startOffset, endOffset); } } diff --git a/core/src/test/java/fr/sncf/osrd/api/pathfinding/PathfindingResultConverterTest.java b/core/src/test/java/fr/sncf/osrd/api/pathfinding/PathfindingResultConverterTest.java index e79c68d1972..a6322375617 100644 --- a/core/src/test/java/fr/sncf/osrd/api/pathfinding/PathfindingResultConverterTest.java +++ b/core/src/test/java/fr/sncf/osrd/api/pathfinding/PathfindingResultConverterTest.java @@ -1,16 +1,19 @@ package fr.sncf.osrd.api.pathfinding; import static fr.sncf.osrd.Helpers.getBlocksOnRoutes; -import static fr.sncf.osrd.api.pathfinding.PathfindingResultConverter.*; +import static fr.sncf.osrd.api.pathfinding.PathfindingResultConverter.makePathWaypoint; import static fr.sncf.osrd.utils.KtToJavaConverter.toIntList; import static fr.sncf.osrd.utils.indexing.DirStaticIdxKt.toDirection; import static fr.sncf.osrd.utils.indexing.DirStaticIdxKt.toValue; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import fr.sncf.osrd.Helpers; import fr.sncf.osrd.api.FullInfra; -import fr.sncf.osrd.sim_infra.impl.PathPropertiesImpl; import fr.sncf.osrd.api.utils.PathPropUtils; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.utils.Direction; import fr.sncf.osrd.utils.graph.Pathfinding; import org.junit.jupiter.api.Test; @@ -33,13 +36,12 @@ public void testMakePathForward() { ranges.add(new Pathfinding.EdgeRange<>(block, 0, infra.blockInfra().getBlockLength(block))); } - var path = PathPropUtils.makePathProps(infra.rawInfra(), infra.blockInfra(), ranges); - var pathImpl = (PathPropertiesImpl) path; + var chunkPath = PathPropUtils.makeChunkPath(infra.rawInfra(), infra.blockInfra(), ranges); var expectedLength = 10_000_000 + 1_000_000; // length of route 1 + 2 - assertEquals(0, pathImpl.getBeginOffset()); - assertEquals(expectedLength, pathImpl.getEndOffset()); - checkBlocks(infra, pathImpl, Set.of("TA0", "TA6", "TC1"), Direction.INCREASING, expectedLength); + assertEquals(0, chunkPath.getBeginOffset()); + assertEquals(expectedLength, chunkPath.getEndOffset()); + checkBlocks(infra, chunkPath, Set.of("TA0", "TA6", "TC1"), Direction.INCREASING, expectedLength); } /** Convert block ranges into a path, with the chunks going backward and partial ranges */ @@ -61,13 +63,12 @@ public void testMakePathBackward() { new Pathfinding.EdgeRange<>(blocks.get(3), 0, infra.blockInfra().getBlockLength(blocks.get(3)) - 10_000) ); - var path = PathPropUtils.makePathProps(infra.rawInfra(), infra.blockInfra(), ranges); - var pathImpl = (PathPropertiesImpl) path; + var chunkPath = PathPropUtils.makeChunkPath(infra.rawInfra(), infra.blockInfra(), ranges); var expectedBlockLength = 1_050_000 + 10_000_000; // length of route 1 + 2 - assertEquals(10_000, pathImpl.getBeginOffset()); - assertEquals(expectedBlockLength - 10_000, pathImpl.getEndOffset()); - checkBlocks(infra, pathImpl, Set.of("TC0", "TD0", "TA6"), Direction.DECREASING, expectedBlockLength); + assertEquals(10_000, chunkPath.getBeginOffset()); + assertEquals(expectedBlockLength - 10_000, chunkPath.getEndOffset()); + checkBlocks(infra, chunkPath, Set.of("TC0", "TD0", "TA6"), Direction.DECREASING, expectedBlockLength); } /** Tests the waypoint result on a path that has one user-defined waypoint and one operational point */ @@ -138,7 +139,7 @@ public void testPathWaypointOnLoop() { private static void checkBlocks( FullInfra infra, - PathPropertiesImpl path, + ChunkPath path, Set allowedTracks, Direction direction, long length diff --git a/core/src/test/java/fr/sncf/osrd/standalone_sim/ConflictDetectionTest.java b/core/src/test/java/fr/sncf/osrd/standalone_sim/ConflictDetectionTest.java index a0ad0aa6543..2e4c21fb62a 100644 --- a/core/src/test/java/fr/sncf/osrd/standalone_sim/ConflictDetectionTest.java +++ b/core/src/test/java/fr/sncf/osrd/standalone_sim/ConflictDetectionTest.java @@ -1,10 +1,11 @@ package fr.sncf.osrd.standalone_sim; import static fr.sncf.osrd.Helpers.fullInfraFromRJS; -import static fr.sncf.osrd.Helpers.pathFromRoutes; +import static fr.sncf.osrd.Helpers.chunkPathFromRoutes; import static fr.sncf.osrd.api.ConflictDetectionEndpoint.ConflictDetectionResult.Conflict.ConflictType.ROUTING; import static fr.sncf.osrd.api.ConflictDetectionEndpoint.ConflictDetectionResult.Conflict.ConflictType.SPACING; import static fr.sncf.osrd.envelope_sim.TestMRSPBuilder.makeSimpleMRSP; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makeTrackLocation; import static fr.sncf.osrd.sim_infra.api.TrackInfraKt.getTrackSectionFromNameOrThrow; import static fr.sncf.osrd.utils.units.Distance.fromMeters; @@ -22,6 +23,7 @@ import fr.sncf.osrd.envelope_sim.pipelines.MaxEffortEnvelope; import fr.sncf.osrd.envelope_sim.pipelines.MaxSpeedEnvelope; import fr.sncf.osrd.sim_infra.api.PathProperties; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.standalone_sim.result.ResultTrain; import fr.sncf.osrd.train.RollingStock; import fr.sncf.osrd.train.StandaloneTrainSchedule; @@ -50,11 +52,12 @@ public void testRequirementProperties() throws Exception { var rawInfra = fullInfra.rawInfra(); // A path from the center track of south-west station to the center-top track of mid west station - var path = pathFromRoutes(rawInfra, List.of("rt.buffer_stop.1->DA0", "rt.DA0->DA5", "rt.DA5->DC5"), + var chunkPath = chunkPathFromRoutes(rawInfra, List.of("rt.buffer_stop.1->DA0", "rt.DA0->DA5", "rt.DA5->DC5"), makeTrackLocation(getTrackSectionFromNameOrThrow("TA1", rawInfra), fromMeters(146.6269028126681)), makeTrackLocation(getTrackSectionFromNameOrThrow("TC1", rawInfra), fromMeters(444.738508351214))); + var pathProps = makePathProperties(rawInfra, chunkPath); - var simResult = simpleSim(fullInfra, path, 0, Double.POSITIVE_INFINITY); + var simResult = simpleSim(fullInfra, pathProps, chunkPath, 0, Double.POSITIVE_INFINITY); var spacingRequirements = simResult.train.spacingRequirements; // ensure spacing requirements span the entire trip duration @@ -102,13 +105,15 @@ public void headToHeadRoutingConflict() throws Exception { // these paths are fairly distant from each other, but require passing a signal which protects // a very long route. As the routes for pathA and pathB are incompatible with each other, // a conflict should occur. - var pathA = pathFromRoutes(rawInfra, List.of("rt.buffer_stop.0->DA2", "rt.DA2->DA5"), + var chunkPathA = chunkPathFromRoutes(rawInfra, List.of("rt.buffer_stop.0->DA2", "rt.DA2->DA5"), makeTrackLocation(ta0, fromMeters(1795)), makeTrackLocation(ta0, fromMeters(1825))); - var pathB = pathFromRoutes(rawInfra, List.of("rt.DD0->DC0", "rt.DC0->DA3"), + var pathPropsA = makePathProperties(rawInfra, chunkPathA); + var chunkPathB = chunkPathFromRoutes(rawInfra, List.of("rt.DD0->DC0", "rt.DC0->DA3"), makeTrackLocation(tc0, fromMeters(205)), makeTrackLocation(tc0, fromMeters(175))); + var pathPropsB = makePathProperties(rawInfra, chunkPathB); - var simResultA = simpleSim(fullInfra, pathA, 0, Double.POSITIVE_INFINITY); - var simResultB = simpleSim(fullInfra, pathB, 0, Double.POSITIVE_INFINITY); + var simResultA = simpleSim(fullInfra, pathPropsA, chunkPathA, 0, Double.POSITIVE_INFINITY); + var simResultB = simpleSim(fullInfra, pathPropsB, chunkPathB, 0, Double.POSITIVE_INFINITY); // if both trains runs at the same time, there should be a conflict { @@ -147,14 +152,18 @@ public void divergenceRoutingConflict() throws Exception { var td0 = getTrackSectionFromNameOrThrow("TD0", rawInfra); // path that stops in station - var pathA = pathFromRoutes(rawInfra, List.of("rt.DA2->DA5", "rt.DA5->DC4"), + var chunkPathA = chunkPathFromRoutes(rawInfra, List.of("rt.DA2->DA5", "rt.DA5->DC4"), makeTrackLocation(ta6, 0), makeTrackLocation(tc0, fromMeters(600))); + var pathPropsA = makePathProperties(rawInfra, chunkPathA); // path that continues on - var pathB = pathFromRoutes(rawInfra, List.of("rt.DA2->DA5", "rt.DA5->DC5", "rt.DC5->DD2"), + var chunkPathB = chunkPathFromRoutes(rawInfra, List.of("rt.DA2->DA5", "rt.DA5->DC5", "rt.DC5->DD2"), makeTrackLocation(ta6, 0), makeTrackLocation(td0, fromMeters(8500))); + var pathPropsB = makePathProperties(rawInfra, chunkPathB); - var simResultA = simpleSim(fullInfra, pathA, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); - var simResultB = simpleSim(fullInfra, pathB, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + var simResultA = simpleSim(fullInfra, pathPropsA, chunkPathA, Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY); + var simResultB = simpleSim(fullInfra, pathPropsB, chunkPathB, Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY); record ConflictStatus(boolean spacing, boolean routing) {} @@ -212,7 +221,8 @@ private static TrainRequirements convertRequirements(long trainId, double offset return new TrainRequirements(trainId, spacingRequirements, routingRequirements); } - private static SimResult simpleSim(FullInfra fullInfra, PathProperties path, double initialSpeed, double maxSpeed) { + private static SimResult simpleSim(FullInfra fullInfra, PathProperties path, ChunkPath chunkPath, + double initialSpeed, double maxSpeed) { var testRollingStock = TestTrains.REALISTIC_FAST_TRAIN; if (maxSpeed > testRollingStock.maxSpeed) maxSpeed = testRollingStock.maxSpeed; @@ -232,7 +242,7 @@ private static SimResult simpleSim(FullInfra fullInfra, PathProperties path, dou testRollingStock, initialSpeed, List.of(), List.of(), List.of(), "test", RollingStock.Comfort.STANDARD, null, null ); - return new SimResult(ScheduleMetadataExtractor.run(envelope, path, schedule, fullInfra), envelope); + return new SimResult(ScheduleMetadataExtractor.run(envelope, path, chunkPath, schedule, fullInfra), envelope); } private static double getLastZoneReleaseTime(ResultTrain.RoutingRequirement requirement) { diff --git a/core/src/test/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorTests.java b/core/src/test/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorTests.java index 960ff019acd..6059178d710 100644 --- a/core/src/test/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorTests.java +++ b/core/src/test/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorTests.java @@ -2,6 +2,7 @@ import static fr.sncf.osrd.Helpers.fullInfraFromRJS; import static fr.sncf.osrd.envelope_sim.MaxEffortEnvelopeBuilder.makeSimpleMaxEffortEnvelope; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makeTrackLocation; import fr.sncf.osrd.Helpers; @@ -10,6 +11,7 @@ import fr.sncf.osrd.envelope_sim.SimpleContextBuilder; import fr.sncf.osrd.sim_infra.api.PathProperties; import fr.sncf.osrd.sim_infra.api.TrackInfraKt; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.train.RollingStock; import fr.sncf.osrd.train.StandaloneTrainSchedule; import fr.sncf.osrd.train.TestTrains; @@ -26,7 +28,7 @@ public void tinyInfraTests() throws Exception { var infra = fullInfraFromRJS(rjsInfra); var barA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.bar_a", infra.rawInfra()); var fooA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.foo_a", infra.rawInfra()); - var path = Helpers.pathFromRoutes( + var chunkPath = Helpers.chunkPathFromRoutes( infra.rawInfra(), List.of( "rt.buffer_stop_c->tde.track-bar", @@ -36,13 +38,14 @@ public void tinyInfraTests() throws Exception { makeTrackLocation(barA, 200), makeTrackLocation(fooA, 0) ); - var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(path.getLength()), 0); + var pathProps = makePathProperties(infra.rawInfra(), chunkPath); + var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(pathProps.getLength()), 0); var testRollingStock = TestTrains.REALISTIC_FAST_TRAIN; var envelope = makeSimpleMaxEffortEnvelope( testContext, testRollingStock.maxSpeed, new double[]{} ); - makeRouteOccupancy(infra, path, testRollingStock, envelope); + makeRouteOccupancy(infra, pathProps, chunkPath, testRollingStock, envelope); } @Test @@ -51,7 +54,7 @@ public void tinyInfraEndsInMiddleRoutesTests() throws Exception { var infra = fullInfraFromRJS(rjsInfra); var barA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.bar_a", infra.rawInfra()); var fooA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.foo_a", infra.rawInfra()); - var path = Helpers.pathFromRoutes( + var chunkPath = Helpers.chunkPathFromRoutes( infra.rawInfra(), List.of( "rt.buffer_stop_c->tde.track-bar", @@ -61,14 +64,15 @@ public void tinyInfraEndsInMiddleRoutesTests() throws Exception { makeTrackLocation(barA, 100), makeTrackLocation(fooA, 100) ); - var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(path.getLength()), 0); + var pathProps = makePathProperties(infra.rawInfra(), chunkPath); + var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(pathProps.getLength()), 0); var testRollingStock = TestTrains.REALISTIC_FAST_TRAIN; var envelope = makeSimpleMaxEffortEnvelope( testContext, testRollingStock.maxSpeed, new double[]{} ); // We only check that no assertion is thrown in the validation - makeRouteOccupancy(infra, path, testRollingStock, envelope); + makeRouteOccupancy(infra, pathProps, chunkPath, testRollingStock, envelope); } @Test @@ -77,7 +81,7 @@ public void veryLongTrainTests() throws Exception { var infra = fullInfraFromRJS(rjsInfra); var barA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.bar_a", infra.rawInfra()); var fooA = TrackInfraKt.getTrackSectionFromNameOrThrow("ne.micro.foo_a", infra.rawInfra()); - var path = Helpers.pathFromRoutes( + var chunkPath = Helpers.chunkPathFromRoutes( infra.rawInfra(), List.of( "rt.buffer_stop_c->tde.track-bar", @@ -87,14 +91,15 @@ public void veryLongTrainTests() throws Exception { makeTrackLocation(barA, 100), makeTrackLocation(fooA, 100) ); - var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(path.getLength()), 0); + var pathProps = makePathProperties(infra.rawInfra(), chunkPath); + var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(pathProps.getLength()), 0); var testRollingStock = TestTrains.VERY_LONG_FAST_TRAIN; var envelope = makeSimpleMaxEffortEnvelope( testContext, testRollingStock.maxSpeed, new double[]{} ); // We only check that no assertion is thrown in the validation - makeRouteOccupancy(infra, path, testRollingStock, envelope); + makeRouteOccupancy(infra, pathProps, chunkPath, testRollingStock, envelope); } @Test @@ -106,20 +111,21 @@ public void oneLineInfraTests() throws Exception { for (int i = 0; i < 9; i++) routes.add(String.format("rt.detector.%d->detector.%d", i, i + 1)); routes.add("rt.detector.9->buffer_stop.1"); - var path = Helpers.pathFromRoutes( + var chunkPath = Helpers.chunkPathFromRoutes( infra.rawInfra(), routes, makeTrackLocation(TrackInfraKt.getTrackSectionFromNameOrThrow("track.0", infra.rawInfra()), 0), makeTrackLocation(TrackInfraKt.getTrackSectionFromNameOrThrow("track.9", infra.rawInfra()), 0) ); - var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(path.getLength()), 0); + var pathProps = makePathProperties(infra.rawInfra(), chunkPath); + var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(pathProps.getLength()), 0); var testRollingStock = TestTrains.REALISTIC_FAST_TRAIN; var envelope = makeSimpleMaxEffortEnvelope( testContext, testRollingStock.maxSpeed, new double[]{} ); // We only check that no assertion is thrown in the validation - makeRouteOccupancy(infra, path, testRollingStock, envelope); + makeRouteOccupancy(infra, pathProps, chunkPath, testRollingStock, envelope); } @Test @@ -127,31 +133,29 @@ public void veryShortPathTests() throws Exception { var rjsInfra = Helpers.getExampleInfra("one_line/infra.json"); var infra = fullInfraFromRJS(rjsInfra); var routes = List.of("rt.buffer_stop.0->detector.0"); - var path = Helpers.pathFromRoutes( + var chunkPath = Helpers.chunkPathFromRoutes( infra.rawInfra(), routes, makeTrackLocation(TrackInfraKt.getTrackSectionFromNameOrThrow("track.0", infra.rawInfra()), 0), makeTrackLocation(TrackInfraKt.getTrackSectionFromNameOrThrow("track.0", infra.rawInfra()), 10) ); - var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(path.getLength()), 0); + var pathProps = makePathProperties(infra.rawInfra(), chunkPath); + var testContext = SimpleContextBuilder.makeSimpleContext(Distance.toMeters(pathProps.getLength()), 0); var testRollingStock = TestTrains.REALISTIC_FAST_TRAIN; var envelope = makeSimpleMaxEffortEnvelope( testContext, testRollingStock.maxSpeed, new double[]{} ); // We only check that no assertion is thrown in the validation - makeRouteOccupancy(infra, path, testRollingStock, envelope); + makeRouteOccupancy(infra, pathProps, chunkPath, testRollingStock, envelope); } - - private static void makeRouteOccupancy( - FullInfra fullInfra, - PathProperties path, RollingStock testRollingStock, Envelope envelope - ) { + private static void makeRouteOccupancy(FullInfra fullInfra, PathProperties path, ChunkPath chunkPath, + RollingStock testRollingStock, Envelope envelope) { var schedule = new StandaloneTrainSchedule( testRollingStock, 0, new ArrayList<>(), List.of(), List.of(), "test", RollingStock.Comfort.STANDARD, null, null ); - ScheduleMetadataExtractor.run(envelope, path, schedule, fullInfra); + ScheduleMetadataExtractor.run(envelope, path, chunkPath, schedule, fullInfra); } } diff --git a/core/src/test/java/fr/sncf/osrd/stdcm/STDCMHelpers.java b/core/src/test/java/fr/sncf/osrd/stdcm/STDCMHelpers.java index c87c19a75c3..521c757fd2f 100644 --- a/core/src/test/java/fr/sncf/osrd/stdcm/STDCMHelpers.java +++ b/core/src/test/java/fr/sncf/osrd/stdcm/STDCMHelpers.java @@ -1,16 +1,17 @@ package fr.sncf.osrd.stdcm; +import static fr.sncf.osrd.sim_infra.api.PathPropertiesKt.makePathProperties; import static fr.sncf.osrd.train.TestTrains.REALISTIC_FAST_TRAIN; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import fr.sncf.osrd.DriverBehaviour; import fr.sncf.osrd.api.FullInfra; -import fr.sncf.osrd.api.utils.PathPropUtils; import fr.sncf.osrd.api.stdcm.STDCMRequest; -import fr.sncf.osrd.DriverBehaviour; +import fr.sncf.osrd.api.utils.PathPropUtils; import fr.sncf.osrd.envelope_sim_infra.EnvelopeTrainPath; -import fr.sncf.osrd.sim_infra.api.PathProperties; +import fr.sncf.osrd.sim_infra.impl.ChunkPath; import fr.sncf.osrd.standalone_sim.EnvelopeStopWrapper; import fr.sncf.osrd.standalone_sim.StandaloneSim; import fr.sncf.osrd.stdcm.graph.STDCMSimulations; @@ -33,10 +34,12 @@ public static Multimap makeOccupancyFromPath( Set> endLocations, double departureTime ) { - var trainPath = makeTrainPath(infra, startLocations, endLocations); + var chunkPath = makeChunkPath(infra, startLocations, endLocations); + var trainPath = makePathProperties(infra.rawInfra(), chunkPath); var result = StandaloneSim.run( infra, trainPath, + chunkPath, EnvelopeTrainPath.from(infra.rawInfra(), trainPath), List.of(new StandaloneTrainSchedule( REALISTIC_FAST_TRAIN, @@ -71,8 +74,8 @@ public static Multimap makeOccupancyFromPath( ); } - /** Creates a train path object from start and end locations */ - private static PathProperties makeTrainPath( + /** Creates a chunk path object from start and end locations */ + private static ChunkPath makeChunkPath( FullInfra infra, Set> startLocations, Set> endLocations @@ -80,7 +83,7 @@ private static PathProperties makeTrainPath( var path = new Pathfinding<>(new GraphAdapter(infra.blockInfra(), infra.rawInfra())) .setEdgeToLength(block -> infra.blockInfra().getBlockLength(block)) .runPathfinding(List.of(startLocations, endLocations)); - return PathPropUtils.makePathProps(infra.rawInfra(), infra.blockInfra(), path.ranges()); + return PathPropUtils.makeChunkPath(infra.rawInfra(), infra.blockInfra(), path.ranges()); } /** Returns how long the longest occupancy segment lasts, which is the minimum delay we need to add diff --git a/core/src/test/kotlin/fr/sncf/osrd/sim_infra_adapter/PathPropertiesTests.kt b/core/src/test/kotlin/fr/sncf/osrd/sim_infra_adapter/PathPropertiesTests.kt index cb39a4e44ab..28a22f45dc3 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/sim_infra_adapter/PathPropertiesTests.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/sim_infra_adapter/PathPropertiesTests.kt @@ -7,25 +7,13 @@ import fr.sncf.osrd.geom.Point import fr.sncf.osrd.railjson.schema.common.graph.ApplicableDirection import fr.sncf.osrd.railjson.schema.geom.RJSLineString import fr.sncf.osrd.railjson.schema.infra.RJSOperationalPoint -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSApplicableDirectionsTrackRange -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSCatenary -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSCurve -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSLoadingGaugeLimit -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSOperationalPointPart -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSSlope -import fr.sncf.osrd.railjson.schema.infra.trackranges.RJSSpeedSection +import fr.sncf.osrd.railjson.schema.infra.trackranges.* import fr.sncf.osrd.railjson.schema.rollingstock.RJSLoadingGaugeType -import fr.sncf.osrd.sim_infra.api.LocationInfra -import fr.sncf.osrd.sim_infra.api.PathProperties -import fr.sncf.osrd.sim_infra.api.TrackChunk -import fr.sncf.osrd.sim_infra.api.buildPathPropertiesFrom -import fr.sncf.osrd.sim_infra.api.dirIter import fr.sncf.osrd.train.TestTrains.MAX_SPEED import fr.sncf.osrd.utils.Direction import fr.sncf.osrd.utils.DistanceRangeMap import fr.sncf.osrd.utils.indexing.StaticIdx -import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf -import fr.sncf.osrd.utils.units.Distance +import fr.sncf.osrd.utils.pathFromTracks import fr.sncf.osrd.utils.units.Speed.Companion.fromMetersPerSecond import fr.sncf.osrd.utils.units.meters import org.assertj.core.api.Assertions.assertThat @@ -359,16 +347,4 @@ class PathPropertiesTests { } return LineString.make(xs.toDoubleArray(), ys.toDoubleArray()) } - - /** Build a path from track ids */ - private fun pathFromTracks(infra: LocationInfra, trackIds: List, - dir: Direction, start: Distance, end: Distance - ): PathProperties { - val chunkList = mutableDirStaticIdxArrayListOf() - trackIds - .map { id -> infra.getTrackSectionFromName(id)!! } - .flatMap { track -> infra.getTrackSectionChunks(track).dirIter(dir) } - .forEach { dirChunk -> chunkList.add(dirChunk) } - return buildPathPropertiesFrom(infra, chunkList, start, end) - } }