Skip to content

Commit

Permalink
core: editoast: handle failing simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
flomonster committed Apr 30, 2024
1 parent 1565855 commit 6199e2b
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fr.sncf.osrd.sim_infra.utils

import fr.sncf.osrd.reporting.exceptions.ErrorType
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.sim_infra.api.*
import fr.sncf.osrd.utils.indexing.*

Expand Down Expand Up @@ -116,7 +118,9 @@ private fun findRoute(
return routeId
}
}
throw RuntimeException("Couldn't find a route matching the given chunk list")
val error = OSRDError(ErrorType.ScheduleMetadataExtractionFailed)
error.context["reason"] = "Couldn't find a route matching the given chunk list"
throw error
}

/** Returns false if the route differs from the path */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ public enum ErrorType {
"path_with_repeated_tracks", "the path goes over the same track multiple times", ErrorCause.INTERNAL),
PathHasInvalidItemPositions(
"path_has_invalid_item_positions", "the path has invalid item positions", ErrorCause.INTERNAL),
ScheduleMetadataExtractionFailed(
"schedule_metadata_extraction_failed", "schedule metadata extraction failed", ErrorCause.INTERNAL),
AllowanceRangeOutOfBounds("allowance_range", "Allowance ranges are out of bounds", ErrorCause.USER),
AllowanceOutOfBounds("allowance", "Allowance is out of bounds", ErrorCause.USER),
InvalidSpeedLimitValue("speed_limit_value", "Speed limit must be greater than 0", ErrorCause.USER),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import fr.sncf.osrd.envelope.EnvelopeInterpolate
import fr.sncf.osrd.envelope.EnvelopePhysics
import fr.sncf.osrd.envelope.EnvelopeTimeInterpolate
import fr.sncf.osrd.envelope_sim_infra.EnvelopeTrainPath
import fr.sncf.osrd.reporting.exceptions.ErrorType
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.signaling.SigSystemManager
import fr.sncf.osrd.signaling.SignalingSimulator
import fr.sncf.osrd.signaling.SignalingTrainState
Expand Down Expand Up @@ -581,7 +583,9 @@ fun trainPathBlockOffset(
}
}
}
throw RuntimeException("Couldn't find first chunk on the block path")
val error = OSRDError(ErrorType.ScheduleMetadataExtractionFailed)
error.context["reason"] = "Couldn't find first chunk on the block path"
throw error
}

fun simplifyPositions(positions: ArrayList<ResultPosition>): ArrayList<ResultPosition> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fr.sncf.osrd.api.ElectricalProfileSetManager
import fr.sncf.osrd.api.ExceptionHandler
import fr.sncf.osrd.api.InfraManager
import fr.sncf.osrd.api.pathfinding.makeChunkPath
import fr.sncf.osrd.reporting.exceptions.OSRDError
import fr.sncf.osrd.reporting.warnings.DiagnosticRecorderImpl
import fr.sncf.osrd.sim_infra.api.RawInfra
import fr.sncf.osrd.sim_infra.api.Route
Expand Down Expand Up @@ -68,8 +69,10 @@ class SimulationEndpoint(
request.initialSpeed,
request.margins,
)

return RsJson(RsWithBody(SimulationResponse.adapter.toJson(res)))
return RsJson(RsWithBody(simulationResponseAdapter.toJson(res)))
} catch (error: OSRDError) {
val response = SimulationFailed(error);
return RsJson(RsWithBody(simulationResponseAdapter.toJson(response)))
} catch (ex: Throwable) {
return ExceptionHandler.handle(ex)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,27 @@ package fr.sncf.osrd.api.api_v2.standalone_sim
import com.squareup.moshi.Json
import com.squareup.moshi.JsonAdapter
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.RoutingRequirement
import fr.sncf.osrd.api.api_v2.SignalSighting
import fr.sncf.osrd.api.api_v2.SpacingRequirement
import fr.sncf.osrd.api.api_v2.ZoneUpdate
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.Offset
import fr.sncf.osrd.utils.units.TimeDelta

class SimulationResponse(
interface SimulationResponse

class SimulationSuccess(
val base: ReportTrain,
val provisional: ReportTrain,
@Json(name = "final_output") val finalOutput: CompleteReportTrain,
val mrsp: MRSPResponse,
@Json(name = "power_restrictions") val powerRestrictions: List<PowerRestriction>,
) {
companion object {
val adapter: JsonAdapter<SimulationResponse> =
Moshi.Builder()
.addLast(UnitAdapterFactory())
.addLast(KotlinJsonAdapterFactory())
.build()
.adapter(SimulationResponse::class.java)
}
}
) : SimulationResponse

class MRSPResponse(
val positions: List<Offset<Path>>,
Expand Down Expand Up @@ -59,3 +54,20 @@ class PowerRestriction(
val code: String,
val handled: Boolean
)

class SimulationFailed(
@Json(name = "core_error") val coreError: OSRDError,
) : SimulationResponse

val polymorphicAdapter: PolymorphicJsonAdapterFactory<SimulationResponse> =
PolymorphicJsonAdapterFactory.of(SimulationResponse::class.java, "status")
.withSubtype(SimulationSuccess::class.java, "success")
.withSubtype(SimulationFailed::class.java, "simulation_failed")

val simulationResponseAdapter: JsonAdapter<SimulationResponse> =
Moshi.Builder()
.add(polymorphicAdapter)
.addLast(UnitAdapterFactory())
.addLast(KotlinJsonAdapterFactory())
.build()
.adapter(SimulationResponse::class.java)
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fun runStandaloneSimulation(
schedule: List<SimulationScheduleItem>,
initialSpeed: Double,
margins: RangeValues<MarginValue>,
): SimulationResponse {
): SimulationSuccess {
// MRSP & SpeedLimits
val mrsp = MRSP.computeMRSP(pathProps, rollingStock, true, speedLimitTag)
val speedLimits = MRSP.computeMRSP(pathProps, rollingStock, false, speedLimitTag)
Expand Down Expand Up @@ -129,7 +129,7 @@ fun runStandaloneSimulation(
schedule
)

return SimulationResponse(
return SimulationSuccess(
base = maxEffortResult,
provisional = provisionalResult,
finalOutput = finalEnvelopeResult,
Expand Down
29 changes: 27 additions & 2 deletions editoast/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ components:
type: object
- properties:
train_names:
description: |-
List of train names involved in the conflict.
This is a one to one mapping with the [CoreConflict::train_ids].
items:
type: string
type: array
Expand Down Expand Up @@ -4820,6 +4823,17 @@ components:
- rolling_stock_name
- status
type: object
- properties:
core_error:
$ref: '#/components/schemas/InternalError'
status:
enum:
- pathfinding_failed
type: string
required:
- core_error
- status
type: object
PathfindingStep:
properties:
duration:
Expand Down Expand Up @@ -6748,7 +6762,7 @@ components:
- electrification_ranges
- power_restriction_ranges
type: object
SimulationResult:
SimulationResponse:
oneOf:
- properties:
base:
Expand Down Expand Up @@ -6883,11 +6897,22 @@ components:
- status
type: object
- properties:
status:
enum:
- pathfinding_not_found
type: string
required:
- status
type: object
- properties:
error_type:
type: string
status:
enum:
- pathfinding_failed
type: string
required:
- error_type
- status
type: object
- properties:
Expand Down Expand Up @@ -11306,7 +11331,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SimulationResult'
$ref: '#/components/schemas/SimulationResponse'
description: Simulation Output
summary: Retrieve the space, speed and time curve of a given train
tags:
Expand Down
34 changes: 27 additions & 7 deletions editoast/src/core/v2/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use serde::Serialize;
use utoipa::ToSchema;

use super::pathfinding::TrackRange;
use crate::core::v2::pathfinding::PathfindingResult;
use crate::core::{AsCoreRequest, Json};
use crate::error::InternalError;
use crate::RollingStockModel;
use derivative::Derivative;
use editoast_schemas::primitives::Identifier;
Expand All @@ -22,6 +24,7 @@ use std::hash::Hash;
editoast_common::schemas! {
CompleteReportTrain,
ReportTrain,
SimulationResponse,
}

#[derive(Debug, Serialize, Derivative)]
Expand Down Expand Up @@ -224,13 +227,30 @@ pub struct SimulationRequest {
pub electrical_profile_set_id: Option<i64>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SimulationResponse {
pub base: ReportTrain,
pub provisional: ReportTrain,
pub final_output: CompleteReportTrain,
pub mrsp: Mrsp,
pub power_restrictions: Vec<SimulationPowerRestrictionRange>,
// We accepted the difference of memory size taken by variants
// Since there is only on success and others are error cases
#[derive(Serialize, Deserialize, Clone, Debug, ToSchema)]
#[serde(tag = "status", rename_all = "snake_case")]
#[allow(clippy::large_enum_variant)]
pub enum SimulationResponse {
Success {
#[schema(value_type = ReportTrainV2)]
base: ReportTrain,
#[schema(value_type = ReportTrainV2)]
provisional: ReportTrain,
#[schema(inline)]
final_output: CompleteReportTrain,
#[schema(inline)]
mrsp: Mrsp,
#[schema(inline)]
power_restrictions: Vec<SimulationPowerRestrictionRange>,
},
PathfindingFailed {
pathfinding_result: PathfindingResult,
},
SimulationFailed {
core_error: InternalError,
},
}

impl AsCoreRequest<Json<SimulationResponse>> for SimulationRequest {
Expand Down
4 changes: 2 additions & 2 deletions editoast/src/views/v2/timetable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use utoipa::ToSchema;
use crate::core::v2::conflict_detection::ConflictDetectionRequest;
use crate::core::v2::conflict_detection::CoreConflict;
use crate::core::v2::conflict_detection::TrainRequirements;
use crate::core::v2::simulation::SimulationResponse;
use crate::core::AsCoreRequest;
use crate::decl_paginated_response;
use crate::error::Result;
Expand All @@ -37,7 +38,6 @@ use crate::modelsv2::Update;
use crate::views::pagination::PaginatedResponse;
use crate::views::pagination::PaginationQueryParam;
use crate::views::v2::train_schedule::train_simulation_batch;
use crate::views::v2::train_schedule::SimulationResult;
use crate::CoreClient;
use crate::DbPool;
use crate::RedisClient;
Expand Down Expand Up @@ -320,7 +320,7 @@ pub async fn conflicts(
continue;
};
let final_output = match sim {
SimulationResult::Success { final_output, .. } => final_output,
SimulationResponse::Success { final_output, .. } => final_output,
_ => continue,
};
trains_requirements.insert(
Expand Down
Loading

0 comments on commit 6199e2b

Please sign in to comment.