Skip to content

Commit 1a25262

Browse files
committed
core: include train name in generated debug data
Signed-off-by: Eloi Charpentier <[email protected]>
1 parent 9920db2 commit 1a25262

File tree

6 files changed

+100
-28
lines changed

6 files changed

+100
-28
lines changed

core/src/main/java/fr/sncf/osrd/api/TimetableCacheManager.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,34 @@ import org.slf4j.LoggerFactory
2727

2828
typealias TimetableId = Int
2929

30-
@JvmInline
31-
value class STDCMRequirements(val map: Map<ZoneId, RangeSet<Double>>) :
32-
Map<ZoneId, RangeSet<Double>> by map {
30+
data class STDCMRequirements(
31+
val map: Map<ZoneId, RangeSet<Double>>,
32+
val requirementMetadata: Map<ZoneId, List<RequirementMetadata>>,
33+
) {
34+
@Serializable
35+
data class RequirementMetadata(val from: Double, val to: Double, val trainName: String)
3336

3437
fun toSerializable(): SerializableMap {
3538
return SerializableMap(
3639
map.entries.associate { (key, value) ->
3740
key.index to value.asRanges().map { SerializableRange.fromRange(it) }
38-
}
41+
},
42+
requirementMetadata.mapKeys { it.key.index },
3943
)
4044
}
4145

4246
@Serializable
43-
data class SerializableMap(val map: Map<UInt, List<SerializableRange>>) {
47+
data class SerializableMap(
48+
val map: Map<UInt, List<SerializableRange>>,
49+
val requirementMetadata: Map<UInt, List<RequirementMetadata>>,
50+
) {
4451
fun toSTDCMRequirements(): STDCMRequirements {
4552
val converted =
4653
map.entries.associate { (key, value) ->
4754
ZoneId(key) to SerializableRange.rangesToRangeSet(value)
4855
}
49-
return STDCMRequirements(converted)
56+
val metadata = requirementMetadata.mapKeys { ZoneId(it.key) }
57+
return STDCMRequirements(converted, metadata)
5058
}
5159
}
5260

@@ -122,7 +130,7 @@ class TimetableCacheManager(
122130
}
123131
}
124132
cache[cacheKey] = requirements
125-
val nEntries = requirements.entries.sumOf { it.value.asRanges().size }
133+
val nEntries = requirements.map.entries.sumOf { it.value.asRanges().size }
126134
logger.info(
127135
"timetable requirements computed in ${time.inWholeSeconds} seconds, $nEntries map entries"
128136
)

core/src/main/java/fr/sncf/osrd/api/TimetableProvider.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ class TimetableDownloader(
136136
): STDCMRequirements {
137137
return runBlocking {
138138
val res = mutableMapOf<ZoneId, RangeSet<Double>>()
139+
val metadata =
140+
mutableMapOf<ZoneId, MutableList<STDCMRequirements.RequirementMetadata>>()
139141
val trainRequirementsById = fetchTrainRequirements(infraId, timetableId)
140142
trainRequirementsById.collect { trainRequirementById ->
141143
for (rjsRequirement in trainRequirementById.spacingRequirements) {
@@ -147,9 +149,18 @@ class TimetableDownloader(
147149
)
148150
val set = res.computeIfAbsent(requirement.zone) { TreeRangeSet.create() }
149151
set.add(Range.closedOpen(requirement.beginTime, requirement.endTime))
152+
metadata
153+
.computeIfAbsent(requirement.zone) { mutableListOf() }
154+
.add(
155+
STDCMRequirements.RequirementMetadata(
156+
requirement.beginTime,
157+
requirement.endTime,
158+
trainRequirementById.trainName,
159+
)
160+
)
150161
}
151162
}
152-
STDCMRequirements(res)
163+
STDCMRequirements(res, metadata)
153164
}
154165
}
155166
}
@@ -173,6 +184,7 @@ class JsonTimetableProvider(val timetableDirectory: String) : TimetableProvider
173184
val jsonReader = JsonReader.of(source)
174185
jsonReader.beginArray()
175186
val res = mutableMapOf<ZoneId, RangeSet<Double>>()
187+
val metadata = mutableMapOf<ZoneId, MutableList<STDCMRequirements.RequirementMetadata>>()
176188
while (jsonReader.hasNext()) {
177189
val train = adapter.fromJson(jsonReader)!!
178190
for (rjsSpacingReq in train.spacingRequirements) {
@@ -184,10 +196,19 @@ class JsonTimetableProvider(val timetableDirectory: String) : TimetableProvider
184196
)
185197
val set = res.computeIfAbsent(spacingReq.zone) { TreeRangeSet.create() }
186198
set.add(Range.closedOpen(spacingReq.beginTime, spacingReq.endTime))
199+
metadata
200+
.computeIfAbsent(spacingReq.zone) { mutableListOf() }
201+
.add(
202+
STDCMRequirements.RequirementMetadata(
203+
spacingReq.beginTime,
204+
spacingReq.endTime,
205+
train.trainName,
206+
)
207+
)
187208
}
188209
}
189210

190-
return STDCMRequirements(res)
211+
return STDCMRequirements(res, metadata)
191212
}
192213
}
193214

core/src/main/java/fr/sncf/osrd/cli/BenchSTDCM.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class BenchSTDCM : CliCommand {
177177
0.0,
178178
steps,
179179
makeBlockAvailability(
180-
requirements,
180+
requirements.requirements,
181181
gridMarginBeforeTrain = request.timeGapBefore.seconds,
182182
gridMarginAfterTrain = request.timeGapAfter.seconds,
183183
timeStep = request.timeStep!!.seconds,

core/src/main/kotlin/fr/sncf/osrd/api/conflicts/ConflictDetectionRequest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ open class TrainRequirementsRequest(
4848
// TrainRequirementsRequest>
4949
class TrainRequirementsById(
5050
@Json(name = "train_id") val trainId: String,
51+
@Json(name = "train_name") val trainName: String,
5152
@Json(name = "start_time") startTime: ZonedDateTime,
5253
@Json(name = "spacing_requirements") spacingRequirements: Collection<RJSSpacingRequirement>,
5354
@Json(name = "routing_requirements") routingRequirements: Collection<RJSRoutingRequirement>,

core/src/main/kotlin/fr/sncf/osrd/api/stdcm/OutputSimDebugData.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import com.squareup.moshi.Json
55
import com.squareup.moshi.JsonAdapter
66
import com.squareup.moshi.Moshi
77
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
8+
import fr.sncf.osrd.api.STDCMRequirements
89
import fr.sncf.osrd.api.path_properties.PathPropResponse
910
import fr.sncf.osrd.api.path_properties.makePathPropResponse
1011
import fr.sncf.osrd.api.path_properties.polymorphicElectrificationAdapter
1112
import fr.sncf.osrd.api.standalone_sim.SimulationSuccess
1213
import fr.sncf.osrd.api.standalone_sim.polymorphicElectricalProfileAdapter
1314
import fr.sncf.osrd.api.standalone_sim.polymorphicSimulationResponseAdapter
1415
import fr.sncf.osrd.api.standalone_sim.polymorphicSpeedLimitSourceAdapter
15-
import fr.sncf.osrd.conflicts.ParsedRequirements
1616
import fr.sncf.osrd.path.interfaces.TrainPath
1717
import fr.sncf.osrd.sim_infra.api.RawInfra
18+
import fr.sncf.osrd.sim_infra.api.ZoneId
1819
import fr.sncf.osrd.stdcm.STDCMResult
1920
import fr.sncf.osrd.stdcm.graph.EngineeringAllowanceRange
2021
import fr.sncf.osrd.utils.json.UnitAdapterFactory
@@ -68,7 +69,7 @@ fun generateDebugData(
6869
path: STDCMResult,
6970
simulationResponse: SimulationSuccess,
7071
departureTime: ZonedDateTime,
71-
requirements: ParsedRequirements,
72+
requirements: Map<ZoneId, List<STDCMRequirements.RequirementMetadata>>,
7273
): OutputSimDebugData {
7374
return OutputSimDebugData(
7475
simOutput = simulationResponse,
@@ -85,24 +86,24 @@ fun generateDebugData(
8586

8687
fun makeOtherRequirements(
8788
rawInfra: RawInfra,
88-
requirements: ParsedRequirements,
89+
requirements: Map<ZoneId, List<STDCMRequirements.RequirementMetadata>>,
8990
path: STDCMResult,
9091
): List<TrainZoneRequirement> {
9192
val res = mutableListOf<TrainZoneRequirement>()
9293
val minRelevantTime = -3_600.0
9394
val maxRelevantTime = path.envelope.totalTime + 3_600.0
9495
for (zoneRange in path.trainPath.getZoneRanges()) {
95-
val timeRanges = requirements[zoneRange.value] ?: continue
96-
for (timeRange in timeRanges.values) {
97-
val beginTime = max(minRelevantTime, timeRange.lowerEndpoint() - path.departureTime)
98-
val endTime = min(maxRelevantTime, timeRange.upperEndpoint() - path.departureTime)
96+
val metadataList = requirements[zoneRange.value] ?: continue
97+
for (metadata in metadataList) {
98+
val beginTime = max(minRelevantTime, metadata.from - path.departureTime)
99+
val endTime = min(maxRelevantTime, metadata.to - path.departureTime)
99100
if (endTime < beginTime) continue
100101
res.add(
101102
TrainZoneRequirement(
102103
zoneName = rawInfra.getZoneName(zoneRange.value),
103104
beginTime = beginTime.seconds,
104105
endTime = endTime.seconds,
105-
trainName = null, // TODO: would be very useful, but hard to get
106+
trainName = metadata.trainName,
106107
)
107108
)
108109
}

core/src/main/kotlin/fr/sncf/osrd/api/stdcm/STDCMEndpoint.kt

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class STDCMEndpoint(
129129
0.0,
130130
steps,
131131
makeBlockAvailability(
132-
requirements,
132+
requirements.requirements,
133133
gridMarginBeforeTrain = request.timeGapBefore.seconds,
134134
gridMarginAfterTrain = request.timeGapAfter.seconds,
135135
timeStep = request.timeStep!!.seconds,
@@ -163,7 +163,13 @@ class STDCMEndpoint(
163163
val departureTime =
164164
request.startTime.plus(ofMillis((path.departureTime * 1000).toLong()))
165165

166-
logDebugData(infra.rawInfra, path, simulationResponse, departureTime, requirements)
166+
logDebugData(
167+
infra.rawInfra,
168+
path,
169+
simulationResponse,
170+
departureTime,
171+
requirements.metadata,
172+
)
167173

168174
val response = STDCMSuccess(simulationResponse, pathfindingResponse, departureTime)
169175
RsJson(RsWithBody(stdcmResponseAdapter.toJson(response)))
@@ -182,7 +188,7 @@ class STDCMEndpoint(
182188
path: STDCMResult,
183189
simulationResponse: SimulationSuccess,
184190
departureTime: ZonedDateTime,
185-
requirements: ParsedRequirements,
191+
requirements: Map<ZoneId, List<STDCMRequirements.RequirementMetadata>>,
186192
) {
187193
val filename = System.getenv("STDCM_DEBUG_DATA_FILENAME") ?: return
188194
val debugData =
@@ -378,6 +384,11 @@ fun parseSteps(
378384
.toList()
379385
}
380386

387+
data class RequirementsWithMetadata(
388+
val requirements: ParsedRequirements,
389+
val metadata: Map<ZoneId, List<STDCMRequirements.RequirementMetadata>>,
390+
)
391+
381392
/**
382393
* Collect all spacing requirements in an easily fetchable format. Combines both train requirements
383394
* and work schedules.
@@ -386,13 +397,23 @@ fun getRequirements(
386397
request: STDCMRequest,
387398
infra: FullInfra,
388399
timetableCacheManager: TimetableCacheManager,
389-
): ParsedRequirements {
390-
val res = mutableMapOf<ZoneId, TreeRangeSet<Double>>()
400+
): RequirementsWithMetadata {
401+
val requirements = mutableMapOf<ZoneId, TreeRangeSet<Double>>()
402+
val metadata = mutableMapOf<ZoneId, MutableList<STDCMRequirements.RequirementMetadata>>()
391403
convertWorkScheduleCollection(infra.rawInfra, request.workSchedules)
392404
.spacingRequirements
393405
.forEach { spacingReq ->
394-
val set = res.computeIfAbsent(spacingReq.zone) { TreeRangeSet.create() }
406+
val set = requirements.computeIfAbsent(spacingReq.zone) { TreeRangeSet.create() }
395407
set.add(Range.closedOpen(spacingReq.beginTime, spacingReq.endTime))
408+
metadata
409+
.computeIfAbsent(spacingReq.zone) { mutableListOf() }
410+
.add(
411+
STDCMRequirements.RequirementMetadata(
412+
spacingReq.beginTime,
413+
spacingReq.endTime,
414+
"work schedule",
415+
)
416+
)
396417
}
397418

398419
val trainRequirements = runBlocking { timetableCacheManager.get(infra, request.timetableId) }
@@ -403,8 +424,8 @@ fun getRequirements(
403424
searchWindowBeginEpoch +
404425
request.maximumDepartureDelay!!.seconds +
405426
request.maximumRunTime.seconds
406-
for ((zoneId, rangeSet) in trainRequirements) {
407-
val setBuilder = res.computeIfAbsent(zoneId) { TreeRangeSet.create() }
427+
for ((zoneId, rangeSet) in trainRequirements.map) {
428+
val setBuilder = requirements.computeIfAbsent(zoneId) { TreeRangeSet.create() }
408429
for (range in rangeSet.asRanges()) {
409430
// Filter out unnecessary requirements
410431
val included =
@@ -422,9 +443,29 @@ fun getRequirements(
422443
}
423444
}
424445
}
425-
return res.mapValues { rangeSet ->
426-
TreeMap(rangeSet.value.asRanges().associateBy { it.upperEndpoint() })
446+
447+
for (entry in trainRequirements.requirementMetadata) {
448+
val metadataList = metadata.computeIfAbsent(entry.key) { mutableListOf() }
449+
for (metadata in entry.value) {
450+
val included =
451+
metadata.from > searchWindowBeginEpoch && metadata.to < searchWindowEndEpoch
452+
if (included) {
453+
metadataList.add(
454+
STDCMRequirements.RequirementMetadata(
455+
metadata.from - searchWindowBeginEpoch,
456+
metadata.to - searchWindowBeginEpoch,
457+
metadata.trainName,
458+
)
459+
)
460+
}
461+
}
427462
}
463+
return RequirementsWithMetadata(
464+
requirements.mapValues { rangeSet ->
465+
TreeMap(rangeSet.value.asRanges().associateBy { it.upperEndpoint() })
466+
},
467+
metadata,
468+
)
428469
}
429470

430471
fun parseMarginValue(margin: MarginValue): AllowanceValue? {

0 commit comments

Comments
 (0)