diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index d703f10f55e..ce8c0acf466 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -2411,6 +2411,14 @@ paths: Enforces that the path used by the train should be free and available at least that many milliseconds before its passage. minimum: 0 + tonnage: + type: number + format: double + nullable: true + total_length: + type: number + format: double + nullable: true work_schedule_group_id: type: integer format: int64 @@ -8217,6 +8225,14 @@ components: Enforces that the path used by the train should be free and available at least that many milliseconds before its passage. minimum: 0 + tonnage: + type: number + format: double + nullable: true + total_length: + type: number + format: double + nullable: true work_schedule_group_id: type: integer format: int64 diff --git a/editoast/src/core/simulation.rs b/editoast/src/core/simulation.rs index 01f49fb3fd2..99ccf1f247b 100644 --- a/editoast/src/core/simulation.rs +++ b/editoast/src/core/simulation.rs @@ -66,6 +66,51 @@ pub struct PhysicsRollingStock { pub raise_pantograph_time: Option, } +pub struct SimulationParameters { + pub tonnage: Option, + pub total_length: Option, +} + +impl PhysicsRollingStock { + pub fn new( + traction_engine: RollingStockModel, + params: SimulationParameters, + ) -> Self { + let traction_engine_length = traction_engine.length * 1000.0; // RollingStockModel length is in km, convert to m + let length = params + .total_length // Simulation params is in m + .unwrap_or_else(|| traction_engine_length) + .round() as u64; + + let traction_engine_mass = traction_engine.mass; // RollingStockModel mass is in kg + let params_mass = params.tonnage.map(|m|m * 1000.0); // Simulation params is in tons, convert to kg + let mass = params_mass.unwrap_or_else(|| { + traction_engine_mass + }).round() as u64; + + Self { + effort_curves: traction_engine.effort_curves, + base_power_class: traction_engine.base_power_class, + length, + mass, + max_speed: traction_engine.max_speed, + startup_time: (traction_engine.startup_time * 1000.0).round() as u64, + startup_acceleration: traction_engine.startup_acceleration, + comfort_acceleration: traction_engine.comfort_acceleration, + gamma: traction_engine.gamma, + inertia_coefficient: traction_engine.inertia_coefficient, + rolling_resistance: traction_engine.rolling_resistance, + power_restrictions: traction_engine.power_restrictions.into_iter().collect(), + electrical_power_startup_time: traction_engine + .electrical_power_startup_time + .map(|v| (v * 1000.0).round() as u64), + raise_pantograph_time: traction_engine + .raise_pantograph_time + .map(|v| (v * 1000.0).round() as u64), + } + } +} + impl From for PhysicsRollingStock { fn from(value: RollingStockModel) -> Self { Self { diff --git a/editoast/src/views/timetable/stdcm.rs b/editoast/src/views/timetable/stdcm.rs index b98df4c0e1f..092ce35fc25 100644 --- a/editoast/src/views/timetable/stdcm.rs +++ b/editoast/src/views/timetable/stdcm.rs @@ -25,6 +25,8 @@ use utoipa::ToSchema; use super::SelectionSettings; use crate::core::pathfinding::PathfindingResult; +use crate::core::simulation::PhysicsRollingStock; +use crate::core::simulation::SimulationParameters; use crate::core::simulation::{RoutingRequirement, SimulationResponse, SpacingRequirement}; use crate::core::stdcm::STDCMResponse; use crate::core::stdcm::TrainRequirement; @@ -109,6 +111,17 @@ pub struct STDCMRequestPayload { #[serde(default)] #[schema(value_type = Option, example = json!(["5%", "2min/100km"]))] margin: Option, + tonnage: Option, + total_length: Option, +} + +impl From for SimulationParameters { + fn from(payload: STDCMRequestPayload) -> Self { + Self { + tonnage: payload.tonnage, + total_length: payload.total_length, + } + } } #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)] @@ -247,9 +260,12 @@ async fn stdcm( // 5. Build STDCM request let stdcm_response = STDCMRequest { + rolling_stock: PhysicsRollingStock::new( + rolling_stock.clone(), + stdcm_request.clone().into(), + ), infra: infra.id, expected_version: infra.version, - rolling_stock: rolling_stock.clone().into(), rolling_stock_loading_gauge: rolling_stock.loading_gauge, rolling_stock_supported_signaling_systems: rolling_stock .supported_signaling_systems diff --git a/front/src/applications/stdcm/utils/formatStdcmConfV2.ts b/front/src/applications/stdcm/utils/formatStdcmConfV2.ts index ea13f256780..7ab42529877 100644 --- a/front/src/applications/stdcm/utils/formatStdcmConfV2.ts +++ b/front/src/applications/stdcm/utils/formatStdcmConfV2.ts @@ -26,6 +26,8 @@ type ValidStdcmConfig = { rollingStockComfort: TrainScheduleBase['comfort']; path: PathfindingItem[]; speedLimitByTag?: string; + tonnage?: number; + totalLength?: number; maximumRunTime?: number; startTime?: string; // must be a datetime latestStartTime?: string; @@ -224,6 +226,8 @@ export const checkStdcmConf = ( maximumRunTime, }), speedLimitByTag, + tonnage: osrdconf.tonnage, + totalLength: osrdconf.totalLength, margin: standardStdcmAllowance, gridMarginBefore, gridMarginAfter, @@ -246,6 +250,8 @@ export const formatStdcmPayload = ( margin: createMargin(validConfig.margin), rolling_stock_id: validConfig.rollingStockId, speed_limit_tags: validConfig.speedLimitByTag, + tonnage: validConfig.tonnage, + total_length: validConfig.totalLength, ...(!stdcmV2Activated && { maximum_run_time: toMsOrUndefined(validConfig.maximumRunTime), maximum_departure_delay: sec2ms( diff --git a/front/src/applications/stdcmV2/components/StdcmConsist.tsx b/front/src/applications/stdcmV2/components/StdcmConsist.tsx index 17bdc3e8542..820b6e7c240 100644 --- a/front/src/applications/stdcmV2/components/StdcmConsist.tsx +++ b/front/src/applications/stdcmV2/components/StdcmConsist.tsx @@ -1,10 +1,12 @@ import { useEffect } from 'react'; import { Input, ComboBox } from '@osrd-project/ui-core'; +import { isNil } from 'lodash'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import type { LightRollingStockWithLiveries } from 'common/api/osrdEditoastApi'; -import { useOsrdConfActions } from 'common/osrdContext'; +import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; import SpeedLimitByTagSelector from 'common/SpeedLimitByTagSelector/SpeedLimitByTagSelector'; import { useStoreDataForSpeedLimitByTagSelector } from 'common/SpeedLimitByTagSelector/useStoreDataForSpeedLimitByTagSelector'; import RollingStock2Img from 'modules/rollingStock/components/RollingStock2Img'; @@ -35,9 +37,28 @@ const StdcmConsist = ({ setCurrentSimulationInputs, disabled = false }: StdcmCon const { speedLimitByTag, speedLimitsByTags, dispatchUpdateSpeedLimitByTag } = useStoreDataForSpeedLimitByTagSelector(); - const { updateRollingStockID } = useOsrdConfActions() as StdcmConfSliceActions; + const { updateRollingStockID, updateTonnage, updateTotalLength } = + useOsrdConfActions() as StdcmConfSliceActions; const dispatch = useAppDispatch(); + const { getTonnage, getTotalLength } = useOsrdConfSelectors(); + const tonnage = useSelector(getTonnage); + const totalLength = useSelector(getTotalLength); + const onTonnageChange = (e: React.ChangeEvent) => { + // TODO better handling of number in input component + const tonnageValue = parseInt(e.target.value, 10); + if (!isNil(tonnageValue)) { + dispatch(updateTonnage(tonnageValue)); + } + }; + const onTotalLengthChange = (e: React.ChangeEvent) => { + // TODO better handling of number in input component + const totalLengthValue = parseInt(e.target.value, 10); + if (!isNil(totalLengthValue)) { + dispatch(updateTotalLength(totalLengthValue)); + } + }; + const { rollingStock } = useStoreDataForRollingStockSelector(); const { filters, searchRollingStock, searchRollingStockById, filteredRollingStockList } = @@ -125,8 +146,24 @@ const StdcmConsist = ({ setCurrentSimulationInputs, disabled = false }: StdcmCon />
- - + +
extends InfraStateReducers ['updateElectricalProfileSetId']: CaseReducer>; ['updateRollingStockID']: CaseReducer>; ['updateSpeedLimitByTag']: CaseReducer>; + ['updateTonnage']: CaseReducer>; + ['updateTotalLength']: CaseReducer>; ['updateInitialSpeed']: CaseReducer>; ['updateOriginTime']: CaseReducer>; ['updateOriginUpperBoundTime']: CaseReducer>; @@ -159,6 +161,12 @@ export function buildCommonConfReducers(): CommonConfRe updateSpeedLimitByTag(state: Draft, action: PayloadAction) { state.speedLimitByTag = action.payload === null ? undefined : action.payload; }, + updateTonnage(state: Draft, action: PayloadAction) { + state.tonnage = action.payload === null ? undefined : action.payload; + }, + updateTotalLength(state: Draft, action: PayloadAction) { + state.totalLength = action.payload === null ? undefined : action.payload; + }, updateInitialSpeed(state: Draft, action: PayloadAction) { state.initialSpeed = action.payload; }, diff --git a/front/src/reducers/osrdconf/osrdConfCommon/selectors.ts b/front/src/reducers/osrdconf/osrdConfCommon/selectors.ts index 9dcb3ae4945..4d1957bcda6 100644 --- a/front/src/reducers/osrdconf/osrdConfCommon/selectors.ts +++ b/front/src/reducers/osrdconf/osrdConfCommon/selectors.ts @@ -42,6 +42,8 @@ const buildCommonConfSelectors = ( getSearchDatetimeWindow: makeOsrdConfSelector('searchDatetimeWindow'), getRollingStockID: makeOsrdConfSelector('rollingStockID'), getSpeedLimitByTag: makeOsrdConfSelector('speedLimitByTag'), + getTonnage: makeOsrdConfSelector('tonnage'), + getTotalLength: makeOsrdConfSelector('totalLength'), getInitialSpeed: makeOsrdConfSelector('initialSpeed'), getOriginDate: makeOsrdConfSelector('originDate'), getOriginTime: makeOsrdConfSelector('originTime'), diff --git a/front/src/reducers/osrdconf/types.ts b/front/src/reducers/osrdconf/types.ts index 8b501a0adc3..734be50c4e8 100644 --- a/front/src/reducers/osrdconf/types.ts +++ b/front/src/reducers/osrdconf/types.ts @@ -29,6 +29,8 @@ export interface OsrdConfState extends InfraState { searchDatetimeWindow?: { begin: Date; end: Date }; rollingStockID?: number; speedLimitByTag?: string; + tonnage?: number; + totalLength?: number; powerRestriction: PowerRestriction[]; initialSpeed?: number; originDate?: string;