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) && ( @@ -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" }