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

core: reduce the amount of logs for repeated events #9755

Merged
merged 1 commit into from
Nov 20, 2024
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 @@ -139,18 +139,24 @@ private fun convertWorkSchedule(
timeToAdd: TimeDelta = 0.seconds,
): Collection<ResultTrain.SpacingRequirement> {
val res = mutableListOf<ResultTrain.SpacingRequirement>()

// Used to log invalid data (but only once per request)
var missingTracks = mutableSetOf<String>()
var tracksNotCoveredByRoutes = mutableSetOf<String>()

for (range in workSchedule.trackRanges) {
val track = rawInfra.getTrackSectionFromName(range.trackSection) ?: continue
val track = rawInfra.getTrackSectionFromName(range.trackSection)
if (track == null) {
missingTracks.add(range.trackSection)
continue
}
for (chunk in rawInfra.getTrackSectionChunks(track)) {
val chunkStartOffset = rawInfra.getTrackChunkOffset(chunk)
val chunkEndOffset = chunkStartOffset + rawInfra.getTrackChunkLength(chunk).distance
if (chunkStartOffset > range.end || chunkEndOffset < range.begin) continue
val zone = rawInfra.getTrackChunkZone(chunk)
if (zone == null) {
requirementsParserLogger.info(
"Skipping part of work schedule [${workSchedule.startTime}; ${workSchedule.endTime}] " +
"because it is on a track not fully covered by routes: $track",
)
tracksNotCoveredByRoutes.add(range.trackSection)
continue
}
res.add(
Expand All @@ -163,5 +169,19 @@ private fun convertWorkSchedule(
)
}
}
if (missingTracks.isNotEmpty()) {
val msg =
"${missingTracks.size} track sections referenced in work schedules were not found on the infra: " +
missingTracks.take(3).joinToString(", ") +
(if (missingTracks.size > 3) ", ..." else "")
requirementsParserLogger.warn(msg)
}
if (tracksNotCoveredByRoutes.isNotEmpty()) {
val msg =
"${tracksNotCoveredByRoutes.size} track sections were not fully covered by routes (ignoring some work schedules): " +
tracksNotCoveredByRoutes.take(3).joinToString(", ") +
(if (tracksNotCoveredByRoutes.size > 3) ", ..." else "")
requirementsParserLogger.warn(msg)
}
return res
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class STDCMPathfinding(
assert(stops.isNotEmpty())
starts = getStartNodes(stops, listOf(constraints))
val path = findPathImpl()
graph.stdcmSimulations.logWarnings()
if (path == null) {
logger.info("Failed to find a path")
return null
Expand Down
106 changes: 64 additions & 42 deletions core/src/main/kotlin/fr/sncf/osrd/stdcm/graph/STDCMSimulations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class STDCMSimulations {
private var simulatedEnvelopes: HashMap<BlockSimulationParameters, SoftReference<Envelope>?> =
HashMap()

// Used to log how many simulations failed (to log it once at the end of the processing)
private var nFailedSimulation = 0

/**
* Returns the corresponding envelope if the block's envelope has already been computed in
* simulatedEnvelopes, otherwise computes the matching envelope and adds it to the STDCMGraph.
Expand Down Expand Up @@ -66,6 +69,67 @@ class STDCMSimulations {
simulatedEnvelopes[blockParams] = SoftReference(simulatedEnvelope)
return simulatedEnvelope
}

/**
* Returns an envelope matching the given block. The envelope time starts when the train enters
* the block. stopPosition specifies the position at which the train should stop, may be null
* (no stop).
*
* Note: there are some approximations made here as we only "see" the tracks on the given
* blocks. We are missing slopes and speed limits from earlier in the path.
*/
fun simulateBlock(
rawInfra: RawSignalingInfra,
infraExplorer: InfraExplorer,
initialSpeed: Double,
start: Offset<Block>,
rollingStock: RollingStock,
comfort: Comfort?,
timeStep: Double,
stopPosition: Offset<Block>?,
trainTag: String?
): Envelope? {
if (stopPosition != null && stopPosition == Offset<Block>(0.meters))
return makeSinglePointEnvelope(0.0)
val blockLength = infraExplorer.getCurrentBlockLength()
if (start >= blockLength) return makeSinglePointEnvelope(initialSpeed)
var stops = doubleArrayOf()
var simLength = blockLength.distance - start.distance
if (stopPosition != null) {
stops = doubleArrayOf(stopPosition.distance.meters)
simLength = Distance.min(simLength, stopPosition.distance)
}
val path = infraExplorer.getCurrentEdgePathProperties(start, simLength)
val envelopePath = EnvelopeTrainPath.from(rawInfra, path)
val context = build(rollingStock, envelopePath, timeStep, comfort)
val mrsp = computeMRSP(path, rollingStock, false, trainTag)
return try {
val maxSpeedEnvelope = MaxSpeedEnvelope.from(context, stops, mrsp)
MaxEffortEnvelope.from(context, initialSpeed, maxSpeedEnvelope)
} catch (e: OSRDError) {
// The train can't reach its destination, for example because of high slopes
if (nFailedSimulation == 0) {
// We only log the first one (to get an actual error message but not spam any
// further)
logger.info(
"First failure of an STDCM Simulation during the search (ignoring this possible path): ${e.message}"
)
}
nFailedSimulation++
null
}
}

/**
* Log any relevant warnings about what happened during the processing, to be called once at the
* end. Aggregates events into fewer log entries.
*/
fun logWarnings() {
if (nFailedSimulation > 0)
logger.info(
"A total of $nFailedSimulation STDCM Simulations failed during the search (usually because of lack of traction)"
)
}
}

/** Create an EnvelopeSimContext instance from the blocks and extra parameters. */
Expand All @@ -83,48 +147,6 @@ fun makeSimContext(
return build(rollingStock, envelopePath, timeStep, comfort)
}

/**
* Returns an envelope matching the given block. The envelope time starts when the train enters the
* block. stopPosition specifies the position at which the train should stop, may be null (no stop).
*
* Note: there are some approximations made here as we only "see" the tracks on the given blocks. We
* are missing slopes and speed limits from earlier in the path.
*/
fun simulateBlock(
rawInfra: RawSignalingInfra,
infraExplorer: InfraExplorer,
initialSpeed: Double,
start: Offset<Block>,
rollingStock: RollingStock,
comfort: Comfort?,
timeStep: Double,
stopPosition: Offset<Block>?,
trainTag: String?
): Envelope? {
if (stopPosition != null && stopPosition == Offset<Block>(0.meters))
return makeSinglePointEnvelope(0.0)
val blockLength = infraExplorer.getCurrentBlockLength()
if (start >= blockLength) return makeSinglePointEnvelope(initialSpeed)
var stops = doubleArrayOf()
var simLength = blockLength.distance - start.distance
if (stopPosition != null) {
stops = doubleArrayOf(stopPosition.distance.meters)
simLength = Distance.min(simLength, stopPosition.distance)
}
val path = infraExplorer.getCurrentEdgePathProperties(start, simLength)
val envelopePath = EnvelopeTrainPath.from(rawInfra, path)
val context = build(rollingStock, envelopePath, timeStep, comfort)
val mrsp = computeMRSP(path, rollingStock, false, trainTag)
return try {
val maxSpeedEnvelope = MaxSpeedEnvelope.from(context, stops, mrsp)
MaxEffortEnvelope.from(context, initialSpeed, maxSpeedEnvelope)
} catch (e: OSRDError) {
// The train can't reach its destination, for example because of high slopes
logger.info("STDCM Simulation failed (ignoring this possible path): ${e.message}")
null
}
}

/** Make an envelope with a single point of the given speed */
private fun makeSinglePointEnvelope(speed: Double): Envelope {
return Envelope.make(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMultimap
import fr.sncf.osrd.graph.Pathfinding.EdgeLocation
import fr.sncf.osrd.railjson.schema.rollingstock.Comfort
import fr.sncf.osrd.sim_infra.api.Block
import fr.sncf.osrd.stdcm.graph.simulateBlock
import fr.sncf.osrd.stdcm.preprocessing.OccupancySegment
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.utils.DummyInfra
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMultimap
import fr.sncf.osrd.graph.Pathfinding.EdgeLocation
import fr.sncf.osrd.railjson.schema.rollingstock.Comfort
import fr.sncf.osrd.sim_infra.api.Block
import fr.sncf.osrd.stdcm.graph.simulateBlock
import fr.sncf.osrd.stdcm.preprocessing.OccupancySegment
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.utils.DummyInfra
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMultimap
import fr.sncf.osrd.graph.Pathfinding.EdgeLocation
import fr.sncf.osrd.railjson.schema.rollingstock.Comfort
import fr.sncf.osrd.sim_infra.api.Block
import fr.sncf.osrd.stdcm.graph.simulateBlock
import fr.sncf.osrd.stdcm.preprocessing.OccupancySegment
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.utils.DummyInfra
Expand Down
34 changes: 33 additions & 1 deletion core/src/test/kotlin/fr/sncf/osrd/stdcm/STDCMHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMultimap
import fr.sncf.osrd.DriverBehaviour
import fr.sncf.osrd.api.FullInfra
import fr.sncf.osrd.api.pathfinding.makeChunkPath
import fr.sncf.osrd.envelope.Envelope
import fr.sncf.osrd.envelope_sim_infra.EnvelopeTrainPath
import fr.sncf.osrd.graph.GraphAdapter
import fr.sncf.osrd.graph.Pathfinding
Expand All @@ -15,10 +16,11 @@ 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.standalone_sim.result.ResultTrain.SpacingRequirement
import fr.sncf.osrd.stdcm.graph.simulateBlock
import fr.sncf.osrd.stdcm.graph.STDCMSimulations
import fr.sncf.osrd.stdcm.infra_exploration.InfraExplorer
import fr.sncf.osrd.stdcm.infra_exploration.initInfraExplorer
import fr.sncf.osrd.stdcm.preprocessing.OccupancySegment
import fr.sncf.osrd.train.RollingStock
import fr.sncf.osrd.train.StandaloneTrainSchedule
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.train.TrainStop
Expand Down Expand Up @@ -118,6 +120,36 @@ fun getBlocksRunTime(infra: FullInfra, blocks: List<BlockId>): Double {
}
return time
}

/** Helper function to call `simulateBlock` without instantiating an `STDCMSimulations` */
fun simulateBlock(
rawInfra: RawSignalingInfra,
infraExplorer: InfraExplorer,
initialSpeed: Double,
start: Offset<Block>,
rollingStock: RollingStock,
comfort: Comfort?,
timeStep: Double,
stopPosition: Offset<Block>?,
trainTag: String?
): Envelope? {
val sim = STDCMSimulations()
val res =
sim.simulateBlock(
rawInfra,
infraExplorer,
initialSpeed,
start,
rollingStock,
comfort,
timeStep,
stopPosition,
trainTag
)
sim.logWarnings()
return res
}

/**
* Checks that the result doesn't cross an occupied section, with a certain tolerance for binary
* search inaccuracies
Expand Down
Loading