Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: OpenRailAssociation/osrd
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3a36df3859640aeb75da58ef4546a39722c7526f
Choose a base ref
..
head repository: OpenRailAssociation/osrd
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ccd740908c7b24de3ed71e077caca640cc9e7954
Choose a head ref
Showing with 432 additions and 376 deletions.
  1. +7 −250 core/src/main/java/fr/sncf/osrd/conflicts/Conflicts.kt
  2. +154 −0 core/src/main/java/fr/sncf/osrd/conflicts/IncrementalConflictDetector.kt
  3. +18 −18 core/src/main/kotlin/fr/sncf/osrd/api/api_v2/standalone_sim/RollingStockParser.kt
  4. +1 −1 core/src/main/kotlin/fr/sncf/osrd/api/api_v2/standalone_sim/SimulationEndpoint.kt
  5. +2 −2 core/src/main/kotlin/fr/sncf/osrd/api/api_v2/standalone_sim/SimulationRequest.kt
  6. +6 −5 core/src/main/kotlin/fr/sncf/osrd/api/api_v2/stdcm/STDCMEndpointV2.kt
  7. +2 −2 core/src/main/kotlin/fr/sncf/osrd/api/api_v2/stdcm/STDCMRequestV2.kt
  8. +5 −6 core/src/main/kotlin/fr/sncf/osrd/api/stdcm/STDCMEndpoint.kt
  9. +20 −22 core/src/main/kotlin/fr/sncf/osrd/stdcm/preprocessing/implementation/BlockAvailability.kt
  10. +63 −0 core/src/test/kotlin/fr/sncf/osrd/conflicts/IncrementalConflictDetectorTests.kt
  11. +1 −1 editoast/src/core/simulation.rs
  12. +1 −1 editoast/src/core/stdcm.rs
  13. +2 −8 editoast/src/views/mod.rs
  14. +32 −0 editoast/src/views/projects.rs
  15. +62 −2 editoast/src/views/test_app.rs
  16. +1 −1 editoast/src/views/timetable/stdcm.rs
  17. +2 −1 editoast/src/views/train_schedule.rs
  18. +4 −5 front/package-lock.json
  19. +1 −1 front/package.json
  20. +4 −2 front/src/applications/stdcm/components/StdcmForm/StdcmOpSchedule.tsx
  21. +2 −2 front/src/applications/stdcm/components/StdcmForm/StdcmVias.tsx
  22. +30 −35 front/src/reducers/osrdconf/stdcmConf/index.ts
  23. +1 −1 front/src/reducers/osrdconf/types.ts
  24. +2 −2 front/tests/utils/setup-utils.ts
  25. +5 −4 python/osrd_schemas/poetry.lock
  26. +4 −4 python/railjson_generator/poetry.lock
257 changes: 7 additions & 250 deletions core/src/main/java/fr/sncf/osrd/conflicts/Conflicts.kt
Original file line number Diff line number Diff line change
@@ -7,10 +7,6 @@ import fr.sncf.osrd.api.ConflictDetectionEndpoint.ConflictDetectionResult.Confli
import fr.sncf.osrd.api.ConflictDetectionEndpoint.ConflictDetectionResult.ConflictRequirement
import fr.sncf.osrd.standalone_sim.result.ResultTrain.RoutingRequirement
import fr.sncf.osrd.standalone_sim.result.ResultTrain.SpacingRequirement
import kotlin.math.max
import kotlin.math.min

const val DEFAULT_WORK_SCHEDULE_ID: Long = -1

interface SpacingTrainRequirement {
val trainId: Long
@@ -54,75 +50,24 @@ enum class RequirementType {
WORK_SCHEDULE
}

data class ConflictProperties(
// If there are conflicts, minimum delay that should be added to the train so that there are no
// conflicts anymore
val minDelayWithoutConflicts: Double,
// If there are no conflicts, maximum delay that can be added to the train without creating any
// conflict
val maxDelayWithoutConflicts: Double,
// If there are no conflicts, minimum begin time of the next requirement that could conflict
val timeOfNextConflict: Double
)

fun detectConflicts(trainRequirements: List<TrainRequirements>): List<Conflict> {
return detectRequirementConflicts(convertTrainRequirements(trainRequirements))
}

fun detectRequirementConflicts(requirements: List<Requirements>): List<Conflict> {
val res = incrementalConflictDetectorFromRequirements(requirements).checkConflicts()
val res = conflictDetectorFromRequirements(requirements).checkConflicts()
return mergeConflicts(res)
}

interface IncrementalConflictDetector {
interface ConflictDetector {
fun checkConflicts(): List<Conflict>

fun checkConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): List<Conflict>

/** Only check the given new spacing requirement against the scheduled requirements */
fun checkSpacingRequirement(req: SpacingRequirement): List<Conflict>

/** Only check the given new routing requirement against the scheduled requirements */
fun checkRoutingRequirement(req: RoutingRequirement): List<Conflict>

fun minDelayWithoutConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double

fun maxDelayWithoutConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double

fun timeOfNextConflict(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double

fun analyseConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): ConflictProperties
}

fun incrementalConflictDetector(
trainRequirements: List<TrainRequirements>
): IncrementalConflictDetector {
return IncrementalConflictDetectorImpl(convertTrainRequirements(trainRequirements))
}

fun incrementalConflictDetectorFromRequirements(
requirements: List<Requirements>
): IncrementalConflictDetector {
return IncrementalConflictDetectorImpl(requirements)
fun conflictDetectorFromRequirements(requirements: List<Requirements>): ConflictDetector {
return ConflictDetectorImpl(requirements)
}

class IncrementalConflictDetectorImpl(requirements: List<Requirements>) :
IncrementalConflictDetector {
class ConflictDetectorImpl(requirements: List<Requirements>) : ConflictDetector {
private val spacingZoneRequirements =
mutableMapOf<String, MutableList<SpacingZoneRequirement>>()
private val routingZoneRequirements =
@@ -249,201 +194,13 @@ class IncrementalConflictDetectorImpl(requirements: List<Requirements>) :
}
return res
}

override fun checkConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): List<Conflict> {
val res = mutableListOf<Conflict>()
for (spacingRequirement in spacingRequirements) {
res.addAll(checkSpacingRequirement(spacingRequirement))
}
for (routingRequirement in routingRequirements) {
res.addAll(checkRoutingRequirement(routingRequirement))
}
return res
}

override fun checkSpacingRequirement(req: SpacingRequirement): List<Conflict> {
val requirements = spacingZoneRequirements[req.zone] ?: return listOf()

val res = mutableListOf<Conflict>()
for (otherReq in requirements) {
val beginTime = max(req.beginTime, otherReq.beginTime)
val endTime = min(req.endTime, otherReq.endTime)
if (beginTime < endTime) {
val trainIds = mutableListOf<Long>()
val workScheduleIds = mutableListOf<Long>()
if (otherReq.id.type == RequirementType.WORK_SCHEDULE)
workScheduleIds.add(otherReq.id.id)
else trainIds.add(otherReq.id.id)
val conflictReq = ConflictRequirement(req.zone, beginTime, endTime)
res.add(
Conflict(
trainIds,
workScheduleIds,
beginTime,
endTime,
ConflictType.SPACING,
listOf(conflictReq)
)
)
}
}

return res
}

override fun checkRoutingRequirement(req: RoutingRequirement): List<Conflict> {
val res = mutableListOf<Conflict>()
for (zoneReq in req.zones) {
val zoneReqConfig =
RoutingZoneConfig(zoneReq.entryDetector, zoneReq.exitDetector, zoneReq.switches!!)
val requirements = routingZoneRequirements[zoneReq.zone!!] ?: continue

for (otherReq in requirements) {
if (otherReq.config == zoneReqConfig) continue
val beginTime = max(req.beginTime, otherReq.beginTime)
val endTime = min(zoneReq.endTime, otherReq.endTime)
val conflictReq = ConflictRequirement(zoneReq.zone, beginTime, endTime)
if (beginTime < endTime)
res.add(
Conflict(
listOf(otherReq.trainId),
beginTime,
endTime,
ConflictType.ROUTING,
listOf(conflictReq)
)
)
}
}
return res
}

override fun analyseConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): ConflictProperties {
val minDelayWithoutConflicts =
minDelayWithoutConflicts(spacingRequirements, routingRequirements)
if (minDelayWithoutConflicts != 0.0) { // There are initial conflicts
return ConflictProperties(minDelayWithoutConflicts, 0.0, 0.0)
} else { // There are no initial conflicts
var maxDelay = Double.POSITIVE_INFINITY
var timeOfNextConflict = Double.POSITIVE_INFINITY
for (spacingRequirement in spacingRequirements) {
if (spacingZoneRequirements[spacingRequirement.zone!!] != null) {
val endTime = spacingRequirement.endTime
for (requirement in spacingZoneRequirements[spacingRequirement.zone!!]!!) {
if (endTime <= requirement.beginTime) {
maxDelay = min(maxDelay, requirement.beginTime - endTime)
timeOfNextConflict = min(timeOfNextConflict, requirement.beginTime)
}
}
}
}
for (routingRequirement in routingRequirements) {
for (zoneReq in routingRequirement.zones) {
if (routingZoneRequirements[zoneReq.zone!!] != null) {
val endTime = zoneReq.endTime
val config =
RoutingZoneConfig(
zoneReq.entryDetector,
zoneReq.exitDetector,
zoneReq.switches!!
)
for (requirement in routingZoneRequirements[zoneReq.zone!!]!!) {
if (endTime <= requirement.beginTime && config != requirement.config) {
maxDelay = min(maxDelay, requirement.beginTime - endTime)
timeOfNextConflict = min(timeOfNextConflict, requirement.beginTime)
}
}
}
}
}
return ConflictProperties(minDelayWithoutConflicts, maxDelay, timeOfNextConflict)
}
}

override fun minDelayWithoutConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double {
var globalMinDelay = 0.0
while (globalMinDelay.isFinite()) {
var minDelay = 0.0
for (spacingRequirement in spacingRequirements) {
if (spacingZoneRequirements[spacingRequirement.zone!!] != null) {
val conflictingRequirements =
spacingZoneRequirements[spacingRequirement.zone!!]!!.filter {
!(spacingRequirement.beginTime >= it.endTime ||
spacingRequirement.endTime <= it.beginTime)
}
if (conflictingRequirements.isNotEmpty()) {
val latestEndTime = conflictingRequirements.maxOf { it.endTime }
minDelay = max(minDelay, latestEndTime - spacingRequirement.beginTime)
}
}
}
for (routingRequirement in routingRequirements) {
for (zoneReq in routingRequirement.zones) {
if (routingZoneRequirements[zoneReq.zone!!] != null) {
val config =
RoutingZoneConfig(
zoneReq.entryDetector,
zoneReq.exitDetector,
zoneReq.switches!!
)
val conflictingRequirements =
routingZoneRequirements[zoneReq.zone!!]!!.filter {
!(routingRequirement.beginTime >= it.endTime ||
zoneReq.endTime <= it.beginTime) && config != it.config
}
if (conflictingRequirements.isNotEmpty()) {
val latestEndTime = conflictingRequirements.maxOf { it.endTime }
minDelay = max(minDelay, latestEndTime - routingRequirement.beginTime)
}
}
}
}
// No new conflicts
if (minDelay == 0.0) return globalMinDelay

// Check for conflicts with newly added delay
globalMinDelay += minDelay
spacingRequirements.onEach {
it.beginTime += minDelay
it.endTime += minDelay
}
routingRequirements.onEach { routingRequirement ->
routingRequirement.beginTime += minDelay
routingRequirement.zones.onEach { it.endTime += minDelay }
}
}
return Double.POSITIVE_INFINITY
}

override fun maxDelayWithoutConflicts(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double {
return analyseConflicts(spacingRequirements, routingRequirements).maxDelayWithoutConflicts
}

override fun timeOfNextConflict(
spacingRequirements: List<SpacingRequirement>,
routingRequirements: List<RoutingRequirement>
): Double {
return analyseConflicts(spacingRequirements, routingRequirements).timeOfNextConflict
}
}

/**
* Return a list of requirement conflict groups. If requirements pairs (A, B) and (B, C) are
* conflicting, then (A, B, C) are part of the same conflict group.
*/
private fun <ReqT : ResourceRequirement> detectRequirementConflicts(
internal fun <ReqT : ResourceRequirement> detectRequirementConflicts(
requirements: MutableList<ReqT>,
conflicting: (ReqT, ReqT) -> Boolean,
): List<List<ReqT>> {
@@ -581,7 +338,7 @@ fun mergeConflicts(conflicts: List<Conflict>): List<Conflict> {
return mergedConflicts
}

private fun convertTrainRequirements(
internal fun convertTrainRequirements(
trainRequirements: List<TrainRequirements>
): List<Requirements> {
val res = mutableListOf<Requirements>()
Loading