diff --git a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx b/src/components/dialogs/limits/limits-groups-contextual-menu.tsx
index da0c4a94ac..dee7c961ea 100644
--- a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx
+++ b/src/components/dialogs/limits/limits-groups-contextual-menu.tsx
@@ -80,28 +80,31 @@ export function LimitsGroupsContextualMenu({
};
const handleDuplicateTab = () => {
+ let newName: string = '';
if (indexSelectedLimitSet1 !== null) {
- const duplicatedLimits1 = getValues(
+ const duplicatedLimits1: OperationalLimitsGroup = getValues(
`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}[${indexSelectedLimitSet1}]`
);
+ newName = duplicatedLimits1.id + '_COPY';
const newLimitsGroup1: OperationalLimitsGroup = {
...duplicatedLimits1,
- [ID]: '',
+ [ID]: newName,
};
appendToLimitsGroups1(newLimitsGroup1);
}
if (indexSelectedLimitSet2 !== null) {
- const duplicatedLimits2 = getValues(
+ const duplicatedLimits2: OperationalLimitsGroup = getValues(
`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_2}[${indexSelectedLimitSet2}]`
);
+ newName = duplicatedLimits2.id + '_COPY';
const newLimitsGroup2: OperationalLimitsGroup = {
...duplicatedLimits2,
- [ID]: '',
+ [ID]: newName,
};
appendToLimitsGroups2(newLimitsGroup2);
}
- startEditingLimitsGroup(getValues(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}`).length - 1, '');
+ startEditingLimitsGroup(getValues(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}`).length - 1, newName);
};
return (
diff --git a/src/components/dialogs/limits/operational-limits-groups-tabs.tsx b/src/components/dialogs/limits/operational-limits-groups-tabs.tsx
index 72ad017837..7f6f566b26 100644
--- a/src/components/dialogs/limits/operational-limits-groups-tabs.tsx
+++ b/src/components/dialogs/limits/operational-limits-groups-tabs.tsx
@@ -306,7 +306,7 @@ export function OperationalLimitsGroupsTabs({
const newIndex: number = limitsGroups1.length;
appendEmptyOperationalLimitsGroup(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}`, '');
appendEmptyOperationalLimitsGroup(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_2}`, '');
- startEditingLimitsGroup(newIndex, `LIMIT_SET`);
+ startEditingLimitsGroup(newIndex, `DEFAULT`);
}
}, [
editingTabIndex,
@@ -356,10 +356,11 @@ export function OperationalLimitsGroupsTabs({
{(index === hoveredRowIndex || index === activatedByMenuTabIndex) && (
) =>
handleOpenMenu(e, index)
}
+ // during the naming of a limit set no other limit set manipulation is allowed :
+ disabled={editingTabIndex !== -1}
>
@@ -371,27 +372,31 @@ export function OperationalLimitsGroupsTabs({
/>
))}
-
+
)
}
+ sx={limitsStyles.limitsBackground}
/>
{
const lowBound = `${Math.trunc(limit.limitDuration.lowBound / 60)} min`;
@@ -67,7 +68,13 @@ const LimitReductionsTableForm: FunctionComponent<{
return columnsDefinition;
}, [intl, limits, getToolTipColumn]);
- return ;
+ return (
+
+ );
};
export default LimitReductionsTableForm;
diff --git a/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-cell.tsx b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-cell.tsx
new file mode 100644
index 0000000000..d582e57f51
--- /dev/null
+++ b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-cell.tsx
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { FunctionComponent } from 'react';
+import { IColumnsDef } from '../limitreductions/columns-definitions';
+import { TableCell } from '@mui/material';
+import { FloatInput, RawReadOnlyInput } from '@gridsuite/commons-ui';
+import { VOLTAGE_LEVEL } from '../../../../utils/field-constants';
+
+export const CustomVoltageLevelTableCell: FunctionComponent<{
+ formName: string;
+ rowIndex: number;
+ column: IColumnsDef;
+}> = ({ formName, rowIndex, column }) => {
+ return (
+
+ {column.dataKey === VOLTAGE_LEVEL ? (
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-row.tsx b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-row.tsx
new file mode 100644
index 0000000000..85604d8570
--- /dev/null
+++ b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table-row.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { TableRow } from '@mui/material';
+import { FunctionComponent } from 'react';
+
+import { CustomVoltageLevelTableCell } from './custom-voltage-level-table-cell';
+import { IColumnsDef } from '../limitreductions/columns-definitions';
+
+interface TableRowComponentProps {
+ formName: string;
+ columnsDefinition: IColumnsDef[];
+ index: number;
+}
+
+export const CustomVoltageLevelTableRow: FunctionComponent = ({
+ formName,
+ columnsDefinition,
+ index,
+}) => {
+ return (
+
+ {columnsDefinition.map((column: IColumnsDef) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/dialogs/parameters/common/limitreductions/limit-reductions-table.tsx b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table.tsx
similarity index 62%
rename from src/components/dialogs/parameters/common/limitreductions/limit-reductions-table.tsx
rename to src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table.tsx
index a989c56bd3..32bc06c17e 100644
--- a/src/components/dialogs/parameters/common/limitreductions/limit-reductions-table.tsx
+++ b/src/components/dialogs/parameters/common/voltage-level-table/custom-voltage-level-table.tsx
@@ -5,21 +5,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
-import { FunctionComponent } from 'react';
-import { IColumnsDef, LIMIT_REDUCTIONS_FORM } from './columns-definitions';
+import { FunctionComponent, useMemo } from 'react';
+import { IColumnsDef, LIMIT_REDUCTIONS_FORM } from '../limitreductions/columns-definitions';
import { useFieldArray } from 'react-hook-form';
-import LimitReductionTableRow from './limit-reduction-table-row';
+import LimitReductionTableRow from '../limitreductions/limit-reduction-table-row';
+import { CustomVoltageLevelTableRow } from './custom-voltage-level-table-row';
interface LimitReductionsTableProps {
columnsDefinition: IColumnsDef[];
tableHeight: number;
+ formName: string;
}
-const LimitReductionsTable: FunctionComponent = ({ columnsDefinition, tableHeight }) => {
+const CustomVoltageLevelTable: FunctionComponent = ({
+ formName,
+ columnsDefinition,
+ tableHeight,
+}) => {
const { fields: rows } = useFieldArray({
- name: LIMIT_REDUCTIONS_FORM,
+ name: formName,
});
+ const TableRowComponent = useMemo(
+ () => (formName === LIMIT_REDUCTIONS_FORM ? LimitReductionTableRow : CustomVoltageLevelTableRow),
+ [formName]
+ );
+
return (
= ({ co
{rows.map((row, index) => (
-
+
))}
@@ -54,4 +70,4 @@ const LimitReductionsTable: FunctionComponent = ({ co
);
};
-export default LimitReductionsTable;
+export default CustomVoltageLevelTable;
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx
new file mode 100644
index 0000000000..45a8512bbe
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { Grid } from '@mui/material';
+import { ESTIM_ALGO_TYPE, ESTIM_LOG_LEVEL, PRINCIPAL_OBSERVABLE_ZONE } from 'components/utils/field-constants';
+import { estimAlgoTypeValues, estimLogLevelValues, TabValue } from './state-estimation-parameters-utils';
+import { FieldLabel, MuiSelectInput, SwitchInput } from '@gridsuite/commons-ui';
+import { styles } from '../parameters-style';
+
+export const StateEstimationGeneralParameters = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-loadbounds-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-loadbounds-parameters.tsx
new file mode 100644
index 0000000000..5efc7a02eb
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-loadbounds-parameters.tsx
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { FunctionComponent, useMemo } from 'react';
+import { DEFAULT_BOUNDS, DEFAULT_FIXED_BOUNDS, VOLTAGE_LEVEL } from '../../../utils/field-constants';
+import { loadboundsParametersFields, TabValue } from './state-estimation-parameters-utils';
+import { IColumnsDef } from '../common/limitreductions/columns-definitions';
+import { useIntl } from 'react-intl';
+import { Box, Grid } from '@mui/material';
+import LineSeparator from '../../commons/line-separator';
+import GridSection from '../../commons/grid-section';
+import CustomVoltageLevelTable from '../common/voltage-level-table/custom-voltage-level-table';
+
+export const StateEstimationLoadboundsParameters: FunctionComponent = () => {
+ const intl = useIntl();
+
+ const columnsDefinition = useMemo(() => {
+ const definition = [
+ {
+ dataKey: VOLTAGE_LEVEL,
+ label: intl.formatMessage({ id: 'voltageRange' }),
+ tooltip: intl.formatMessage({ id: 'voltageRange' }),
+ },
+ ];
+ definition.push(
+ ...loadboundsParametersFields.map((parameter) => {
+ return {
+ dataKey: parameter,
+ label: intl.formatMessage({ id: parameter }),
+ tooltip: intl.formatMessage({ id: parameter }),
+ };
+ })
+ );
+ return definition;
+ }, [intl]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-utils.ts b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-utils.ts
new file mode 100644
index 0000000000..9b78b9fde7
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-utils.ts
@@ -0,0 +1,389 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import yup from '../../../utils/yup-config';
+import {
+ DEFAULT_BOUNDS,
+ DEFAULT_FIXED_BOUNDS,
+ ESTIM_ALGO_TYPE,
+ ESTIM_LOG_LEVEL,
+ P_MAX,
+ P_MIN,
+ PRINCIPAL_OBSERVABLE_ZONE,
+ Q_MAX,
+ Q_MIN,
+ QUALITY_PER_REGION,
+ THRESHOLD_ACT_REDUNDANCY,
+ THRESHOLD_ACT_TRANSIT,
+ THRESHOLD_LOST_ACT_LOAD,
+ THRESHOLD_LOST_ACT_PROD,
+ THRESHOLD_LOST_REA_LOAD,
+ THRESHOLD_LOST_REA_PROD,
+ THRESHOLD_NB_CRITICAL_MEASURE,
+ THRESHOLD_NB_INVALID_MEASURE,
+ THRESHOLD_NB_ITER,
+ THRESHOLD_NB_LOST_INJECTIONS,
+ THRESHOLD_NB_LOST_TRANSITS,
+ THRESHOLD_NB_OUT_BOUNDS_GAP,
+ THRESHOLD_OBSERVABILITY_RATE,
+ THRESHOLD_OUT_BOUNDS_GAP_P,
+ THRESHOLD_OUT_BOUNDS_GAP_Q,
+ THRESHOLD_OUT_BOUNDS_GAP_V,
+ THRESHOLD_PER_VOLTAGE_LEVEL,
+ THRESHOLD_REA_REDUNDANCY,
+ THRESHOLD_REA_TRANSIT,
+ VOLTAGE_LEVEL,
+ WEIGHT_ACT_LOAD,
+ WEIGHT_ACT_PROD,
+ WEIGHT_ACT_TRANSIT,
+ WEIGHT_IN,
+ WEIGHT_REA_LOAD,
+ WEIGHT_REA_PROD,
+ WEIGHT_REA_TRANSIT,
+ WEIGHT_V,
+ WEIGHTS_PARAMETERS,
+} from '../../../utils/field-constants';
+
+enum VoltageLevels {
+ VL_20_KV = '20 kV',
+ VL_45_KV = '45 kV',
+ VL_63_KV = '63 kV',
+ VL_90_KV = '90 kV',
+ VL_150_KV = '150 kV',
+ VL_225_KV = '225 kV',
+ VL_400_KV = '400 kV',
+}
+
+export enum TabValue {
+ GENERAL = 'general',
+ WEIGHTS = 'weights',
+ QUALITY = 'quality',
+ LOADBOUNDS = 'loadBounds',
+}
+
+enum estimAlgoType {
+ GAUSS_NEWTON_L2 = 'GAUSS_NEWTON_L2',
+ LEVENBERG_MARQUARDT_L2 = 'LEVENBERG_MARQUARDT_L2',
+ NONE = 'NONE',
+}
+
+export const estimAlgoTypeValues = [
+ ...Object.values(estimAlgoType).map((algoType) => ({ id: algoType, label: algoType })),
+];
+
+enum estimLogLevel {
+ PAS = 'PAS',
+ EXPERT = 'EXPERT',
+ DEBUG = 'DEBUG',
+}
+
+export const estimLogLevelValues = [
+ ...Object.values(estimLogLevel).map((logLevel) => ({ id: logLevel, label: logLevel })),
+];
+
+export const weightsParametersFields = [
+ WEIGHT_V,
+ WEIGHT_ACT_TRANSIT,
+ WEIGHT_REA_TRANSIT,
+ WEIGHT_ACT_PROD,
+ WEIGHT_REA_PROD,
+ WEIGHT_ACT_LOAD,
+ WEIGHT_REA_LOAD,
+ WEIGHT_IN,
+];
+
+export const qualityParametersFields = [
+ THRESHOLD_OUT_BOUNDS_GAP_V,
+ THRESHOLD_OUT_BOUNDS_GAP_P,
+ THRESHOLD_OUT_BOUNDS_GAP_Q,
+ THRESHOLD_LOST_ACT_PROD,
+ THRESHOLD_LOST_REA_PROD,
+ THRESHOLD_LOST_ACT_LOAD,
+ THRESHOLD_LOST_REA_LOAD,
+ THRESHOLD_ACT_TRANSIT,
+ THRESHOLD_REA_TRANSIT,
+];
+
+export const loadboundsParametersFields = [P_MIN, P_MAX, Q_MIN, Q_MAX];
+
+interface VoltageLevelLabel {
+ voltageLevel: string;
+}
+
+interface VoltageLevelCode {
+ voltageLevel: number;
+}
+
+//Quality parameters has a dedicated label for data per voltage level
+interface ThresholdVoltageLevelCode {
+ thresholdVoltageLevel: number;
+}
+
+interface WeightsParameters extends VoltageLevelCode {
+ weightV: number;
+ weightActTransit: number;
+ weightReaTransit: number;
+ weightActProd: number;
+ weightReaProd: number;
+ weightActLoad: number;
+ weightReaLoad: number;
+ weightIN: number;
+}
+
+interface WeightParametersForm extends VoltageLevelLabel, Omit {}
+
+interface LoadBoundsDetailsParameters extends VoltageLevelCode {
+ pmin: number;
+ pmax: number;
+ qmin: number;
+ qmax: number;
+}
+
+interface LoadBoundsDetailsParametersForm
+ extends VoltageLevelLabel,
+ Omit {}
+
+interface ThresholdsPerVoltageLevel extends ThresholdVoltageLevelCode {
+ thresholdOutBoundsGapV: number;
+ thresholdOutBoundsGapP: number;
+ thresholdOutBoundsGapQ: number;
+ thresholdLostActProd: number;
+ thresholdLostReaProd: number;
+ thresholdLostActLoad: number;
+ thresholdLostReaLoad: number;
+ thresholdActTransit: number;
+ thresholdReaTransit: number;
+}
+
+interface ThresholdsPerVoltageLevelForm
+ extends VoltageLevelLabel,
+ Omit {}
+
+export interface StateEstimationParameters {
+ estimParameters: {
+ principalObservableZone: boolean;
+ estimAlgoType: string;
+ estimLogLevel: string;
+ weights: {
+ weightsParameters: WeightsParameters[];
+ };
+ quality: {
+ thresholdObservabilityRate: number;
+ thresholdActRedundancy: number;
+ thresholdReaRedundancy: number;
+ thresholdNbLostInjections: number;
+ thresholdNbInvalidMeasure: number;
+ thresholdNbCriticalMeasure: number;
+ thresholdNbOutBoundsGap: number;
+ thresholdNbIter: number;
+ thresholdNbLostTransits: number;
+ qualityPerRegion: boolean;
+ thresholdsPerVoltageLevel: ThresholdsPerVoltageLevel[];
+ };
+ loadBounds: {
+ defaultBounds: LoadBoundsDetailsParameters[];
+ defaultFixedBounds: LoadBoundsDetailsParameters[];
+ };
+ };
+}
+
+const ESTIM_PARAMETERS = 'estimParameters';
+
+export const mapFromVoltageLevelCode = (code: number | string): string => {
+ switch (code) {
+ case 1:
+ return VoltageLevels.VL_20_KV;
+ case 2:
+ return VoltageLevels.VL_45_KV;
+ case 3:
+ return VoltageLevels.VL_63_KV;
+ case 4:
+ return VoltageLevels.VL_90_KV;
+ case 5:
+ return VoltageLevels.VL_150_KV;
+ case 6:
+ return VoltageLevels.VL_225_KV;
+ case 7:
+ return VoltageLevels.VL_400_KV;
+ default:
+ return '';
+ }
+};
+
+export const mapToVoltageLevelCode = (code: string): number => {
+ switch (code) {
+ case VoltageLevels.VL_20_KV:
+ return 1;
+ case VoltageLevels.VL_45_KV:
+ return 2;
+ case VoltageLevels.VL_63_KV:
+ return 3;
+ case VoltageLevels.VL_90_KV:
+ return 4;
+ case VoltageLevels.VL_150_KV:
+ return 5;
+ case VoltageLevels.VL_225_KV:
+ return 6;
+ case VoltageLevels.VL_400_KV:
+ return 7;
+ default:
+ return -1;
+ }
+};
+
+export const fromStateEstimationParametersFormToParamValues = (
+ params: StateEstimationParametersForm
+): StateEstimationParameters => ({
+ [ESTIM_PARAMETERS]: {
+ ...params.general,
+ [TabValue.WEIGHTS]: {
+ [WEIGHTS_PARAMETERS]: params.weights.weightsParameters?.map((weight) => ({
+ ...weight,
+ [VOLTAGE_LEVEL]: mapToVoltageLevelCode(weight.voltageLevel),
+ })),
+ },
+ [TabValue.QUALITY]: {
+ ...params.quality,
+ thresholdsPerVoltageLevel: params.quality.thresholdsPerVoltageLevel?.map((threshold) => ({
+ ...threshold,
+ thresholdVoltageLevel: mapToVoltageLevelCode(threshold.voltageLevel),
+ })),
+ },
+ [TabValue.LOADBOUNDS]: {
+ [DEFAULT_BOUNDS]: params.loadBounds.defaultBounds?.map((loadBound) => ({
+ ...loadBound,
+ [VOLTAGE_LEVEL]: mapToVoltageLevelCode(loadBound.voltageLevel),
+ })),
+ [DEFAULT_FIXED_BOUNDS]: params.loadBounds.defaultFixedBounds?.map((loadBound) => ({
+ ...loadBound,
+ [VOLTAGE_LEVEL]: mapToVoltageLevelCode(loadBound.voltageLevel),
+ })),
+ },
+ },
+});
+
+const mapVoltageLevelData = (
+ items: T[]
+): U[] =>
+ items.map((item) => {
+ const voltageLevelValue = 'thresholdVoltageLevel' in item ? item.thresholdVoltageLevel : item.voltageLevel;
+ return {
+ ...item,
+ [VOLTAGE_LEVEL]: mapFromVoltageLevelCode(voltageLevelValue),
+ } as unknown as U;
+ });
+
+export const fromStateEstimationParametersParamToFormValues = (
+ values: StateEstimationParameters['estimParameters']
+): StateEstimationParametersForm => ({
+ [TabValue.GENERAL]: {
+ [PRINCIPAL_OBSERVABLE_ZONE]: values.principalObservableZone,
+ [ESTIM_LOG_LEVEL]: values.estimLogLevel,
+ [ESTIM_ALGO_TYPE]: values.estimAlgoType,
+ },
+ [TabValue.WEIGHTS]: {
+ [WEIGHTS_PARAMETERS]: mapVoltageLevelData(
+ values.weights.weightsParameters
+ ),
+ },
+ [TabValue.QUALITY]: {
+ ...values.quality,
+ [THRESHOLD_PER_VOLTAGE_LEVEL]: mapVoltageLevelData(
+ values.quality.thresholdsPerVoltageLevel
+ ),
+ },
+ [TabValue.LOADBOUNDS]: {
+ [DEFAULT_BOUNDS]: mapVoltageLevelData(
+ values.loadBounds.defaultBounds
+ ),
+ [DEFAULT_FIXED_BOUNDS]: mapVoltageLevelData(
+ values.loadBounds.defaultFixedBounds
+ ),
+ },
+});
+
+export const stateEstimationParametersFormSchema = yup.object().shape({
+ [TabValue.GENERAL]: yup.object().shape({
+ [PRINCIPAL_OBSERVABLE_ZONE]: yup.boolean().required(),
+ [ESTIM_ALGO_TYPE]: yup.string().required(),
+ [ESTIM_LOG_LEVEL]: yup.string().required(),
+ }),
+ [TabValue.WEIGHTS]: yup.object().shape({
+ [WEIGHTS_PARAMETERS]: yup
+ .array()
+ .of(
+ yup.object().shape({
+ [VOLTAGE_LEVEL]: yup.string().required(),
+ [WEIGHT_V]: yup.number().required().min(0).label(WEIGHT_V),
+ [WEIGHT_ACT_TRANSIT]: yup.number().required().max(0).label(WEIGHT_ACT_TRANSIT),
+ [WEIGHT_REA_TRANSIT]: yup.number().required().min(0).label(WEIGHT_REA_TRANSIT),
+ [WEIGHT_ACT_PROD]: yup.number().required().max(0).label(WEIGHT_ACT_PROD),
+ [WEIGHT_REA_PROD]: yup.number().required().min(0).label(WEIGHT_REA_PROD),
+ [WEIGHT_ACT_LOAD]: yup.number().required().max(0).label(WEIGHT_ACT_LOAD),
+ [WEIGHT_REA_LOAD]: yup.number().required().min(0).label(WEIGHT_REA_LOAD),
+ [WEIGHT_IN]: yup.number().required().min(0).label(WEIGHT_IN),
+ })
+ )
+ .required(),
+ }),
+ [TabValue.QUALITY]: yup.object().shape({
+ [THRESHOLD_OBSERVABILITY_RATE]: yup.number().required().min(0).label(THRESHOLD_OBSERVABILITY_RATE),
+ [THRESHOLD_ACT_REDUNDANCY]: yup.number().required().min(0).label(THRESHOLD_ACT_REDUNDANCY),
+ [THRESHOLD_REA_REDUNDANCY]: yup.number().required().min(0).label(THRESHOLD_REA_REDUNDANCY),
+ [THRESHOLD_NB_LOST_INJECTIONS]: yup.number().required().min(0).label(THRESHOLD_NB_LOST_INJECTIONS),
+ [THRESHOLD_NB_INVALID_MEASURE]: yup.number().required().min(0).label(THRESHOLD_NB_INVALID_MEASURE),
+ [THRESHOLD_NB_CRITICAL_MEASURE]: yup.number().required().min(0).label(THRESHOLD_NB_CRITICAL_MEASURE),
+ [THRESHOLD_NB_OUT_BOUNDS_GAP]: yup.number().required().min(0).label(THRESHOLD_NB_OUT_BOUNDS_GAP),
+ [THRESHOLD_NB_ITER]: yup.number().required().min(0).label(THRESHOLD_NB_ITER),
+ [THRESHOLD_NB_LOST_TRANSITS]: yup.number().required().min(0).label(THRESHOLD_NB_LOST_TRANSITS),
+ [QUALITY_PER_REGION]: yup.boolean().required(),
+ [THRESHOLD_PER_VOLTAGE_LEVEL]: yup
+ .array()
+ .of(
+ yup.object().shape({
+ [VOLTAGE_LEVEL]: yup.string().required(),
+ [THRESHOLD_OUT_BOUNDS_GAP_V]: yup.number().required().min(0).label(THRESHOLD_OUT_BOUNDS_GAP_V),
+ [THRESHOLD_OUT_BOUNDS_GAP_P]: yup.number().required().min(0).label(THRESHOLD_OUT_BOUNDS_GAP_P),
+ [THRESHOLD_OUT_BOUNDS_GAP_Q]: yup.number().required().min(0).label(THRESHOLD_OUT_BOUNDS_GAP_Q),
+ [THRESHOLD_LOST_ACT_PROD]: yup.number().required().min(0).label(THRESHOLD_LOST_ACT_PROD),
+ [THRESHOLD_LOST_REA_PROD]: yup.number().required().min(0).label(THRESHOLD_LOST_REA_PROD),
+ [THRESHOLD_LOST_ACT_LOAD]: yup.number().required().min(0).label(THRESHOLD_LOST_ACT_LOAD),
+ [THRESHOLD_LOST_REA_LOAD]: yup.number().required().min(0).label(THRESHOLD_LOST_REA_LOAD),
+ [THRESHOLD_ACT_TRANSIT]: yup.number().required().min(0).label(THRESHOLD_ACT_TRANSIT),
+ [THRESHOLD_REA_TRANSIT]: yup.number().required().min(0).label(THRESHOLD_REA_TRANSIT),
+ })
+ )
+ .required(),
+ }),
+ [TabValue.LOADBOUNDS]: yup.object().shape({
+ [DEFAULT_BOUNDS]: yup
+ .array()
+ .of(
+ yup.object().shape({
+ [VOLTAGE_LEVEL]: yup.string().required(),
+ [P_MIN]: yup.number().required(),
+ [P_MAX]: yup.number().required().min(0).label(P_MAX),
+ [Q_MIN]: yup.number().required(),
+ [Q_MAX]: yup.number().required(),
+ })
+ )
+ .required(),
+ [DEFAULT_FIXED_BOUNDS]: yup
+ .array()
+ .of(
+ yup.object().shape({
+ [VOLTAGE_LEVEL]: yup.string().required(),
+ [P_MIN]: yup.number().required(),
+ [P_MAX]: yup.number().required().min(0).label(P_MAX),
+ [Q_MIN]: yup.number().required(),
+ [Q_MAX]: yup.number().required(),
+ })
+ )
+ .required(),
+ }),
+});
+
+export type StateEstimationParametersForm = yup.InferType;
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx
new file mode 100644
index 0000000000..76f48ea35d
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx
@@ -0,0 +1,209 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { Dispatch, SetStateAction, SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react';
+import { CustomFormProvider, SubmitButton, useSnackMessage } from '@gridsuite/commons-ui';
+import { GENERAL } from '../voltageinit/voltage-init-parameters-form';
+import { Button, DialogActions, Grid, Tab, Tabs } from '@mui/material';
+import { mergeSx } from '../../../utils/functions';
+import { styles } from '../parameters-style';
+import { TabPanel } from '../parameters';
+import { getTabIndicatorStyle, getTabStyle } from '../../../utils/tab-utils';
+import { FormattedMessage } from 'react-intl';
+import { useForm } from 'react-hook-form';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { useSelector } from 'react-redux';
+import { AppState } from '../../../../redux/reducer';
+import {
+ fromStateEstimationParametersFormToParamValues,
+ fromStateEstimationParametersParamToFormValues,
+ StateEstimationParametersForm,
+ stateEstimationParametersFormSchema,
+ TabValue,
+} from './state-estimation-parameters-utils';
+import { StateEstimationGeneralParameters } from './state-estimation-general-parameters';
+import { StateEstimationWeightsParameters } from './state-estimation-weights-parameters';
+import { StateEstimationQualityParameters } from './state-estimation-quality-parameters';
+import { StateEstimationLoadboundsParameters } from './state-estimation-loadbounds-parameters';
+import { updateStateEstimationParameters } from '../../../../services/study/state-estimation';
+import { UseGetStateEstimationParametersProps } from './use-get-state-estimation-parameters';
+
+export const StateEstimationParameters = ({
+ useStateEstimationParameters,
+ setHaveDirtyFields,
+}: {
+ useStateEstimationParameters: UseGetStateEstimationParametersProps;
+ setHaveDirtyFields: Dispatch>;
+}) => {
+ const studyUuid = useSelector((state: AppState) => state.studyUuid);
+ const [stateEstimationParams, setStateEstimationParams] = useStateEstimationParameters;
+
+ const initialFormValues = useMemo(
+ () =>
+ stateEstimationParams
+ ? fromStateEstimationParametersParamToFormValues(stateEstimationParams?.estimParameters)
+ : {},
+ [stateEstimationParams]
+ );
+
+ const formMethods = useForm({
+ defaultValues: initialFormValues,
+ resolver: yupResolver(stateEstimationParametersFormSchema),
+ });
+ const { reset, handleSubmit, formState } = formMethods;
+ const { snackError } = useSnackMessage();
+
+ const handleTabChange = useCallback((_: SyntheticEvent, newValue: TabValue) => {
+ setTabValue(newValue);
+ }, []);
+
+ const [tabValue, setTabValue] = useState(TabValue.GENERAL);
+ const [tabIndexesWithError, setTabIndexesWithError] = useState([]);
+
+ const onValidationError = useCallback(
+ (errors?: any) => {
+ let tabsInError = [];
+ if (errors?.[GENERAL] !== undefined) {
+ tabsInError.push(TabValue.GENERAL);
+ }
+ if (errors?.[TabValue.WEIGHTS] !== undefined) {
+ tabsInError.push(TabValue.WEIGHTS);
+ }
+ if (errors?.[TabValue.QUALITY]) {
+ tabsInError.push(TabValue.QUALITY);
+ }
+ if (errors?.[TabValue.LOADBOUNDS]) {
+ tabsInError.push(TabValue.LOADBOUNDS);
+ }
+ setTabIndexesWithError(tabsInError);
+ },
+ [setTabIndexesWithError]
+ );
+
+ const resetStateEstimationParameters = useCallback(() => {
+ updateStateEstimationParameters(studyUuid, null).catch((error) => {
+ snackError({
+ messageTxt: error.message,
+ headerId: 'paramsChangingError',
+ });
+ });
+ }, [studyUuid, snackError]);
+
+ const clear = useCallback(() => {
+ resetStateEstimationParameters();
+ onValidationError();
+ }, [resetStateEstimationParameters, onValidationError]);
+
+ const onSubmit = useCallback(
+ (newParams: StateEstimationParametersForm) => {
+ updateStateEstimationParameters(studyUuid, fromStateEstimationParametersFormToParamValues(newParams))
+ .then(() => {
+ setStateEstimationParams(fromStateEstimationParametersFormToParamValues(newParams));
+ })
+ .catch((error) => {
+ snackError({
+ messageTxt: error,
+ headerId: 'updateStateEstimationParametersError',
+ });
+ });
+ onValidationError();
+ },
+ [onValidationError, setStateEstimationParams, snackError, studyUuid]
+ );
+
+ useEffect(() => {
+ if (stateEstimationParams) {
+ reset(fromStateEstimationParametersParamToFormValues(stateEstimationParams.estimParameters));
+ }
+ }, [reset, stateEstimationParams]);
+
+ useEffect(() => {
+ setHaveDirtyFields(!!Object.keys(formState.dirtyFields).length);
+ }, [formState, setHaveDirtyFields]);
+
+ return (
+
+
+
+
+ }
+ value={TabValue.GENERAL}
+ sx={getTabStyle(tabIndexesWithError, TabValue.GENERAL)}
+ />
+ }
+ value={TabValue.WEIGHTS}
+ sx={getTabStyle(tabIndexesWithError, TabValue.WEIGHTS)}
+ />
+ }
+ value={TabValue.QUALITY}
+ sx={getTabStyle(tabIndexesWithError, TabValue.QUALITY)}
+ />
+ }
+ value={TabValue.LOADBOUNDS}
+ sx={getTabStyle(tabIndexesWithError, TabValue.LOADBOUNDS)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-quality-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-quality-parameters.tsx
new file mode 100644
index 0000000000..115ac42b67
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-quality-parameters.tsx
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import GridSection from '../../commons/grid-section';
+import { ParameterFloat } from '../widget';
+import { styles } from '../parameters-style';
+import { qualityParametersFields, TabValue } from './state-estimation-parameters-utils';
+import {
+ QUALITY_PER_REGION,
+ THRESHOLD_ACT_REDUNDANCY,
+ THRESHOLD_NB_CRITICAL_MEASURE,
+ THRESHOLD_NB_INVALID_MEASURE,
+ THRESHOLD_NB_ITER,
+ THRESHOLD_NB_LOST_INJECTIONS,
+ THRESHOLD_NB_LOST_TRANSITS,
+ THRESHOLD_NB_OUT_BOUNDS_GAP,
+ THRESHOLD_OBSERVABILITY_RATE,
+ THRESHOLD_PER_VOLTAGE_LEVEL,
+ THRESHOLD_REA_REDUNDANCY,
+ VOLTAGE_LEVEL,
+} from '../../../utils/field-constants';
+import { useMemo } from 'react';
+import { Box, Grid } from '@mui/material';
+import LineSeparator from '../../commons/line-separator';
+import { useIntl } from 'react-intl';
+import { IColumnsDef } from '../common/limitreductions/columns-definitions';
+import { FieldLabel, SwitchInput } from '@gridsuite/commons-ui';
+import CustomVoltageLevelTable from '../common/voltage-level-table/custom-voltage-level-table';
+
+export const StateEstimationQualityParameters = () => {
+ const intl = useIntl();
+
+ const columnsDefinition = useMemo(() => {
+ const definition = [
+ {
+ dataKey: VOLTAGE_LEVEL,
+ label: intl.formatMessage({ id: 'voltageRange' }),
+ tooltip: intl.formatMessage({ id: 'voltageRange' }),
+ },
+ ];
+ definition.push(
+ ...qualityParametersFields.map((parameter) => {
+ return {
+ dataKey: parameter,
+ label: intl.formatMessage({ id: parameter }),
+ tooltip: intl.formatMessage({ id: parameter }),
+ };
+ })
+ );
+ return definition;
+ }, [intl]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-weights-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-weights-parameters.tsx
new file mode 100644
index 0000000000..1bb6f9a18b
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-weights-parameters.tsx
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { FunctionComponent, useMemo } from 'react';
+import { VOLTAGE_LEVEL, WEIGHTS_PARAMETERS } from '../../../utils/field-constants';
+import { TabValue, weightsParametersFields } from './state-estimation-parameters-utils';
+import { IColumnsDef } from '../common/limitreductions/columns-definitions';
+import { useIntl } from 'react-intl';
+import CustomVoltageLevelTable from '../common/voltage-level-table/custom-voltage-level-table';
+
+export const StateEstimationWeightsParameters: FunctionComponent = () => {
+ const intl = useIntl();
+
+ const columnsDefinition = useMemo(() => {
+ const definition = [
+ {
+ dataKey: VOLTAGE_LEVEL,
+ label: intl.formatMessage({ id: 'voltageRange' }),
+ tooltip: intl.formatMessage({ id: 'voltageRange' }),
+ },
+ ];
+ definition.push(
+ ...weightsParametersFields.map((parameter) => {
+ return {
+ dataKey: parameter,
+ label: intl.formatMessage({ id: parameter }),
+ tooltip: intl.formatMessage({ id: parameter }),
+ };
+ })
+ );
+ return definition;
+ }, [intl]);
+
+ return (
+
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/use-get-state-estimation-parameters.ts b/src/components/dialogs/parameters/state-estimation/use-get-state-estimation-parameters.ts
new file mode 100644
index 0000000000..55f16c07af
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/use-get-state-estimation-parameters.ts
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+import { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
+import { useSelector } from 'react-redux';
+import { AppState } from '../../../../redux/reducer';
+import { useSnackMessage } from '@gridsuite/commons-ui';
+import { useOptionalServiceStatus } from '../../../../hooks/use-optional-service-status';
+import { OptionalServicesNames, OptionalServicesStatus } from '../../../utils/optional-services';
+import ComputingType from '../../../computing-status/computing-type';
+import { isComputationParametersUpdated } from '../common/computation-parameters-util';
+import { UUID } from 'crypto';
+import { StateEstimationParameters } from './state-estimation-parameters-utils';
+import { getStateEstimationStudyParameters } from '../../../../services/study/state-estimation';
+
+export type UseGetStateEstimationParametersProps = [
+ StateEstimationParameters | null,
+ Dispatch>
+];
+
+export const useGetStateEstimationParameters = (): UseGetStateEstimationParametersProps => {
+ const studyUuid = useSelector((state: AppState) => state.studyUuid);
+ const studyUpdated = useSelector((state: AppState) => state.studyUpdated);
+ const { snackError } = useSnackMessage();
+ const [stateEstimationParams, setStateEstimationParams] = useState(null);
+
+ const stateEstimationAvailability = useOptionalServiceStatus(OptionalServicesNames.StateEstimation);
+ const stateEstimationAvailabilityRef = useRef(stateEstimationAvailability);
+ stateEstimationAvailabilityRef.current = stateEstimationAvailability;
+
+ const fetchStateEstimationStudyParameters = useCallback(
+ (studyUuid: UUID) => {
+ getStateEstimationStudyParameters(studyUuid)
+ .then((params: StateEstimationParameters) => {
+ setStateEstimationParams(params);
+ })
+ .catch((error) => {
+ snackError({
+ messageTxt: error.message,
+ headerId: 'paramsRetrievingError',
+ });
+ });
+ },
+ [snackError]
+ );
+ useEffect(() => {
+ if (studyUuid && stateEstimationAvailability === OptionalServicesStatus.Up) {
+ fetchStateEstimationStudyParameters(studyUuid);
+ }
+ }, [stateEstimationAvailability, studyUuid, fetchStateEstimationStudyParameters]);
+
+ // fetch the parameter if STATE_ESTIMATION notification type is received.
+ useEffect(() => {
+ if (
+ studyUuid &&
+ stateEstimationAvailabilityRef.current === OptionalServicesStatus.Up &&
+ isComputationParametersUpdated(ComputingType.STATE_ESTIMATION, studyUpdated)
+ ) {
+ fetchStateEstimationStudyParameters(studyUuid);
+ }
+ }, [studyUuid, fetchStateEstimationStudyParameters, studyUpdated]);
+
+ return [stateEstimationParams, setStateEstimationParams];
+};
diff --git a/src/components/dialogs/parameters/widget/parameter-float.tsx b/src/components/dialogs/parameters/widget/parameter-float.tsx
index 6e7b0d3316..b292345a2b 100644
--- a/src/components/dialogs/parameters/widget/parameter-float.tsx
+++ b/src/components/dialogs/parameters/widget/parameter-float.tsx
@@ -12,7 +12,7 @@ export interface ParameterFloatProps {
name: string;
label: string;
style: any;
- adornment: any;
+ adornment?: any;
tooltip?: string;
labelSize: number;
inputSize: number;
diff --git a/src/components/parameters-tabs.tsx b/src/components/parameters-tabs.tsx
index 78ca7ae200..c9debb677b 100644
--- a/src/components/parameters-tabs.tsx
+++ b/src/components/parameters-tabs.tsx
@@ -54,6 +54,8 @@ import RunningStatus from './utils/running-status';
import GlassPane from './results/common/glass-pane';
import { SecurityAnalysisParameters } from './dialogs/parameters/security-analysis/security-analysis-parameters';
import { NetworkVisualizationsParameters } from './dialogs/parameters/network-visualizations/network-visualizations-parameters';
+import { StateEstimationParameters } from './dialogs/parameters/state-estimation/state-estimation-parameters';
+import { useGetStateEstimationParameters } from './dialogs/parameters/state-estimation/use-get-state-estimation-parameters';
import DynamicSecurityAnalysisParameters from './dialogs/parameters/dynamic-security-analysis/dynamic-security-analysis-parameters';
import { useGetNonEvacuatedEnergyParameters } from './dialogs/parameters/non-evacuated-energy/use-get-non-evacuated-energy-parameters';
import { stylesLayout, tabStyles } from './utils/tab-utils';
@@ -70,6 +72,7 @@ enum TAB_VALUES {
dynamicSimulationParamsTabValue = 'DYNAMIC_SIMULATION',
dynamicSecurityAnalysisParamsTabValue = 'DYNAMIC_SECURITY_ANALYSIS',
voltageInitParamsTabValue = 'VOLTAGE_INITIALIZATION',
+ stateEstimationTabValue = 'STATE_ESTIMATION',
networkVisualizationsParams = 'networkVisualizationsParams',
}
@@ -94,6 +97,7 @@ const ParametersTabs: FunctionComponent = (props) => {
const dynamicSecurityAnalysisAvailability = useOptionalServiceStatus(OptionalServicesNames.DynamicSecurityAnalysis);
const voltageInitAvailability = useOptionalServiceStatus(OptionalServicesNames.VoltageInit);
const shortCircuitAvailability = useOptionalServiceStatus(OptionalServicesNames.ShortCircuit);
+ const stateEstimationAvailability = useOptionalServiceStatus(OptionalServicesNames.StateEstimation);
const computationStatus = useSelector((state: AppState) => state.computingStatus[tabValue as ComputingType]);
const shortCircuitOneBusStatus = useSelector(
@@ -161,6 +165,8 @@ const ParametersTabs: FunctionComponent = (props) => {
const useShortCircuitParameters = useGetShortCircuitParameters();
+ const useStateEstimationParameters = useGetStateEstimationParameters();
+
const handleChangeTab = (newValue: string) => {
if (haveDirtyFields) {
setNextTabValue(newValue);
@@ -181,12 +187,13 @@ const ParametersTabs: FunctionComponent = (props) => {
useEffect(() => {
setTabValue((oldValue) => {
if (
- !enableDeveloperMode &&
- (oldValue === TAB_VALUES.sensitivityAnalysisParamsTabValue ||
- oldValue === TAB_VALUES.nonEvacuatedEnergyParamsTabValue ||
- oldValue === TAB_VALUES.shortCircuitParamsTabValue ||
- oldValue === TAB_VALUES.dynamicSimulationParamsTabValue ||
- oldValue === TAB_VALUES.dynamicSecurityAnalysisParamsTabValue)
+ (!enableDeveloperMode &&
+ (oldValue === TAB_VALUES.sensitivityAnalysisParamsTabValue ||
+ oldValue === TAB_VALUES.nonEvacuatedEnergyParamsTabValue ||
+ oldValue === TAB_VALUES.shortCircuitParamsTabValue ||
+ oldValue === TAB_VALUES.dynamicSimulationParamsTabValue ||
+ oldValue === TAB_VALUES.dynamicSecurityAnalysisParamsTabValue)) ||
+ oldValue === TAB_VALUES.stateEstimationTabValue
) {
return TAB_VALUES.securityAnalysisParamsTabValue;
}
@@ -237,18 +244,26 @@ const ParametersTabs: FunctionComponent = (props) => {
return ;
case TAB_VALUES.voltageInitParamsTabValue:
return ;
+ case TAB_VALUES.stateEstimationTabValue:
+ return (
+
+ );
case TAB_VALUES.networkVisualizationsParams:
return ;
}
}, [
+ tabValue,
loadFlowParametersBackend,
securityAnalysisParametersBackend,
sensitivityAnalysisBackend,
nonEvacuatedEnergyBackend,
- tabValue,
useNonEvacuatedEnergyParameters,
useShortCircuitParameters,
user,
+ useStateEstimationParameters,
]);
return (
@@ -318,6 +333,11 @@ const ParametersTabs: FunctionComponent = (props) => {
label={}
value={TAB_VALUES.voltageInitParamsTabValue}
/>
+ }
+ value={TAB_VALUES.stateEstimationTabValue}
+ />
{/*In order to insert a Divider under a Tabs collection it need to be nested in a dedicated Tab to prevent console warnings*/}
} disabled />
= ({
const [manualTabSwitch, setManualTabSwitch] = useState(true);
const [rowData, setRowData] = useState([]);
+ const [equipmentToUpdateId, setEquipmentToUpdateId] = useState(null);
const isLockedColumnNamesEmpty = useMemo(() => formattedLockedColumns.size === 0, [formattedLockedColumns.size]);
@@ -190,9 +191,28 @@ export const TableWrapper: FunctionComponent = ({
[tableDefinition.type]
);
+ const highlightUpdatedEquipment = useCallback(() => {
+ if (!equipmentToUpdateId) {
+ return;
+ }
+
+ const api = gridRef.current?.api;
+ const rowNode = api?.getRowNode(equipmentToUpdateId);
+
+ if (rowNode && api) {
+ api.flashCells({
+ rowNodes: [rowNode],
+ flashDuration: 1000,
+ });
+ }
+
+ setEquipmentToUpdateId(null);
+ }, [equipmentToUpdateId]);
+
const { equipments, errorMessage, isFetching } = useSpreadsheetEquipments(
tableDefinition.type,
- formatFetchedEquipmentsHandler
+ formatFetchedEquipmentsHandler,
+ highlightUpdatedEquipment
);
useEffect(() => {
@@ -314,6 +334,7 @@ export const TableWrapper: FunctionComponent = ({
const onRowClicked = useCallback(
(event: RowClickedEvent) => {
const equipmentId = event.data.id;
+ setEquipmentToUpdateId(equipmentId);
handleOpenModificationDialog(equipmentId);
},
[handleOpenModificationDialog]
diff --git a/src/components/spreadsheet/use-spreadsheet-equipments.ts b/src/components/spreadsheet/use-spreadsheet-equipments.ts
index a3719fab3d..9d3efccd02 100644
--- a/src/components/spreadsheet/use-spreadsheet-equipments.ts
+++ b/src/components/spreadsheet/use-spreadsheet-equipments.ts
@@ -31,7 +31,8 @@ type FormatFetchedEquipments = (equipments: Identifiable[]) => Identifiable[];
export const useSpreadsheetEquipments = (
type: SpreadsheetEquipmentType,
- formatFetchedEquipments: FormatFetchedEquipments
+ formatFetchedEquipments: FormatFetchedEquipments,
+ highlightUpdatedEquipment: () => void
) => {
const dispatch = useDispatch();
const allEquipments = useSelector((state: AppState) => state.spreadsheetNetwork);
@@ -122,6 +123,7 @@ export const useSpreadsheetEquipments = (
if (impactedSubstationsIds.length > 0 && studyUuid && currentRootNetworkUuid && currentNode?.id) {
// The formatting of the fetched equipments is done in the reducer
fetchAllEquipments(studyUuid, nodeId, currentRootNetworkUuid, impactedSubstationsIds).then((values) => {
+ highlightUpdatedEquipment();
dispatch(updateEquipments(values, nodeId));
});
resetImpactedSubstationsIds();
@@ -163,6 +165,7 @@ export const useSpreadsheetEquipments = (
resetImpactedSubstationsIds,
resetDeletedEquipments,
resetImpactedElementTypes,
+ highlightUpdatedEquipment,
]);
useEffect(() => {
diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts
index d95d790d48..5a14a25b32 100644
--- a/src/components/utils/field-constants.ts
+++ b/src/components/utils/field-constants.ts
@@ -400,6 +400,52 @@ export const STAND_BY_AUTOMATON = 'StandbyAutomaton';
export const FILTERS_SHUNT_COMPENSATOR_TABLE = 'shuntCompensatorInfos';
export const SPREADSHEET_GS_FILTER = 'SpreadsheetGsFilter';
+/* State estimation parameters fields */
+/* General */
+export const PRINCIPAL_OBSERVABLE_ZONE = 'principalObservableZone';
+export const ESTIM_ALGO_TYPE = 'estimAlgoType';
+export const ESTIM_LOG_LEVEL = 'estimLogLevel';
+/* Weights */
+export const WEIGHTS_PARAMETERS = 'weightsParameters';
+export const WEIGHT_V = 'weightV';
+export const WEIGHT_ACT_TRANSIT = 'weightActTransit';
+export const WEIGHT_REA_TRANSIT = 'weightReaTransit';
+export const WEIGHT_ACT_PROD = 'weightActProd';
+export const WEIGHT_REA_PROD = 'weightReaProd';
+export const WEIGHT_ACT_LOAD = 'weightActLoad';
+export const WEIGHT_REA_LOAD = 'weightReaLoad';
+export const WEIGHT_IN = 'weightIN';
+/* Quality */
+export const THRESHOLD_OBSERVABILITY_RATE = 'thresholdObservabilityRate';
+export const THRESHOLD_ACT_REDUNDANCY = 'thresholdActRedundancy';
+export const THRESHOLD_REA_REDUNDANCY = 'thresholdReaRedundancy';
+export const THRESHOLD_NB_LOST_INJECTIONS = 'thresholdNbLostInjections';
+export const THRESHOLD_NB_INVALID_MEASURE = 'thresholdNbInvalidMeasure';
+export const THRESHOLD_NB_CRITICAL_MEASURE = 'thresholdNbCriticalMeasure';
+export const THRESHOLD_NB_OUT_BOUNDS_GAP = 'thresholdNbOutBoundsGap';
+export const THRESHOLD_NB_ITER = 'thresholdNbIter';
+export const THRESHOLD_NB_LOST_TRANSITS = 'thresholdNbLostTransits';
+export const QUALITY_PER_REGION = 'qualityPerRegion';
+export const THRESHOLD_PER_VOLTAGE_LEVEL = 'thresholdsPerVoltageLevel';
+export const THRESHOLD_OUT_BOUNDS_GAP_V = 'thresholdOutBoundsGapV';
+export const THRESHOLD_OUT_BOUNDS_GAP_P = 'thresholdOutBoundsGapP';
+export const THRESHOLD_OUT_BOUNDS_GAP_Q = 'thresholdOutBoundsGapQ';
+export const THRESHOLD_LOST_ACT_PROD = 'thresholdLostActProd';
+export const THRESHOLD_LOST_REA_PROD = 'thresholdLostReaProd';
+export const THRESHOLD_LOST_ACT_LOAD = 'thresholdLostActLoad';
+export const THRESHOLD_LOST_REA_LOAD = 'thresholdLostReaLoad';
+export const THRESHOLD_ACT_TRANSIT = 'thresholdActTransit';
+export const THRESHOLD_REA_TRANSIT = 'thresholdReaTransit';
+
+/* Loadbounds */
+
+export const DEFAULT_BOUNDS = 'defaultBounds';
+export const DEFAULT_FIXED_BOUNDS = 'defaultFixedBounds';
+export const P_MIN = 'pmin';
+export const P_MAX = 'pmax';
+export const Q_MIN = 'qmin';
+export const Q_MAX = 'qmax';
+
export const BRANCH_MEASUREMENTS = 'branchMeasurements';
export const MEASUREMENT_P1 = 'measurementP1';
export const MEASUREMENT_P2 = 'measurementP2';
diff --git a/src/services/study/state-estimation.ts b/src/services/study/state-estimation.ts
index 41c2611fc6..e662af417a 100644
--- a/src/services/study/state-estimation.ts
+++ b/src/services/study/state-estimation.ts
@@ -5,9 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index';
+import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index';
import { backendFetch, backendFetchJson, backendFetchText } from '../utils';
import { UUID } from 'crypto';
+import { StateEstimationParameters } from '../../components/dialogs/parameters/state-estimation/state-estimation-parameters-utils';
export function startStateEstimation(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) {
console.info(
@@ -60,3 +61,27 @@ export function fetchStateEstimationResult(studyUuid: UUID, currentNodeUuid: UUI
console.debug(url);
return backendFetchJson(url);
}
+
+export function updateStateEstimationParameters(studyUuid: UUID | null, newParams: StateEstimationParameters | null) {
+ console.info('set state estimation parameters');
+ const url = getStudyUrl(studyUuid) + '/state-estimation/parameters';
+ console.debug(url);
+
+ console.info('newParams in rest API', newParams);
+
+ return backendFetch(url, {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(newParams),
+ });
+}
+
+export function getStateEstimationStudyParameters(studyUuid: UUID) {
+ console.info('get state estimation study parameters');
+ const getStateEstimParams = getStudyUrl(studyUuid) + '/state-estimation/parameters';
+ console.debug(getStateEstimParams);
+ return backendFetchJson(getStateEstimParams);
+}
diff --git a/src/translations/dynamic/errors-locale-en.ts b/src/translations/dynamic/errors-locale-en.ts
index 21845d1cc9..5c0b675e4c 100644
--- a/src/translations/dynamic/errors-locale-en.ts
+++ b/src/translations/dynamic/errors-locale-en.ts
@@ -43,6 +43,8 @@ const errors_locale_en = {
// Other
resetLoadFlowParametersWarning:
'Impossible to retrieve the load flow parameters defined in the user profile (default values are used)',
+ //State estimation
+ updateStateEstimationParametersError: 'An error occurred while updating the state estimation parameters',
};
export default errors_locale_en;
diff --git a/src/translations/dynamic/errors-locale-fr.ts b/src/translations/dynamic/errors-locale-fr.ts
index 749a141601..e557d1302d 100644
--- a/src/translations/dynamic/errors-locale-fr.ts
+++ b/src/translations/dynamic/errors-locale-fr.ts
@@ -67,6 +67,9 @@ const errors_locale_fr = {
// Other
resetLoadFlowParametersWarning:
'Impossible de récupérer les paramètres de calcul de répartition définis dans le profil utilisateur (les valeurs par défaut sont appliquées)',
+ //State estimation
+ updateStateEstimationParametersError:
+ "Une erreur est survenue lors de la mise a jour des paramètres de l'estimateur d'état",
};
export default errors_locale_fr;
diff --git a/src/translations/en.json b/src/translations/en.json
index e02c0dab43..22f826ca28 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1,9 +1,9 @@
{
"General": "General",
- "LimitReductions" : "Limit reductions",
- "IST" : "IST",
- "LimitDurationInterval" : "Between {highBound} and {lowBound}",
+ "LimitReductions": "Limit reductions",
+ "IST": "IST",
+ "LimitDurationInterval": "Between {highBound} and {lowBound}",
"LimitDurationAfterIST": "Beyond IT{value}",
"voltageRange": "Voltage range",
@@ -1486,5 +1486,49 @@
"rootNetworknameValidityCheckError": "An error occurred while checking root network name",
"rootNetworkDirectoryFetchingError": "An error occurred while fetching root directory",
"rootNetworkNotFound": "No root network was found for the study with ID \"{studyUuid}\"",
- "createRootNetworksError": "An error occurred while creating the root network"
+ "createRootNetworksError": "An error occurred while creating the root network",
+ "StateEstimationParametersGeneralTabLabel": "General",
+ "StateEstimationParametersWeightsTabLabel": "Weights",
+ "StateEstimationParametersQualityTabLabel": "Thresholds",
+ "StateEstimationParametersLoadboundsTabLabel": "Loadbounds",
+ "StateEstimationParametersPrincipalObservableZoneLabel": "Estimation on main observable zone",
+ "StateEstimationParametersAlgoTypeLabel": "Algorithm",
+ "StateEstimationParametersLogLevelLabel": "Log level",
+ "PAS": "Minimal",
+ "EXPERT": "Expert",
+ "DEBUG": "Debug",
+ "weightV": "V",
+ "weightActTransit": "Active flow",
+ "weightReaTransit": "Reactive flow",
+ "weightActProd": "Active prod",
+ "weightReaProd": "Reactive prod",
+ "weightActLoad": "Active load",
+ "weightReaLoad": "Reactive load",
+ "weightIN": "Null Inj",
+ "StateEstimationParametersQualitySection" : "Quality",
+ "qualityPerRegion": "Level of quality by region",
+ "thresholdObservabilityRate": "Threshold observability rate",
+ "thresholdActRedundancy": "Threshold active redundancy",
+ "thresholdReaRedundancy": "Threshold reactive redundancy",
+ "thresholdNbLostInjections": "Lost injections number",
+ "thresholdNbInvalidMeasure": "Invalid measurements number",
+ "thresholdNbCriticalMeasure": "Critical measurements number",
+ "thresholdNbOutBoundsGap": "Out of bounds gaps number",
+ "thresholdNbIter": "Iteration number",
+ "thresholdNbLostTransits": "Lost flows number",
+ "thresholdOutBoundsGapV": "Out of bounds voltage gaps",
+ "thresholdOutBoundsGapP": "Out of bounds active power gaps",
+ "thresholdOutBoundsGapQ": "Out of bounds reactive power gaps",
+ "thresholdLostActProd": "Lost prod active power",
+ "thresholdLostReaProd": "Lost prod reactive power",
+ "thresholdLostActLoad": "Lost load active power",
+ "thresholdLostReaLoad": "Lost load reactive power",
+ "thresholdActTransit": "Active flow threshold",
+ "thresholdReaTransit": "Reactive flow threshold",
+ "StateEstimationParametersDefaultBoundsSection": "Default bounds section by voltage level (affines loads)",
+ "StateEstimationParametersDefaultFixedBoundsSection": "Default bounds section by voltage level (fixed loads)",
+ "pmin": "Min P",
+ "pmax": "Max P",
+ "qmin": "Min Q",
+ "qmax": "Max Q"
}
diff --git a/src/translations/fr.json b/src/translations/fr.json
index 3bee925ef0..b0bcadf15d 100644
--- a/src/translations/fr.json
+++ b/src/translations/fr.json
@@ -1,9 +1,9 @@
{
"General": "Général",
- "LimitReductions" : "Abattements",
- "IST" : "IST",
- "LimitDurationInterval" : "Entre {highBound} et {lowBound}",
+ "LimitReductions": "Abattements",
+ "IST": "IST",
+ "LimitDurationInterval": "Entre {highBound} et {lowBound}",
"LimitDurationAfterIST": "Au-delà de IT{value}",
"voltageRange": "Niveau de tension",
@@ -433,7 +433,7 @@
"CalculatedValue": "Valeur calculée (A ou kV)",
"OverloadedEquipment": "Ouvrage",
"PermanentLimitName": "IST",
- "Permanent":"Permanent",
+ "Permanent": "Permanent",
"LimitAcceptableDuration": "Durée",
"Overload": "Surcharge",
@@ -1483,5 +1483,50 @@
"rootNetworknameValidityCheckError": "Une erreur est survenue lors de la vérification du nom de réseau racine",
"rootNetworkDirectoryFetchingError": "Une erreur est survenue lors de la lecture du dossier du réseau",
"rootNetworkNotFound": "Aucun réseau racine n'a été trouvé pour l'étude avec l'identifiant \"{studyUuid}\"",
- "createRootNetworksError": "Une erreur est survenue lors de la création du réseau racine"
+ "createRootNetworksError": "Une erreur est survenue lors de la création du réseau racine",
+ "createRootNetworksError": "An error occurred while creating the root network",
+ "StateEstimationParametersGeneralTabLabel": "Général",
+ "StateEstimationParametersWeightsTabLabel": "Pondération",
+ "StateEstimationParametersQualityTabLabel": "Seuils",
+ "StateEstimationParametersLoadboundsTabLabel": "Bornes des consos",
+ "StateEstimationParametersPrincipalObservableZoneLabel": "Estimation sur la zone principale observable",
+ "StateEstimationParametersAlgoTypeLabel": "Algorithme",
+ "StateEstimationParametersLogLevelLabel": "Niveau de trace",
+ "PAS": "Minimal",
+ "EXPERT": "Expert",
+ "DEBUG": "Debug",
+ "weightV": "Tension",
+ "weightActTransit": "Transit actif",
+ "weightReaTransit": "Transit réactif",
+ "weightActProd": "Production active",
+ "weightReaProd": "Production réactive",
+ "weightActLoad": "Consommation active",
+ "weightReaLoad": "Consommation réactive",
+ "weightIN": "Inj nulle",
+ "StateEstimationParametersQualitySection": "Qualité",
+ "qualityPerRegion": "Niveau de qualité par région",
+ "thresholdObservabilityRate": "Seuil taux observabilité",
+ "thresholdActRedundancy": "Seuil de redondance active",
+ "thresholdReaRedundancy": "Seuil de redondance réactive",
+ "thresholdNbLostInjections": "Nombre d'injections perdues",
+ "thresholdNbInvalidMeasure": "Nombre de télémesures erronées",
+ "thresholdNbCriticalMeasure": "Pourcentage de télémesures critiques",
+ "thresholdNbOutBoundsGap": "Pourcentage d'écarts hors norme",
+ "thresholdNbIter": "Nombre d'itérations",
+ "thresholdNbLostTransits": "Nombre de transits perdus",
+ "thresholdOutBoundsGapV": "Tension écarts hors norme",
+ "thresholdOutBoundsGapP": "Transit actif écarts hors norme",
+ "thresholdOutBoundsGapQ": "Transit réactif écarts hors norme",
+ "thresholdLostActProd": "Production active perdue",
+ "thresholdLostReaProd": "Production réactive perdue",
+ "thresholdLostActLoad": "Consommation active perdue",
+ "thresholdLostReaLoad": "Consommation réactive perdue",
+ "thresholdActTransit": "Transit actif",
+ "thresholdReaTransit": "Transit réactif",
+ "StateEstimationParametersDefaultBoundsSection": "Borne par défaut par niveau de tension (consos affines)",
+ "StateEstimationParametersDefaultFixedBoundsSection": "Borne par défaut par niveau de tension (consos fixes)",
+ "pmin": "P Min",
+ "pmax": "P Max",
+ "qmin": "Q Min",
+ "qmax": "Q Max"
}