Skip to content

Commit

Permalink
core: change interface for pathfinding errors on rolling-stock constr…
Browse files Browse the repository at this point in the history
…aints

TODO:
* build real response
* tests all constraints
  • Loading branch information
bougue-pe committed Jul 9, 2024
1 parent 839f7ef commit c783c53
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import fr.sncf.osrd.api.api_v2.TrackRange
import fr.sncf.osrd.conflicts.TravelledPath
import fr.sncf.osrd.graph.Pathfinding.Range
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.sim_infra.api.Path
import fr.sncf.osrd.utils.json.UnitAdapterFactory
import fr.sncf.osrd.utils.units.Length
import fr.sncf.osrd.utils.units.Offset
import java.util.*

interface PathfindingBlockResponse

Expand All @@ -25,7 +28,24 @@ class PathfindingBlockSuccess(

/** Offsets of the waypoints given as input */
@Json(name = "path_items_positions") val pathItemPositions: List<Offset<Path>>
) : PathfindingBlockResponse
) : PathfindingBlockResponse {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PathfindingBlockSuccess) return false

if (blocks != other.blocks) return false
if (routes != other.routes) return false
if (trackSectionRanges != other.trackSectionRanges) return false
if (length != other.length) return false
if (pathItemPositions != other.pathItemPositions) return false

return true
}

override fun hashCode(): Int {
return Objects.hash(blocks, routes, trackSectionRanges, length, pathItemPositions)
}
}

class NotFoundInBlocks(
@Json(name = "track_section_ranges") val trackSectionRanges: List<TrackRange>,
Expand All @@ -39,41 +59,21 @@ class NotFoundInRoutes(

class NotFoundInTracks : PathfindingBlockResponse

open class IncompatibleConstraint(
val blocks: List<String>,
val routes: List<String>,
@Json(name = "track_section_ranges") val trackSectionRanges: List<TrackRange>,
val length: Length<Path>,
@Json(name = "incompatible_ranges")
val incompatibleRanges: List<List<Offset<Path>>>, // List of pairs
class IncompatibleConstraintsPathResponse(
@Json(name = "relaxed_constraints_path") val relaxedConstraintsPath: PathfindingBlockSuccess,
@Json(name = "incompatible_constraints") val incompatibleConstraints: IncompatibleConstraints
) : PathfindingBlockResponse

class IncompatibleElectrification(
blocks: List<String>,
routes: List<String>,
@Json(name = "track_section_ranges") trackSectionRanges: List<TrackRange>,
length: Length<Path>,
@Json(name = "incompatible_ranges")
incompatibleRanges: List<List<Offset<Path>>> // List of pairs
) : IncompatibleConstraint(blocks, routes, trackSectionRanges, length, incompatibleRanges)

class IncompatibleLoadingGauge(
blocks: List<String>,
routes: List<String>,
@Json(name = "track_section_ranges") trackSectionRanges: List<TrackRange>,
length: Length<Path>,
@Json(name = "incompatible_ranges")
incompatibleRanges: List<List<Offset<Path>>> // List of pairs
) : IncompatibleConstraint(blocks, routes, trackSectionRanges, length, incompatibleRanges)

class IncompatibleSignalingSystem(
blocks: List<String>,
routes: List<String>,
@Json(name = "track_section_ranges") trackSectionRanges: List<TrackRange>,
length: Length<Path>,
@Json(name = "incompatible_ranges")
incompatibleRanges: List<List<Offset<Path>>> // List of pairs
) : IncompatibleConstraint(blocks, routes, trackSectionRanges, length, incompatibleRanges)
data class IncompatibleConstraints(
@Json(name = "incompatible_electrification_ranges")
val incompatibleElectrificationRanges: List<RangeValue<String>>,
@Json(name = "incompatible_gauge_ranges")
val incompatibleGaugeRanges: List<Range<TravelledPath>>,
@Json(name = "incompatible_signaling_system_ranges")
val incompatibleSignalingSystemRanges: List<RangeValue<String>>
)

data class RangeValue<T>(val range: Range<TravelledPath>, val value: T)

class PathfindingFailed(
@Json(name = "core_error") val coreError: OSRDError,
Expand All @@ -87,9 +87,7 @@ val polymorphicPathfindingResponseAdapter: PolymorphicJsonAdapterFactory<Pathfin
.withSubtype(NotFoundInBlocks::class.java, "not_found_in_blocks")
.withSubtype(NotFoundInRoutes::class.java, "not_found_in_routes")
.withSubtype(NotFoundInTracks::class.java, "not_found_in_tracks")
.withSubtype(IncompatibleElectrification::class.java, "incompatible_electrification")
.withSubtype(IncompatibleLoadingGauge::class.java, "incompatible_loading_gauge")
.withSubtype(IncompatibleSignalingSystem::class.java, "incompatible_signaling_system")
.withSubtype(IncompatibleConstraintsPathResponse::class.java, "incompatible_constraints")
.withSubtype(NotEnoughPathItems::class.java, "not_enough_path_items")
.withSubtype(PathfindingFailed::class.java, "pathfinding_failed")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,45 +155,61 @@ private fun computePaths(
.runPathfinding(waypoints)
if (res == null) {
// This way of handling it is suboptimal, but it should be reworked soon
val relaxedPathResponse =
runPathfindingPostProcessing(infra, possiblePathWithoutErrorNoConstraints)

when (currentConstraint::class.java) {
ElectrificationConstraints::class.java -> {
throw NoPathFoundException(
IncompatibleElectrification(
listOf(),
listOf(),
listOf(),
Length(0.meters),
listOf(),
IncompatibleConstraintsPathResponse(
relaxedPathResponse,
IncompatibleConstraints(
listOf(
RangeValue(
Pathfinding.Range(Offset.zero(), Offset.zero()),
"elec"
)
),
listOf(),
listOf()
)
)
)
}
LoadingGaugeConstraints::class.java -> {
throw NoPathFoundException(
IncompatibleLoadingGauge(
listOf(),
listOf(),
listOf(),
Length(0.meters),
listOf(),
IncompatibleConstraintsPathResponse(
relaxedPathResponse,
IncompatibleConstraints(
listOf(),
listOf(Pathfinding.Range(Offset.zero(), Offset.zero())),
listOf()
)
)
)
}
SignalingSystemConstraints::class.java -> {
throw NoPathFoundException(
IncompatibleSignalingSystem(
listOf(),
listOf(),
listOf(),
Length(0.meters),
listOf(),
IncompatibleConstraintsPathResponse(
relaxedPathResponse,
IncompatibleConstraints(
listOf(),
listOf(),
listOf(
RangeValue(
Pathfinding.Range(Offset.zero(), Offset.zero()),
"signal"
)
)
)
)
)
}
}
}
}
}
// It didn’t fail due to a constraint, no path exists
// It didn’t fail due to a RS constraint, no path exists
throw NoPathFoundException(NotFoundInBlocks(listOf(), Length(0.meters)))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import fr.sncf.osrd.utils.units.sumDistances
fun runPathfindingPostProcessing(
infra: FullInfra,
rawPath: PathfindingResultId<Block>
): PathfindingBlockResponse {
): PathfindingBlockSuccess {
// We reuse some of the old function of pathfindingResultConverter,
// there will be some cleanup to be made when the old version is removed
val oldRoutePath = makeRoutePath(infra.blockInfra, infra.rawInfra, rawPath.ranges)
Expand Down
46 changes: 44 additions & 2 deletions core/src/test/kotlin/fr/sncf/osrd/pathfinding/PathfindingV2Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ package fr.sncf.osrd.pathfinding
import fr.sncf.osrd.api.ApiTest
import fr.sncf.osrd.api.api_v2.TrackLocation
import fr.sncf.osrd.api.api_v2.TrackRange
import fr.sncf.osrd.api.api_v2.pathfinding.*
import fr.sncf.osrd.api.api_v2.pathfinding.IncompatibleConstraints
import fr.sncf.osrd.api.api_v2.pathfinding.IncompatibleConstraintsPathResponse
import fr.sncf.osrd.api.api_v2.pathfinding.PathfindingBlockRequest
import fr.sncf.osrd.api.api_v2.pathfinding.PathfindingBlockSuccess
import fr.sncf.osrd.api.api_v2.pathfinding.PathfindingBlocksEndpointV2
import fr.sncf.osrd.api.api_v2.pathfinding.RangeValue
import fr.sncf.osrd.api.api_v2.pathfinding.pathfindingRequestAdapter
import fr.sncf.osrd.api.api_v2.pathfinding.pathfindingResponseAdapter
import fr.sncf.osrd.graph.Pathfinding
import fr.sncf.osrd.railjson.schema.common.graph.EdgeDirection
import fr.sncf.osrd.railjson.schema.rollingstock.RJSLoadingGaugeType
import fr.sncf.osrd.utils.takes.TakesUtils
Expand Down Expand Up @@ -78,6 +86,29 @@ class PathfindingV2Test : ApiTest() {
val waypointsStart = listOf(waypointStart)
val waypointsEnd = listOf(waypointEnd)
val waypoints = listOf(waypointsStart, waypointsEnd)

val unconstrainedRequestBody =
pathfindingRequestAdapter.toJson(
PathfindingBlockRequest(
rollingStockLoadingGauge = RJSLoadingGaugeType.GC,
rollingStockIsThermal = true,
rollingStockSupportedElectrifications = listOf(),
rollingStockSupportedSignalingSystems =
listOf("BAL", "BAPR", "TVM300", "TVM430"),
timeout = null,
infra = "tiny_infra/infra.json",
expectedVersion = "1",
pathItems = waypoints,
)
)
val unconstrainedRawResponse =
PathfindingBlocksEndpointV2(infraManager)
.act(RqFake("POST", "/v2/pathfinding/blocks", unconstrainedRequestBody))
val unconstrainedResponse = TakesUtils.readBodyResponse(unconstrainedRawResponse)
val unconstrainedParsed =
(pathfindingResponseAdapter.fromJson(unconstrainedResponse)
as? PathfindingBlockSuccess)!!

val requestBody =
pathfindingRequestAdapter.toJson(
PathfindingBlockRequest(
Expand All @@ -96,6 +127,17 @@ class PathfindingV2Test : ApiTest() {
.act(RqFake("POST", "/v2/pathfinding/blocks", requestBody))
val response = TakesUtils.readBodyResponse(rawResponse)
val parsed =
(pathfindingResponseAdapter.fromJson(response) as? IncompatibleElectrification)!!
(pathfindingResponseAdapter.fromJson(response)
as? IncompatibleConstraintsPathResponse)!!
assert(parsed.relaxedConstraintsPath == unconstrainedParsed)
assert(
parsed.incompatibleConstraints ==
IncompatibleConstraints(
incompatibleElectrificationRanges =
listOf(RangeValue(Pathfinding.Range(Offset.zero(), Offset.zero()), "elec")),
incompatibleGaugeRanges = listOf(),
incompatibleSignalingSystemRanges = listOf()
)
)
}
}

0 comments on commit c783c53

Please sign in to comment.