Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: stdcm consist form validation #9574

Merged
merged 2 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion front/public/locales/en/stdcm.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,21 @@
"tonnage": "Tonnage",
"maxSpeed": "Max speed",
"tractionEngine": "Traction engine",
"towedRollingStock": "Towed rolling stock"
"towedRollingStock": "Towed rolling stock",
"errors": {
"totalMass": {
"negative": "Must be positive.",
"range": "The total weight must be between {{low}} and {{high}}t"
},
"totalLength": {
"negative": "Must be positive.",
"range": "The total length must be between {{low}} and {{high}}m"
},
"maxSpeed": {
"negative": "Must be positive.",
"range": "The max speed must be between {{low}} and {{high}}km/h"
}
}
},
"datetimeOutsideWindow": "Date must be between {{low}} and {{high}}",
"debug": {
Expand Down
16 changes: 15 additions & 1 deletion front/public/locales/fr/stdcm.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,21 @@
"tonnage": "Tonnage",
"maxSpeed": "Vitesse max.",
"tractionEngine": "Engin de traction",
"towedRollingStock": "Matériel remorqué"
"towedRollingStock": "Matériel remorqué",
"errors": {
"totalMass": {
"negative": "Doit être positif.",
"range": "Le tonnage total doit être compris entre {{low}} et {{high}}t"
},
"totalLength": {
"negative": "Doit être positif.",
"range": "La longueur totale doit être comprise entre {{low}} et {{high}}m"
},
"maxSpeed": {
"negative": "Doit être positif.",
"range": "La vitesse max. doit être comprise entre {{low}} et {{high}}km/h"
}
}
},
"datetimeOutsideWindow": "La date et l'heure doivent être comprises entre le {{low}} et le {{high}}",
"debug": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import cx from 'classnames';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import useStdcmTowedRollingStock from 'applications/stdcm/hooks/useStdcmTowedRollingStock';
import { extractMarkersInfo } from 'applications/stdcm/utils';
import {
validateMaxSpeed,
validateTotalLength,
validateTotalMass,
} from 'applications/stdcm/utils/consistValidation';
import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext';
import useInfraStatus from 'modules/pathfinding/hooks/useInfraStatus';
import { useStoreDataForRollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector';
import { Map } from 'modules/trainschedule/components/ManageTrainSchedule';
import { type StdcmConfSliceActions, resetMargins } from 'reducers/osrdconf/stdcmConf';
import type { StdcmConfSelectors } from 'reducers/osrdconf/stdcmConf/selectors';
Expand Down Expand Up @@ -61,6 +68,9 @@ const StdcmConfig = ({
getProjectID,
getScenarioID,
getStudyID,
getTotalMass,
getTotalLength,
getMaxSpeed,
} = useOsrdConfSelectors() as StdcmConfSelectors;
const origin = useSelector(getStdcmOrigin);
const pathSteps = useSelector(getStdcmPathSteps);
Expand All @@ -69,11 +79,38 @@ const StdcmConfig = ({
const studyID = useSelector(getStudyID);
const scenarioID = useSelector(getScenarioID);

const totalMass = useSelector(getTotalMass);
const totalLength = useSelector(getTotalLength);
const maxSpeed = useSelector(getMaxSpeed);

const pathfinding = useStaticPathfinding(infra);
const formRef = useRef<HTMLDivElement>(null);

const [formErrors, setFormErrors] = useState<StdcmConfigErrors>();

const { rollingStock } = useStoreDataForRollingStockSelector();
const towedRollingStock = useStdcmTowedRollingStock();

const consistErrors = useMemo(() => {
const totalMassError = validateTotalMass({
tractionEngineMass: rollingStock?.mass,
towedMass: towedRollingStock?.mass,
totalMass,
});

const totalLengthError = validateTotalLength({
tractionEngineLength: rollingStock?.length,
towedLength: towedRollingStock?.length,
totalLength,
});

return {
totalMass: totalMassError,
totalLength: totalLengthError,
maxSpeed: validateMaxSpeed(maxSpeed, rollingStock?.max_speed),
};
}, [rollingStock, towedRollingStock, totalMass, totalLength, maxSpeed]);

const disabled = isPending || retainedSimulationIndex > -1;

const markersInfo = useMemo(() => extractMarkersInfo(pathSteps), [pathSteps]);
Expand Down Expand Up @@ -146,7 +183,7 @@ const StdcmConfig = ({
/>
<div className="stdcm-simulation-inputs">
<div className="stdcm-consist-container">
<StdcmConsist disabled={disabled} />
<StdcmConsist consistErrors={consistErrors} disabled={disabled} />
</div>
<div className="stdcm__separator" />
<div ref={formRef} className="stdcm-simulation-itinerary">
Expand Down Expand Up @@ -175,7 +212,10 @@ const StdcmConfig = ({
isDisabled={
disabled ||
!showBtnToLaunchSimulation ||
formErrors?.errorType === StdcmConfigErrorTypes.INFRA_NOT_LOADED
formErrors?.errorType === StdcmConfigErrorTypes.INFRA_NOT_LOADED ||
!!consistErrors.totalMass ||
!!consistErrors.totalLength ||
!!consistErrors.maxSpeed
}
/>
{formErrors && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { Input, ComboBox } from '@osrd-project/ui-core';
import { useTranslation } from 'react-i18next';

import useStdcmTowedRollingStock from 'applications/stdcm/hooks/useStdcmTowedRollingStock';
import {
CONSIST_MAX_SPEED_MIN,
CONSIST_TOTAL_LENGTH_MAX,
CONSIST_TOTAL_MASS_MAX,
} from 'applications/stdcm/utils/consistValidation';
import type { LightRollingStockWithLiveries, TowedRollingStock } from 'common/api/osrdEditoastApi';
import { useOsrdConfActions } from 'common/osrdContext';
import SpeedLimitByTagSelector from 'common/SpeedLimitByTagSelector/SpeedLimitByTagSelector';
Expand All @@ -14,6 +19,7 @@ import useFilterRollingStock from 'modules/rollingStock/hooks/useFilterRollingSt
import useFilterTowedRollingStock from 'modules/towedRollingStock/hooks/useFilterTowedRollingStock';
import { type StdcmConfSliceActions } from 'reducers/osrdconf/stdcmConf';
import { useAppDispatch } from 'store';
import { kgToT, kmhToMs, msToKmh } from 'utils/physics';

import StdcmCard from './StdcmCard';
import useStdcmConsist from '../../hooks/useStdcmConsist';
Expand All @@ -33,7 +39,7 @@ const ConsistCardTitle = ({
);
};

const StdcmConsist = ({ disabled = false }: StdcmConfigCardProps) => {
const StdcmConsist = ({ consistErrors = {}, disabled = false }: StdcmConfigCardProps) => {
const { t } = useTranslation('stdcm');
const { speedLimitByTag, speedLimitsByTags, dispatchUpdateSpeedLimitByTag } =
useStoreDataForSpeedLimitByTagSelector({ isStdcm: true });
Expand Down Expand Up @@ -166,6 +172,20 @@ const StdcmConsist = ({ disabled = false }: StdcmConfigCardProps) => {
value={totalMass ?? ''}
onChange={onTotalMassChange}
disabled={disabled}
statusWithMessage={
consistErrors?.totalMass
? {
status: 'error',
tooltip: 'left',
message: t(consistErrors.totalMass, {
low: Math.floor(
kgToT((rollingStock?.mass ?? 0) + (towedRollingStock?.mass ?? 0))
),
high: CONSIST_TOTAL_MASS_MAX,
}),
}
: undefined
}
/>
<Input
id="length"
Expand All @@ -176,6 +196,18 @@ const StdcmConsist = ({ disabled = false }: StdcmConfigCardProps) => {
value={totalLength ?? ''}
onChange={onTotalLengthChange}
disabled={disabled}
statusWithMessage={
consistErrors?.totalLength
? {
status: 'error',
tooltip: 'right',
message: t(consistErrors.totalLength, {
low: Math.floor((rollingStock?.length ?? 0) + (towedRollingStock?.length ?? 0)),
high: CONSIST_TOTAL_LENGTH_MAX,
}),
}
: undefined
}
/>
</div>
<div className="stdcm-consist__properties">
Expand All @@ -194,6 +226,20 @@ const StdcmConsist = ({ disabled = false }: StdcmConfigCardProps) => {
value={maxSpeed ?? ''}
onChange={onMaxSpeedChange}
disabled={disabled}
statusWithMessage={
consistErrors?.maxSpeed
? {
status: 'error',
tooltip: 'right',
message: t(consistErrors.maxSpeed, {
low: CONSIST_MAX_SPEED_MIN,
high: Math.floor(
msToKmh(Math.min(rollingStock?.max_speed ?? kmhToMs(CONSIST_MAX_SPEED_MIN)))
),
}),
}
: undefined
}
/>
</div>
</StdcmCard>
Expand Down
14 changes: 2 additions & 12 deletions front/src/applications/stdcm/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,6 @@ export const STDCM_REQUEST_STATUS = Object.freeze({

export const STDCM_TRAIN_ID = -10;

export const COMPOSITION_CODES = [
'HLP',
'MA100',
'MA80',
'MA90',
'ME100',
'ME120',
'ME140',
'MV160',
'MVGV',
];

export const COMPOSITION_CODES_MAX_SPEEDS: Record<string, number | undefined> = {
MA80: 80,
MA90: 90,
Expand All @@ -32,3 +20,5 @@ export const COMPOSITION_CODES_MAX_SPEEDS: Record<string, number | undefined> =
HLP: 100,
MVGV: 200,
};

export const COMPOSITION_CODES = Object.keys(COMPOSITION_CODES_MAX_SPEEDS);
7 changes: 7 additions & 0 deletions front/src/applications/stdcm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export type StdcmResultsOperationalPoint = {
trackName?: string;
};

export type ConsistErrors = {
totalMass?: string;
totalLength?: string;
maxSpeed?: string;
};

export type StdcmResults = {
stdcmResponse: StdcmSuccessResponse;
speedSpaceChartData: SpeedSpaceChartData | null;
Expand Down Expand Up @@ -145,6 +151,7 @@ export type StdcmSimulation = {
/** This type is used for StdcmConsist, StdcmOrigin, StdcmDestination and StdcmVias components */
export type StdcmConfigCardProps = {
disabled?: boolean;
consistErrors?: ConsistErrors;
};

export enum ArrivalTimeTypes {
Expand Down
78 changes: 78 additions & 0 deletions front/src/applications/stdcm/utils/consistValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { kgToT, msToKmh } from 'utils/physics';

export const CONSIST_TOTAL_MASS_MAX = 10000; // ton
export const CONSIST_TOTAL_LENGTH_MAX = 750; // m
export const CONSIST_MAX_SPEED_MIN = 30; // km/h

export const validateTotalMass = ({
tractionEngineMass = 0,
towedMass = 0,
totalMass,
}: {
tractionEngineMass?: number;
towedMass?: number;
totalMass?: number;
}) => {
if (!totalMass) {
Wadjetz marked this conversation as resolved.
Show resolved Hide resolved
return undefined;
}

if (totalMass <= 0) {
return 'consist.errors.totalMass.negative';
}

const tractionMassInTons = kgToT(tractionEngineMass);
const consistMassInTons = kgToT(tractionEngineMass + towedMass);
const massLimit = towedMass ? consistMassInTons : tractionMassInTons;

if (totalMass < massLimit || totalMass >= CONSIST_TOTAL_MASS_MAX) {
return 'consist.errors.totalMass.range';
}

return undefined;
};

export const validateTotalLength = ({
tractionEngineLength = 0,
towedLength = 0,
totalLength,
}: {
tractionEngineLength?: number;
towedLength?: number;
totalLength?: number;
}) => {
if (!totalLength) {
return undefined;
}

if (totalLength <= 0) {
return 'consist.errors.totalLength.negative';
}

const consistLength = Math.floor(tractionEngineLength + towedLength);

if (totalLength < consistLength || totalLength >= CONSIST_TOTAL_LENGTH_MAX) {
return 'consist.errors.totalLength.range';
}

return undefined;
};

export const validateMaxSpeed = (maxSpeed?: number, tractionEngineMaxSpeed?: number) => {
if (!maxSpeed) {
return undefined;
}

if (maxSpeed <= 0) {
return 'consist.errors.maxSpeed.negative';
}

if (
maxSpeed < CONSIST_MAX_SPEED_MIN ||
(tractionEngineMaxSpeed && maxSpeed > Math.floor(msToKmh(tractionEngineMaxSpeed)))
) {
return 'consist.errors.maxSpeed.range';
}

return undefined;
};
13 changes: 9 additions & 4 deletions front/src/styles/scss/applications/stdcm/_card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
background-color: rgba(255, 255, 255, 1);
height: fit-content;
position: relative;
display: flex;
flex-direction: column;
gap: 5px;

.stdcm-default-card-icon {
color: #1844ef;
Expand Down Expand Up @@ -89,7 +92,9 @@
}

.feed-back {
padding: 0;
--field-wrapper-padding-bottom: 9px;
emersion marked this conversation as resolved.
Show resolved Hide resolved
padding-top: 3px;
padding-bottom: 3px;
}

.date-picker {
Expand Down Expand Up @@ -121,7 +126,8 @@

// cards styles
&.consist {
padding-left: 24px;
padding-left: 11px;
padding-right: 11px;
padding-bottom: 15px;

.towed-rolling-stock {
Expand All @@ -130,9 +136,8 @@

.stdcm-consist__properties {
display: grid;
grid-template-columns: 119px 127px;
grid-template-columns: 139px 147px;
justify-content: space-between;
padding-block: 8px;

.osrd-config-item {
padding-right: 3px;
Expand Down
Loading
Loading