Skip to content

Commit

Permalink
editoast: timetable-import: return timings
Browse files Browse the repository at this point in the history
for pathfinding and simulation
  • Loading branch information
Baptiste Prevot authored and Castavo committed Feb 12, 2024
1 parent 2b434ef commit eee07b8
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 82 deletions.
39 changes: 28 additions & 11 deletions editoast/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,17 @@ components:
Identifier:
description: A wrapper around a String that ensures that the string is not empty and not longer than 255 characters.
type: string
ImportTimings:
properties:
pathfinding:
format: double
nullable: true
type: number
simulation:
format: double
nullable: true
type: number
type: object
Infra:
properties:
created:
Expand Down Expand Up @@ -3451,15 +3462,6 @@ components:
required:
- location
type: object
TimetableImportReport:
properties:
errors:
additionalProperties:
$ref: '#/components/schemas/TimetableImportError'
type: object
required:
- errors
type: object
TimetableImportTrain:
properties:
departure_time:
Expand Down Expand Up @@ -3514,6 +3516,17 @@ components:
- begin
- end
type: object
TrainImportReport:
properties:
error:
allOf:
- $ref: '#/components/schemas/TimetableImportError'
nullable: true
timings:
$ref: '#/components/schemas/ImportTimings'
required:
- timings
type: object
TrainSchedule:
properties:
allowances:
Expand Down Expand Up @@ -6076,7 +6089,9 @@ paths:
tags:
- timetable
post:
description: Import a timetable
description: |-
Import a timetable
Returns data about the import for each train imported
parameters:
- description: Timetable id
in: path
Expand All @@ -6100,7 +6115,9 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/TimetableImportReport'
additionalProperties:
$ref: '#/components/schemas/TrainImportReport'
type: object
description: Import report
summary: Import a timetable
tags:
Expand Down
26 changes: 8 additions & 18 deletions editoast/src/views/pathfinding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,34 +434,24 @@ async fn call_core_pf_and_save_result(
.with_waypoints(&mut waypoints)
.with_rolling_stocks(&mut rolling_stocks);
let steps_duration = payload.steps.iter().map(|step| step.duration).collect();
run_pathfinding(
&path_request,
core.as_ref(),
conn,
infra_id,
update_id,
steps_duration,
)
.await
let path_response = path_request.fetch(&core).await?;
save_core_pathfinding(path_response, conn, infra_id, update_id, steps_duration).await
}

/// Run a pathfinding request and store the result in the DB
/// Turn a core pathfinding response into a [Pathfinding] model and store it in the DB
/// If `update_id` is provided then update the corresponding path instead of creating a new one
pub async fn run_pathfinding(
path_request: &CorePathfindingRequest,
core: &CoreClient,
pub async fn save_core_pathfinding(
core_response: PathfindingResponse,
conn: &mut PgConnection,
infra_id: i64,
update_id: Option<i64>,
steps_duration: Vec<f64>,
) -> Result<Pathfinding> {
assert_eq!(steps_duration.len(), path_request.nb_waypoints());
let response = path_request.fetch(core).await?;
let response_track_map = response.fetch_track_map(infra_id, conn).await?;
let response_op_map = response.fetch_op_map(infra_id, conn).await?;
let response_track_map = core_response.fetch_track_map(infra_id, conn).await?;
let response_op_map = core_response.fetch_op_map(infra_id, conn).await?;
let pathfinding = Pathfinding::from_core_response(
steps_duration,
response,
core_response,
&response_track_map,
&response_op_map,
)?;
Expand Down
126 changes: 84 additions & 42 deletions editoast/src/views/timetable/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::models::{
};
use crate::schema::rolling_stock::{RollingStock, RollingStockComfortType};
use crate::views::infra::{call_core_infra_state, InfraState};
use crate::views::pathfinding::run_pathfinding;
use crate::views::pathfinding::save_core_pathfinding;
use crate::views::train_schedule::process_simulation_response;
use actix_web::web::Json;
use chrono::Timelike;
Expand All @@ -36,7 +36,8 @@ crate::schemas! {
TimetableImportPathSchedule,
TimetableImportPathLocation,
TimetableImportTrain,
TimetableImportReport,
TrainImportReport,
ImportTimings,
TimetableImportError,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
Expand All @@ -52,10 +53,34 @@ impl TimetableImportItem {
pub fn report_error(
&self,
error: TimetableImportError,
) -> HashMap<String, TimetableImportError> {
timings: ImportTimings,
) -> HashMap<String, TrainImportReport> {
self.trains
.iter()
.map(|train| (train.name.clone(), TrainImportReport::new(error.clone())))
.map(|train| {
(
train.name.clone(),
TrainImportReport {
error: Some(error.clone()),
timings: timings.clone(),
},
)
})
.collect()
}

pub fn report_success(&self, timings: ImportTimings) -> HashMap<String, TrainImportReport> {
self.trains
.iter()
.map(|train| {
(
train.name.clone(),
TrainImportReport {
error: None,
timings: timings.clone(),
},
)
})
.collect()
}
}
Expand Down Expand Up @@ -86,8 +111,15 @@ pub enum TimetableImportPathLocation {
}

#[derive(Debug, Serialize, ToSchema)]
struct TimetableImportReport {
errors: HashMap<String, TimetableImportError>,
pub struct TrainImportReport {
error: Option<TimetableImportError>,
timings: ImportTimings,
}

#[derive(Debug, Serialize, ToSchema, Default, Clone)]
pub struct ImportTimings {
pub pathfinding: Option<f64>,
pub simulation: Option<f64>,
}

#[derive(Debug, Clone, Serialize, ToSchema)]
Expand Down Expand Up @@ -121,13 +153,14 @@ struct OperationalPointsToParts {
}

/// Import a timetable
/// Returns data about the import for each train imported
#[utoipa::path(
tag = "timetable",
params(
("id" = u64, Path, description = "Timetable id"),
),
responses(
(status = 200, description = "Import report", body = TimetableImportReport),
(status = 200, description = "Import report", body = HashMap<String, TrainImportReport>),
),
)]
#[post("")]
Expand All @@ -136,7 +169,7 @@ pub async fn post_timetable(
timetable_id: Path<i64>,
core_client: Data<CoreClient>,
data: Json<Vec<TimetableImportItem>>,
) -> Result<Json<TimetableImportReport>> {
) -> Result<Json<HashMap<String, TrainImportReport>>> {
let timetable_id = timetable_id.into_inner();
let mut conn = db_pool.get().await?;

Expand All @@ -161,10 +194,10 @@ pub async fn post_timetable(
return Err(TimetableError::InfraNotLoaded { infra_id }.into());
}

let mut errors = HashMap::new();
let mut reports = HashMap::new();

for item in data.into_inner() {
errors.extend(
reports.extend(
import_item(
infra_id,
&infra_version,
Expand All @@ -177,7 +210,15 @@ pub async fn post_timetable(
);
}

Ok(Json(TimetableImportReport { errors }))
Ok(Json(reports))
}

macro_rules! time_execution {
($s:expr) => {{
let timer = std::time::Instant::now();
let res = $s;
(res, timer.elapsed().as_secs_f64())
}};
}

async fn import_item(
Expand All @@ -187,15 +228,17 @@ async fn import_item(
import_item: TimetableImportItem,
timetable_id: i64,
core_client: &CoreClient,
) -> Result<HashMap<String, TimetableImportError>> {
) -> Result<HashMap<String, TrainImportReport>> {
let mut timings = ImportTimings::default();
let Some(rolling_stock_model) =
RollingStockModel::retrieve_by_name(conn, import_item.rolling_stock.clone()).await?
else {
return Ok(
import_item.report_error(TimetableImportError::RollingStockNotFound {
return Ok(import_item.report_error(
TimetableImportError::RollingStockNotFound {
name: import_item.rolling_stock.clone(),
}),
);
},
timings.clone(),
));
};
let rolling_stock_id = rolling_stock_model.id.unwrap();
let rollingstock_version = rolling_stock_model.version;
Expand All @@ -214,7 +257,7 @@ async fn import_item(
let op_to_parts = match find_operation_points(&ops_uic, &ops_id, infra_id, conn).await? {
Ok(op_to_parts) => op_to_parts,
Err(err) => {
return Ok(import_item.report_error(err.clone()));
return Ok(import_item.report_error(err.clone(), timings.clone()));
}
};
// Create waypoints
Expand All @@ -224,36 +267,32 @@ async fn import_item(
// Run pathfinding
// TODO: Stops duration should be associated to trains not path
let steps_duration = vec![0.; pf_request.nb_waypoints()];
let path_response = match run_pathfinding(
&pf_request,
core_client,
conn,
infra_id,
None,
steps_duration,
)
.await
{
Ok(path_response) => path_response,
let (raw_path_response, elapsed) = time_execution!(pf_request.fetch(core_client).await);
timings.pathfinding = Some(elapsed);
let pathfinding_result = match raw_path_response {
Err(error) => {
return Ok(
import_item.report_error(TimetableImportError::PathfindingError {
return Ok(import_item.report_error(
TimetableImportError::PathfindingError {
cause: error.clone(),
}),
);
},
timings.clone(),
));
}
Ok(raw_path_response) => {
save_core_pathfinding(raw_path_response, conn, infra_id, None, steps_duration).await?
}
};
let path_id = path_response.id;
let path_id = pathfinding_result.id;

let waypoint_offsets: Vec<_> = path_response
let waypoint_offsets: Vec<_> = pathfinding_result
.payload
.path_waypoints
.iter()
.filter(|pw| !pw.suggestion)
.map(|pw| pw.path_offset)
.collect();

let mut fake_stops: Vec<_> = path_response
let mut fake_stops: Vec<_> = pathfinding_result
.payload
.path_waypoints
.clone()
Expand All @@ -274,17 +313,20 @@ async fn import_item(
&mut fake_stops,
&rolling_stock,
infra_id,
path_response,
pathfinding_result,
);
// Run the simulation
let response_payload = match request.fetch(core_client).await {
let (response_payload, elapsed) = time_execution!(request.fetch(core_client).await);
timings.simulation = Some(elapsed);
let response_payload = match response_payload {
Ok(response_payload) => response_payload,
Err(error) => {
return Ok(
import_item.report_error(TimetableImportError::SimulationError {
return Ok(import_item.report_error(
TimetableImportError::SimulationError {
cause: error.clone(),
}),
);
},
timings.clone(),
));
}
};

Expand Down Expand Up @@ -313,7 +355,7 @@ async fn import_item(
sim_output.create_conn(conn).await?;
}

Ok(HashMap::new())
Ok(import_item.report_success(timings))
}

async fn find_operation_points(
Expand Down
15 changes: 10 additions & 5 deletions front/src/common/api/osrdEditoastApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,9 @@ export type GetTimetableByIdApiArg = {
/** Timetable id */
id: number;
};
export type PostTimetableByIdApiResponse = /** status 200 Import report */ TimetableImportReport;
export type PostTimetableByIdApiResponse = /** status 200 Import report */ {
[key: string]: TrainImportReport;
};
export type PostTimetableByIdApiArg = {
/** Timetable id */
id: number;
Expand Down Expand Up @@ -2319,10 +2321,13 @@ export type TimetableImportError =
cause: InternalError;
};
};
export type TimetableImportReport = {
errors: {
[key: string]: TimetableImportError;
};
export type ImportTimings = {
pathfinding?: number | null;
simulation?: number | null;
};
export type TrainImportReport = {
error?: TimetableImportError | null;
timings: ImportTimings;
};
export type TimetableImportPathLocation =
| {
Expand Down
Loading

0 comments on commit eee07b8

Please sign in to comment.