Skip to content

Commit

Permalink
core: merge request simulation schedule items with same path offset
Browse files Browse the repository at this point in the history
  • Loading branch information
Erashin committed May 21, 2024
1 parent 8bcaee1 commit 06e8c38
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fr.sncf.osrd.api.api_v2

import fr.sncf.osrd.api.api_v2.standalone_sim.SimulationScheduleItem
import fr.sncf.osrd.utils.units.TimeDelta
import java.lang.Long.min

/**
* Merge consecutive schedule items which are at the same location, i.e. have the same path offset.
* Merge rules for a group of schedule items are as follows:
* - arrival is the minimum value of all the concerned schedule items, null if they are all null.
* - stopFor is the sum of all the stopFor values of all the schedule items, null if they are all
* null.
* - onStopSignal is true if at least one schedule item's onStopSignal is true, false otherwise.
*/
fun parseRawSimulationScheduleItems(
rawSimulationScheduleItems: List<SimulationScheduleItem>
): List<SimulationScheduleItem> {
val simulationScheduleItems = mutableListOf<SimulationScheduleItem>()
var i = 0
while (i < rawSimulationScheduleItems.size) {
val pathOffset = rawSimulationScheduleItems[i].pathOffset
var arrival = rawSimulationScheduleItems[i].arrival
var stopFor = rawSimulationScheduleItems[i].stopFor
var onStopSignal = rawSimulationScheduleItems[i].onStopSignal
while (
i < rawSimulationScheduleItems.size - 1 &&
rawSimulationScheduleItems[i + 1].pathOffset == pathOffset
) {
val nextArrival = rawSimulationScheduleItems[i + 1].arrival
arrival =
when {
nextArrival != null && arrival != null ->
TimeDelta(min(arrival.milliseconds, nextArrival.milliseconds))
nextArrival != null -> nextArrival
else -> arrival
}
val nextStopFor = rawSimulationScheduleItems[i + 1].stopFor
stopFor =
when {
nextStopFor != null && stopFor != null -> stopFor + nextStopFor
nextStopFor != null -> nextStopFor
else -> stopFor
}
onStopSignal = onStopSignal || rawSimulationScheduleItems[i + 1].onStopSignal
i++
}
simulationScheduleItems.add(
SimulationScheduleItem(pathOffset, arrival, stopFor, onStopSignal)
)
i++
}
return simulationScheduleItems
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.sncf.osrd.api.api_v2.standalone_sim
import fr.sncf.osrd.api.ElectricalProfileSetManager
import fr.sncf.osrd.api.ExceptionHandler
import fr.sncf.osrd.api.InfraManager
import fr.sncf.osrd.api.api_v2.parseRawSimulationScheduleItems
import fr.sncf.osrd.api.pathfinding.makeChunkPath
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl
Expand Down Expand Up @@ -65,7 +66,7 @@ class SimulationEndpoint(
parsePowerRestrictions(request.powerRestrictions),
request.options.useElectricalProfiles,
2.0,
request.schedule,
parseRawSimulationScheduleItems(request.schedule),
request.initialSpeed,
request.margins,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import fr.sncf.osrd.api.InfraManager
import fr.sncf.osrd.api.api_v2.*
import fr.sncf.osrd.api.api_v2.pathfinding.findWaypointBlocks
import fr.sncf.osrd.api.api_v2.pathfinding.runPathfindingPostProcessing
import fr.sncf.osrd.api.api_v2.standalone_sim.MarginValue
import fr.sncf.osrd.api.api_v2.standalone_sim.SimulationScheduleItem
import fr.sncf.osrd.api.api_v2.standalone_sim.SimulationSuccess
import fr.sncf.osrd.api.api_v2.standalone_sim.parseRawRollingStock
import fr.sncf.osrd.api.api_v2.standalone_sim.*
import fr.sncf.osrd.conflicts.*
import fr.sncf.osrd.envelope_sim.allowances.utils.AllowanceValue
import fr.sncf.osrd.envelope_sim.allowances.utils.AllowanceValue.Percentage
Expand Down Expand Up @@ -155,9 +152,11 @@ private fun parseMarginValue(margin: MarginValue): AllowanceValue? {
private fun parseSimulationScheduleItems(
trainStops: List<TrainStop>
): List<SimulationScheduleItem> {
return trainStops.map {
SimulationScheduleItem(Offset(it.position.meters), null, it.duration.seconds, true)
}
return parseRawSimulationScheduleItems(
trainStops.map {
SimulationScheduleItem(Offset(it.position.meters), null, it.duration.seconds, true)
}
)
}

/** Sanity check, we assert that the result is not conflicting with the scheduled timetable */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package fr.sncf.osrd.standalone_sim

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import fr.sncf.osrd.api.api_v2.parseRawSimulationScheduleItems
import fr.sncf.osrd.api.api_v2.standalone_sim.SimulationScheduleItem
import fr.sncf.osrd.utils.units.Distance
import fr.sncf.osrd.utils.units.Offset
import fr.sncf.osrd.utils.units.TimeDelta
import java.util.stream.Stream
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SimulationScheduleItemsParserTests {

@ParameterizedTest
@MethodSource("testParseRawSimulationScheduleItemsArgs")
fun parserOutputsMinimumArrivalForSamePathOffset(
simulationScheduleItems: List<SimulationScheduleItem>,
expectedItems: List<SimulationScheduleItem>
) {
val mergedItems = parseRawSimulationScheduleItems(simulationScheduleItems)
Assertions.assertThat(mergedItems).usingRecursiveComparison().isEqualTo(expectedItems)
}

@SuppressFBWarnings(
value = ["UPM_UNCALLED_PRIVATE_METHOD"],
justification = "called implicitly by MethodSource"
)
private fun testParseRawSimulationScheduleItemsArgs(): Stream<Arguments> {
return Stream.of(
// Parser outputs minimum arrival for same path offset
Arguments.of(
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), TimeDelta(200), null, false),
SimulationScheduleItem(Offset(Distance(1000)), TimeDelta(100), null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, null, false),
),
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), TimeDelta(100), null, false),
)
),
// Parser outputs sum of stopFor for same path offset
Arguments.of(
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, TimeDelta(25), false),
SimulationScheduleItem(Offset(Distance(1000)), null, TimeDelta(75), false),
SimulationScheduleItem(Offset(Distance(1000)), null, null, false),
),
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, TimeDelta(100), false),
)
),
// Parser outputs true if at least one onStopSignal is true for same path offset
Arguments.of(
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, null, true),
),
listOf(
SimulationScheduleItem(Offset(Distance.ZERO), null, null, false),
SimulationScheduleItem(Offset(Distance(1000)), null, null, true),
)
)
)
}
}

0 comments on commit 06e8c38

Please sign in to comment.