diff --git a/src/components/app-top-bar.jsx b/src/components/app-top-bar.jsx index 245a024b3f..e77cfaf642 100644 --- a/src/components/app-top-bar.jsx +++ b/src/components/app-top-bar.jsx @@ -70,6 +70,7 @@ const AppTopBar = ({ user, tabIndex, onChangeTab, userManager }) => { const theme = useSelector((state) => state[PARAM_THEME]); const studyUuid = useSelector((state) => state.studyUuid); const currentNode = useSelector((state) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state) => state.currentRootNetwork); const [isDialogSearchOpen, setIsDialogSearchOpen] = useState(false); const [appsAndUrls, setAppsAndUrls] = useState([]); @@ -175,6 +176,7 @@ const AppTopBar = ({ user, tabIndex, onChangeTab, userManager }) => { diff --git a/src/components/computing-status/use-all-computing-status.ts b/src/components/computing-status/use-all-computing-status.ts index ceff069737..ca5387e013 100644 --- a/src/components/computing-status/use-all-computing-status.ts +++ b/src/components/computing-status/use-all-computing-status.ts @@ -60,7 +60,7 @@ const dynamicSimulationStatusCompletions = ['dynamicSimulationResult', 'dynamicS const voltageInitStatusCompletions = ['voltageInitResult', 'voltageInit_failed']; const stateEstimationStatusCompletions = ['stateEstimationResult', 'stateEstimation_failed']; // this hook loads all current computation status into redux then keeps them up to date according to notifications -export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): void => { +export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID): void => { const securityAnalysisAvailability = useOptionalServiceStatus(OptionalServicesNames.SecurityAnalysis); const sensitivityAnalysisAvailability = useOptionalServiceStatus(OptionalServicesNames.SensitivityAnalysis); const nonEvacuatedEnergyAvailability = useOptionalServiceStatus(OptionalServicesNames.SensitivityAnalysis); @@ -72,6 +72,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchLoadFlowStatus, loadFlowStatusInvalidations, loadFlowStatusCompletions, @@ -82,6 +83,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchSecurityAnalysisStatus, securityAnalysisStatusInvalidations, securityAnalysisStatusCompletions, @@ -93,6 +95,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchSensitivityAnalysisStatus, sensitivityAnalysisStatusInvalidations, sensitivityAnalysisStatusCompletions, @@ -104,6 +107,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchNonEvacuatedEnergyStatus, nonEvacuatedEnergyStatusInvalidations, nonEvacuatedEnergyStatusCompletions, @@ -115,6 +119,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchShortCircuitAnalysisStatus, shortCircuitAnalysisStatusInvalidations, shortCircuitAnalysisStatusCompletions, @@ -126,6 +131,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchOneBusShortCircuitAnalysisStatus, oneBusShortCircuitAnalysisStatusInvalidations, oneBusShortCircuitAnalysisStatusCompletions, @@ -137,6 +143,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchDynamicSimulationStatus, dynamicSimulationStatusInvalidations, dynamicSimulationStatusCompletions, @@ -148,6 +155,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchVoltageInitStatus, voltageInitStatusInvalidations, voltageInitStatusCompletions, @@ -159,6 +167,7 @@ export const useAllComputingStatus = (studyUuid: UUID, currentNodeUuid: UUID): v useComputingStatus( studyUuid, currentNodeUuid, + currentRootNetworkUuid, fetchStateEstimationStatus, stateEstimationStatusInvalidations, stateEstimationStatusCompletions, diff --git a/src/components/computing-status/use-computing-status.ts b/src/components/computing-status/use-computing-status.ts index 0c23d97e34..cf9207e1c3 100644 --- a/src/components/computing-status/use-computing-status.ts +++ b/src/components/computing-status/use-computing-status.ts @@ -20,7 +20,8 @@ interface UseComputingStatusProps { ( studyUuid: UUID, nodeUuid: UUID, - fetcher: (studyUuid: UUID, nodeUuid: UUID) => Promise, + currentRootNetworkUuid: UUID, + fetcher: (studyUuid: UUID, nodeUuid: UUID, currentRootNetworkUuid: UUID) => Promise, invalidations: string[], completions: string[], resultConversion: (x: string) => RunningStatus, @@ -31,15 +32,17 @@ interface UseComputingStatusProps { interface LastUpdateProps { studyUpdatedForce: StudyUpdated; - fetcher: (studyUuid: UUID, nodeUuid: UUID) => Promise; + fetcher: (studyUuid: UUID, nodeUuid: UUID, currentRootNetworkUuid: UUID) => Promise; } function isWorthUpdate( studyUpdatedForce: StudyUpdated, - fetcher: (studyUuid: UUID, nodeUuid: UUID) => Promise, + fetcher: (studyUuid: UUID, nodeUuid: UUID, currentRootNetworkUuid: UUID) => Promise, lastUpdateRef: RefObject, nodeUuidRef: RefObject, + rootNetworkUuidRef: RefObject, nodeUuid: UUID, + currentRootNetworkUuid: UUID, invalidations: string[] ): boolean { const headers = studyUpdatedForce?.eventData?.headers; @@ -49,6 +52,9 @@ function isWorthUpdate( if (nodeUuidRef.current !== nodeUuid) { return true; } + if (rootNetworkUuidRef.current !== currentRootNetworkUuid) { + return true; + } if (fetcher && lastUpdateRef.current?.fetcher !== fetcher) { return true; } @@ -84,6 +90,7 @@ function isWorthUpdate( export const useComputingStatus: UseComputingStatusProps = ( studyUuid, nodeUuid, + currentRootNetworkUuid, fetcher, invalidations, completions, @@ -92,6 +99,8 @@ export const useComputingStatus: UseComputingStatusProps = ( optionalServiceAvailabilityStatus = OptionalServicesStatus.Up ) => { const nodeUuidRef = useRef(null); + const rootNetworkUuidRef = useRef(null); + const studyUpdatedForce = useSelector((state: AppState) => state.studyUpdated); const lastUpdateRef = useRef(null); const dispatch = useDispatch(); @@ -114,9 +123,14 @@ export const useComputingStatus: UseComputingStatusProps = ( dispatch(setLastCompletedComputation()); nodeUuidRef.current = nodeUuid; - fetcher(studyUuid, nodeUuid) + rootNetworkUuidRef.current = currentRootNetworkUuid; + fetcher(studyUuid, nodeUuid, currentRootNetworkUuid) .then((res: string) => { - if (!canceledRequest && nodeUuidRef.current === nodeUuid) { + if ( + !canceledRequest && + nodeUuidRef.current === nodeUuid && + rootNetworkUuidRef.current === currentRootNetworkUuid + ) { const status = resultConversion(res); dispatch(setComputingStatus(computingType, status)); if (isComputationCompleted(status)) { @@ -133,25 +147,49 @@ export const useComputingStatus: UseComputingStatusProps = ( return () => { canceledRequest = true; }; - }, [nodeUuid, fetcher, studyUuid, resultConversion, dispatch, computingType, isComputationCompleted]); + }, [ + nodeUuid, + currentRootNetworkUuid, + fetcher, + studyUuid, + resultConversion, + dispatch, + computingType, + isComputationCompleted, + ]); /* initial fetch and update */ useEffect(() => { - if (!studyUuid || !nodeUuid || optionalServiceAvailabilityStatus !== OptionalServicesStatus.Up) { + if ( + !studyUuid || + !nodeUuid || + !currentRootNetworkUuid || + optionalServiceAvailabilityStatus !== OptionalServicesStatus.Up + ) { return; } - const isUpdateForUs = isWorthUpdate( studyUpdatedForce, fetcher, lastUpdateRef, nodeUuidRef, + rootNetworkUuidRef, nodeUuid, + currentRootNetworkUuid, invalidations ); lastUpdateRef.current = { studyUpdatedForce, fetcher }; if (isUpdateForUs) { update(); } - }, [update, fetcher, nodeUuid, invalidations, studyUpdatedForce, studyUuid, optionalServiceAvailabilityStatus]); + }, [ + update, + fetcher, + nodeUuid, + invalidations, + currentRootNetworkUuid, + studyUpdatedForce, + studyUuid, + optionalServiceAvailabilityStatus, + ]); }; diff --git a/src/components/diagrams/diagram-pane.tsx b/src/components/diagrams/diagram-pane.tsx index cc86db304b..cc32e58651 100644 --- a/src/components/diagrams/diagram-pane.tsx +++ b/src/components/diagrams/diagram-pane.tsx @@ -48,7 +48,7 @@ import { AppState, CurrentTreeNode, DiagramState } from 'redux/reducer'; import { SLDMetadata, DiagramMetadata } from '@powsybl/network-viewer'; // Returns a callback that returns a promise -const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { +const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRootNetworkUuid: UUID) => { const { snackError } = useSnackMessage(); const paramUseName = useSelector((state: AppState) => state[PARAM_USE_NAME]); const { getNameOrId } = useNameOrId(); @@ -61,6 +61,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { ? getVoltageLevelSingleLineDiagram({ studyUuid: studyUuid, currentNodeUuid: currentNode?.id, + currentRootNetworkUuid: currentRootNetworkUuid, voltageLevelId: voltageLevelId, useName: paramUseName, centerLabel: networkVisuParams.singleLineDiagramParameters.centerLabel, @@ -73,6 +74,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { [ currentNode, studyUuid, + currentRootNetworkUuid, paramUseName, networkVisuParams.singleLineDiagramParameters.centerLabel, networkVisuParams.singleLineDiagramParameters.diagonalLabel, @@ -87,6 +89,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { ? getSubstationSingleLineDiagram({ studyUuid: studyUuid, currentNodeUuid: currentNode?.id, + currentRootNetworkUuid: currentRootNetworkUuid, substationId: voltageLevelId, useName: paramUseName, centerLabel: networkVisuParams.singleLineDiagramParameters.centerLabel, @@ -104,6 +107,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { networkVisuParams.singleLineDiagramParameters.substationLayout, paramUseName, currentNode, + currentRootNetworkUuid, language, ] ); @@ -113,12 +117,18 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode) => { ? getNetworkAreaDiagramUrl( studyUuid, currentNode?.id, + currentRootNetworkUuid, voltageLevelsIds, depth, networkVisuParams.networkAreaDiagramParameters.initNadWithGeoData ) : null, - [studyUuid, currentNode, networkVisuParams.networkAreaDiagramParameters.initNadWithGeoData] + [ + studyUuid, + currentNode, + currentRootNetworkUuid, + networkVisuParams.networkAreaDiagramParameters.initNadWithGeoData, + ] ); // this callback returns a promise @@ -305,6 +315,7 @@ const styles = { interface DiagramPaneProps { studyUuid: UUID; currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; showInSpreadsheet: (equipment: { equipmentId: string | null; equipmentType: EquipmentType | null }) => void; visible: boolean; } @@ -324,18 +335,25 @@ type DiagramView = { depth?: number; error?: string; nodeId?: UUID; + rootNetworkId?: UUID; additionalMetadata?: any; fetchSvg?: () => Promise>; }; -export function DiagramPane({ studyUuid, currentNode, showInSpreadsheet, visible }: DiagramPaneProps) { +export function DiagramPane({ + studyUuid, + currentNode, + currentRootNetworkUuid, + showInSpreadsheet, + visible, +}: DiagramPaneProps) { const { snackError } = useSnackMessage(); const dispatch = useDispatch(); const intl = useIntl(); const studyUpdatedForce = useSelector((state: AppState) => state.studyUpdated); const [views, setViews] = useState([]); const fullScreenDiagram = useSelector((state: AppState) => state.fullScreenDiagram); - const createView = useDisplayView(studyUuid, currentNode); + const createView = useDisplayView(studyUuid, currentNode, currentRootNetworkUuid); const diagramStates = useSelector((state: AppState) => state.diagramStates); const networkAreaDiagramDepth = useSelector((state: AppState) => state.networkAreaDiagramDepth); const previousNetworkAreaDiagramDepth = useRef(networkAreaDiagramDepth); @@ -354,7 +372,8 @@ export function DiagramPane({ studyUuid, currentNode, showInSpreadsheet, visible const { openDiagramView, closeDiagramView, closeDiagramViews } = useDiagram(); const currentNodeRef = useRef(); currentNodeRef.current = currentNode; - + const currentRootNetworkRef = useRef(); + currentRootNetworkRef.current = currentRootNetworkUuid; const viewsRef = useRef([]); viewsRef.current = views; /** @@ -797,14 +816,23 @@ export function DiagramPane({ studyUuid, currentNode, showInSpreadsheet, visible updateDiagramsByIds(allDiagramIds, true); }, [currentNode, updateDiagramsByIds]); + // This effect will trigger the diagrams' forced update // This effect will trigger the diagrams' forced update useEffect(() => { if (studyUpdatedForce.eventData.headers) { + if ( + studyUpdatedForce.eventData.headers['rootNetwork'] !== currentRootNetworkRef.current && + currentRootNetworkRef + ) { + return; + } if (studyUpdatedForce.eventData.headers['updateType'] === 'loadflowResult') { //TODO reload data more intelligently + updateDiagramsByCurrentNode(); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'study') { // FM if we want to reload data more precisely, we need more information from notifications + updateDiagramsByCurrentNode(); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'buildCompleted') { if (studyUpdatedForce.eventData.headers['node'] === currentNodeRef.current?.id) { diff --git a/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx b/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx index 3adfbfaa29..9e5e89b2b9 100644 --- a/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx +++ b/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx @@ -22,6 +22,7 @@ interface PositionDiagramPaneProps { onClose: () => void; voltageLevelId?: { id: UUID }; currentNodeUuid: UUID; + currentRootNetworkUuid: UUID; studyUuid: UUID; } @@ -30,6 +31,7 @@ const PositionDiagramPane: FC = ({ onClose, voltageLevelId, currentNodeUuid, + currentRootNetworkUuid, studyUuid, }) => { const useName = useSelector((state: AppState) => state[PARAM_USE_NAME]); @@ -47,6 +49,7 @@ const PositionDiagramPane: FC = ({ getVoltageLevelSingleLineDiagram({ studyUuid: studyUuid, currentNodeUuid: currentNodeUuid, + currentRootNetworkUuid: currentRootNetworkUuid, voltageLevelId: voltageLevelId?.id, useName: useName, centerLabel: networkVisuParams.singleLineDiagramParameters.centerLabel, @@ -58,6 +61,7 @@ const PositionDiagramPane: FC = ({ [ studyUuid, currentNodeUuid, + currentRootNetworkUuid, voltageLevelId?.id, useName, networkVisuParams.singleLineDiagramParameters.centerLabel, diff --git a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx index dd2a8b4d68..794b500aa5 100644 --- a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx +++ b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx @@ -127,6 +127,8 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { const diagramViewerRef = useRef(); const { snackError } = useSnackMessage(); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); + const [modificationInProgress, setModificationInProgress] = useState(false); const isAnyNodeBuilding = useIsAnyNodeBuilding(); const [locallySwitchedBreaker, setLocallySwitchedBreaker] = useState(); @@ -147,7 +149,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { isDiagramRunningOneBusShortcircuitAnalysis, displayOneBusShortcircuitAnalysisLoader, resetOneBusShortcircuitAnalysisLoader, - ] = useOneBusShortcircuitAnalysisLoader(props.diagramId, currentNode?.id!); + ] = useOneBusShortcircuitAnalysisLoader(props.diagramId, currentNode?.id!, currentRootNetworkUuid!); // dynamic simulation event configuration states const [equipmentToConfigDynamicSimulationEvent, setEquipmentToConfigDynamicSimulationEvent] = @@ -299,7 +301,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { dispatch(setComputingStatus(ComputingType.SHORT_CIRCUIT_ONE_BUS, RunningStatus.RUNNING)); displayOneBusShortcircuitAnalysisLoader(); dispatch(setComputationStarting(true)); - startShortCircuitAnalysis(studyUuid, currentNode?.id, busId) + startShortCircuitAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid, busId) .catch((error) => { snackError({ messageTxt: error.message, @@ -319,6 +321,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { displayOneBusShortcircuitAnalysisLoader, studyUuid, currentNode?.id, + currentRootNetworkUuid, snackError, resetOneBusShortcircuitAnalysisLoader, ] @@ -349,6 +352,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { fetchNetworkElementInfos( studyUuid, currentNode?.id, + currentRootNetworkUuid, EQUIPMENT_TYPES.HVDC_LINE, EQUIPMENT_INFOS_TYPES.MAP.type, equipmentId, @@ -370,7 +374,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { }); } }, - [studyUuid, currentNode?.id, snackError, handleOpenDeletionDialog, removeEquipment] + [studyUuid, currentNode?.id, currentRootNetworkUuid, snackError, handleOpenDeletionDialog, removeEquipment] ); const handleOpenDynamicSimulationEventDialog = useCallback( @@ -488,6 +492,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} defaultIdValue={equipmentToModify?.equipmentId} isUpdate={true} onClose={() => closeModificationDialog()} @@ -505,6 +510,7 @@ function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} defaultIdValue={equipmentToDelete?.equipmentId} isUpdate={true} onClose={() => closeDeletionDialog()} diff --git a/src/components/diagrams/use-one-bus-shortcircuit-analysis-loader.tsx b/src/components/diagrams/use-one-bus-shortcircuit-analysis-loader.tsx index 6be3eba70e..fdc1485673 100644 --- a/src/components/diagrams/use-one-bus-shortcircuit-analysis-loader.tsx +++ b/src/components/diagrams/use-one-bus-shortcircuit-analysis-loader.tsx @@ -43,7 +43,11 @@ const styles = { //the first function submits the sld data on hand to the redux store and the second function reset the redux store state type oneBusShortcircuitAnalysisLoader = [ReactElement, boolean, () => void, () => void]; -export function useOneBusShortcircuitAnalysisLoader(diagramId: string, nodeId: UUID): oneBusShortcircuitAnalysisLoader { +export function useOneBusShortcircuitAnalysisLoader( + diagramId: string, + nodeId: UUID, + rootNetworkUuid: UUID +): oneBusShortcircuitAnalysisLoader { const studyUpdatedForce = useSelector((state: AppState) => state.studyUpdated); const oneBusShortCircuitAnalysisDiagram = useSelector((state: AppState) => state.oneBusShortCircuitAnalysisDiagram); @@ -83,6 +87,9 @@ export function useOneBusShortcircuitAnalysisLoader(diagramId: string, nodeId: U useEffect(() => { if (studyUpdatedForce.eventData.headers) { + if (studyUpdatedForce.eventData.headers['rootNetwork'] !== rootNetworkUuid) { + return; + } if ( studyUpdatedForce.eventData.headers['updateType'] === 'oneBusShortCircuitAnalysisResult' || studyUpdatedForce.eventData.headers['updateType'] === 'oneBusShortCircuitAnalysis_failed' @@ -90,7 +97,7 @@ export function useOneBusShortcircuitAnalysisLoader(diagramId: string, nodeId: U resetOneBusShortcircuitAnalysisLoader(); } } - }, [resetOneBusShortcircuitAnalysisLoader, studyUpdatedForce]); + }, [resetOneBusShortcircuitAnalysisLoader, studyUpdatedForce, rootNetworkUuid]); return [ oneBusShortcircuitAnalysisLoaderMessage, diff --git a/src/components/dialogs/case-list-selector.jsx b/src/components/dialogs/case-list-selector.jsx new file mode 100644 index 0000000000..afc1e2abf3 --- /dev/null +++ b/src/components/dialogs/case-list-selector.jsx @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2020, 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 { useCallback, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; + +import { FormattedMessage, useIntl } from 'react-intl'; +import { PARAM_FAVORITE_CONTINGENCY_LISTS } from '../../utils/config-params'; +import { useSelector } from 'react-redux'; +import { ElementType } from '@gridsuite/commons-ui'; +import { useSnackMessage, CheckBoxList } from '@gridsuite/commons-ui'; +import { updateConfigParameter } from '../../services/config'; +import { fetchContingencyAndFiltersLists } from '../../services/directory'; +import { fetchContingencyCount } from '../../services/study'; +import { DirectoryItemSelector } from '@gridsuite/commons-ui'; +import { isNodeBuilt } from 'components/graph/util/model-functions'; +import DeleteIcon from '@mui/icons-material/Delete'; +import IconButton from '@mui/material/IconButton'; +import { toggleElementFromList } from 'components/utils/utils'; +import { Grid, DialogActions, Button, DialogTitle, Typography, Dialog, DialogContent, Alert } from '@mui/material'; + +function makeButton(onClick, message, disabled) { + return ( + + + + ); +} + +const CaseListSelector = (props) => { + const favoriteContingencyListUuids = useSelector((state) => state[PARAM_FAVORITE_CONTINGENCY_LISTS]); + + const currentNode = useSelector((state) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state) => state.currentRootNetwork); + + const [contingencyList, setContingencyList] = useState([]); + + const [simulatedContingencyCount, setSimulatedContingencyCount] = useState(0); + + const [checkedContingencyList, setCheckedContingencyList] = useState([]); + + const [favoriteSelectorOpen, setFavoriteSelectorOpen] = useState(false); + + const { snackError } = useSnackMessage(); + + const intl = useIntl(); + + const handleClose = () => { + props.onClose(); + }; + + const handleStart = () => { + props.onStart(checkedContingencyList.map((c) => c.id)); + }; + + const saveFavorites = useCallback( + (newList) => { + updateConfigParameter(PARAM_FAVORITE_CONTINGENCY_LISTS, newList) + .then() + .catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'paramsChangingError', + }); + }); + }, + [snackError] + ); + + useEffect(() => { + setSimulatedContingencyCount(null); + var discardResult = false; + if (isNodeBuilt(currentNode) && props.open) { + fetchContingencyCount( + props.studyUuid, + currentNode.id, + currentRootNetworkUuid, + checkedContingencyList.map((c) => c.id) + ).then((contingencyCount) => { + if (!discardResult) { + setSimulatedContingencyCount(contingencyCount); + } + }); + } + return () => { + discardResult = true; + }; + }, [props.open, props.studyUuid, currentNode, currentRootNetworkUuid, checkedContingencyList]); + + useEffect(() => { + if (favoriteContingencyListUuids && favoriteContingencyListUuids.length > 0 && props.open) { + fetchContingencyAndFiltersLists(favoriteContingencyListUuids) + .then((res) => { + const mapCont = res.reduce((map, obj) => { + map[obj.elementUuid] = { + id: obj.elementUuid, + type: obj.type, + name: obj.elementName, + }; + return map; + }, {}); + setContingencyList( + favoriteContingencyListUuids + .map((id) => mapCont[id]) + .filter((item) => item !== undefined) + .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())) + ); + }) + .catch(() => { + snackError({ + headerId: 'getContingencyListError', + }); + }); + } else { + setContingencyList([]); + } + }, [favoriteContingencyListUuids, snackError, props.open]); + + function getSimulatedContingencyCountLabel() { + return simulatedContingencyCount != null ? simulatedContingencyCount : '...'; + } + + const handleAddFavorite = () => { + setFavoriteSelectorOpen(true); + }; + + const removeFromFavorites = useCallback( + (toRemove) => { + const toRemoveIdsSet = new Set(toRemove.map((e) => e.id)); + saveFavorites(contingencyList.map((e) => e.id).filter((id) => !toRemoveIdsSet.has(id))); + + setCheckedContingencyList((oldChecked) => oldChecked.filter((item) => !toRemoveIdsSet.has(item.id))); + }, + [contingencyList, saveFavorites] + ); + + const addFavorites = (favorites) => { + if (favorites && favorites.length > 0) { + // avoid duplicates here + const newFavoriteIdsSet = new Set([...favoriteContingencyListUuids, ...favorites.map((item) => item.id)]); + saveFavorites(Array.from([...newFavoriteIdsSet])); + } + setFavoriteSelectorOpen(false); + }; + + const handleSecondaryAction = useCallback( + (item, isItemHovered) => + isItemHovered && ( + { + e.stopPropagation(); + removeFromFavorites([item]); + }} + size={'small'} + > + + + ), + [removeFromFavorites] + ); + + return ( + <> + + + + + + + + v.id} + getItemLabel={(v) => v.name} + selectedItems={checkedContingencyList} + onSelectionChange={setCheckedContingencyList} + secondaryAction={handleSecondaryAction} + onItemClick={(contingencyList) => + setCheckedContingencyList((oldCheckedElements) => [ + ...toggleElementFromList(contingencyList, oldCheckedElements, (element) => element.id), + ]) + } + /> + + + + + + {makeButton(handleClose, 'close', false)} + {makeButton(handleAddFavorite, 'AddContingencyList', false)} + {makeButton( + () => removeFromFavorites(checkedContingencyList), + 'DeleteContingencyList', + checkedContingencyList.length === 0 + )} + {makeButton(handleStart, 'Execute', simulatedContingencyCount === 0)} + + + + + ); +}; + +CaseListSelector.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onStart: PropTypes.func.isRequired, + studyUuid: PropTypes.string, + currentNodeUuid: PropTypes.string, +}; + +export default CaseListSelector; diff --git a/src/components/dialogs/connectivity/branch-connectivity-form.tsx b/src/components/dialogs/connectivity/branch-connectivity-form.tsx index 2bad50dd8b..15ea1de722 100644 --- a/src/components/dialogs/connectivity/branch-connectivity-form.tsx +++ b/src/components/dialogs/connectivity/branch-connectivity-form.tsx @@ -18,6 +18,7 @@ import GridItem from '../commons/grid-item'; interface BranchConnectivityFormProps { currentNode: CurrentTreeNode; studyUuid: UUID; + currentRootNetworkUuid: UUID; isModification?: boolean; previousValues?: any; } @@ -25,10 +26,11 @@ interface BranchConnectivityFormProps { const BranchConnectivityForm: FunctionComponent = ({ currentNode, studyUuid, + currentRootNetworkUuid, isModification = false, previousValues, }) => { - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode.id); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode.id, currentRootNetworkUuid); const id1 = `${CONNECTIVITY}.${CONNECTIVITY_1}`; const id2 = `${CONNECTIVITY}.${CONNECTIVITY_2}`; @@ -37,6 +39,7 @@ const BranchConnectivityForm: FunctionComponent = ( id={id1} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} voltageLevelOptions={voltageLevelOptions} withPosition={true} isEquipmentModification={isModification} @@ -52,6 +55,7 @@ const BranchConnectivityForm: FunctionComponent = ( id={id2} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} voltageLevelOptions={voltageLevelOptions} withPosition={true} isEquipmentModification={isModification} diff --git a/src/components/dialogs/connectivity/connectivity-form.jsx b/src/components/dialogs/connectivity/connectivity-form.jsx index ae8683e5ae..0008c8f60c 100644 --- a/src/components/dialogs/connectivity/connectivity-form.jsx +++ b/src/components/dialogs/connectivity/connectivity-form.jsx @@ -57,6 +57,7 @@ export const ConnectivityForm = ({ newBusOrBusbarSectionOptions = [], studyUuid, currentNode, + currentRootNetworkUuid, onVoltageLevelChangeCallback = undefined, isEquipmentModification = false, previousValues, @@ -79,16 +80,28 @@ export const ConnectivityForm = ({ return; } if (watchVoltageLevelId) { - fetchBusesOrBusbarSectionsForVoltageLevel(studyUuid, currentNodeUuid, watchVoltageLevelId).then( - (busesOrbusbarSections) => { - setBusOrBusbarSectionOptions(busesOrbusbarSections); - } - ); + fetchBusesOrBusbarSectionsForVoltageLevel( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + watchVoltageLevelId + ).then((busesOrbusbarSections) => { + setBusOrBusbarSectionOptions(busesOrbusbarSections); + }); } else { setBusOrBusbarSectionOptions([]); setValue(`${id}.${BUS_OR_BUSBAR_SECTION}`, null); } - }, [watchVoltageLevelId, studyUuid, currentNodeUuid, voltageLevelOptions, setValue, id, isEquipmentModification]); + }, [ + watchVoltageLevelId, + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + voltageLevelOptions, + setValue, + id, + isEquipmentModification, + ]); useEffect(() => { if (isEquipmentModification) { @@ -295,6 +308,7 @@ export const ConnectivityForm = ({ onClose={handleCloseDiagramPane} voltageLevelId={{ id: watchVoltageLevelId }} currentNodeUuid={currentNodeUuid} + currentRootNetworkUuid={currentRootNetworkUuid} /> ); diff --git a/src/components/dialogs/contingency-list-selector.jsx b/src/components/dialogs/contingency-list-selector.jsx index 2f8b2bf0f1..425089bd47 100644 --- a/src/components/dialogs/contingency-list-selector.jsx +++ b/src/components/dialogs/contingency-list-selector.jsx @@ -38,6 +38,7 @@ const ContingencyListSelector = (props) => { const favoriteContingencyListUuids = useSelector((state) => state[PARAM_FAVORITE_CONTINGENCY_LISTS]); const currentNode = useSelector((state) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state) => state.currentRootNetwork); const [contingencyList, setContingencyList] = useState([]); @@ -80,6 +81,7 @@ const ContingencyListSelector = (props) => { fetchContingencyCount( props.studyUuid, currentNode.id, + currentRootNetworkUuid, checkedContingencyList.map((c) => c.id) ).then((contingencyCount) => { if (!discardResult) { @@ -90,7 +92,7 @@ const ContingencyListSelector = (props) => { return () => { discardResult = true; }; - }, [props.open, props.studyUuid, currentNode, checkedContingencyList]); + }, [props.open, props.studyUuid, currentNode, checkedContingencyList, currentRootNetworkUuid]); useEffect(() => { if (favoriteContingencyListUuids && favoriteContingencyListUuids.length > 0 && props.open) { diff --git a/src/components/dialogs/dynamicsimulation/event/dynamic-simulation-event-dialog.tsx b/src/components/dialogs/dynamicsimulation/event/dynamic-simulation-event-dialog.tsx index cfb3921a52..b5ea5e4f95 100644 --- a/src/components/dialogs/dynamicsimulation/event/dynamic-simulation-event-dialog.tsx +++ b/src/components/dialogs/dynamicsimulation/event/dynamic-simulation-event-dialog.tsx @@ -38,6 +38,7 @@ export const DynamicSimulationEventDialog = (props: DynamicSimulationEventDialog const { snackError } = useSnackMessage(); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const currentNodeId = currentNode?.id; const [dataFetchStatus, setDataFetchStatus] = useState(FetchStatus.IDLE); const [event, setEvent] = useState(); @@ -86,15 +87,15 @@ export const DynamicSimulationEventDialog = (props: DynamicSimulationEventDialog // load event for equipment useEffect(() => { - if (!studyUuid || !currentNodeId) { + if (!studyUuid || !currentNodeId || !currentRootNetworkUuid) { return; } setDataFetchStatus(FetchStatus.RUNNING); - fetchDynamicSimulationEvent(studyUuid, currentNodeId, equipmentId).then((event) => { + fetchDynamicSimulationEvent(studyUuid, currentNodeId, currentRootNetworkUuid, equipmentId).then((event) => { setDataFetchStatus(FetchStatus.SUCCEED); setEvent(event); }); - }, [currentNodeId, equipmentId, studyUuid, reset]); + }, [currentNodeId, equipmentId, currentRootNetworkUuid, studyUuid, reset]); // reset form data when event available after fetch useEffect(() => { @@ -117,7 +118,7 @@ export const DynamicSimulationEventDialog = (props: DynamicSimulationEventDialog // submit form const handleSubmit = useCallback( (formObj: { [KEY in EventPropertyName]: any }) => { - if (!studyUuid || !currentNodeId) { + if (!studyUuid || !currentNodeId || !currentRootNetworkUuid) { return; } // formObj to EventProperty[] @@ -160,14 +161,24 @@ export const DynamicSimulationEventDialog = (props: DynamicSimulationEventDialog properties, }; - saveDynamicSimulationEvent(studyUuid, currentNodeId, submitEvent).catch((error) => { + saveDynamicSimulationEvent(studyUuid, currentNodeId, currentRootNetworkUuid, submitEvent).catch((error) => { snackError({ messageTxt: error.message, headerId: 'DynamicSimulationEventSaveError', }); }); }, - [currentNodeId, equipmentId, equipmentType, snackError, studyUuid, eventType, event, eventDefinition] + [ + currentNodeId, + equipmentId, + currentRootNetworkUuid, + equipmentType, + snackError, + studyUuid, + eventType, + event, + eventDefinition, + ] ); const waitingOpen = useOpenShortWaitFetching({ diff --git a/src/components/dialogs/equipment-id/equipment-id-selector.jsx b/src/components/dialogs/equipment-id/equipment-id-selector.jsx index b05e5e86e0..0f3b38de91 100644 --- a/src/components/dialogs/equipment-id/equipment-id-selector.jsx +++ b/src/components/dialogs/equipment-id/equipment-id-selector.jsx @@ -29,6 +29,7 @@ const styles = { export const EquipmentIdSelector = ({ studyUuid, currentNode, + currentRootNetworkUuid, defaultValue, setSelectedId, equipmentType, @@ -43,10 +44,12 @@ export const EquipmentIdSelector = ({ const [selectedValue, setSelectedValue] = useState(null); useEffect(() => { - fetchEquipmentsIds(studyUuid, currentNodeUuid, undefined, equipmentType, true).then((values) => { - setEquipmentOptions(values.sort((a, b) => a.localeCompare(b))); - }); - }, [studyUuid, currentNodeUuid, equipmentType]); + fetchEquipmentsIds(studyUuid, currentNodeUuid, currentRootNetworkUuid, undefined, equipmentType, true).then( + (values) => { + setEquipmentOptions(values.sort((a, b) => a.localeCompare(b))); + } + ); + }, [studyUuid, currentNodeUuid, currentRootNetworkUuid, equipmentType]); // We go through this effect to force a rerender and display the loading icon. useEffect(() => { diff --git a/src/components/dialogs/equipment-search-dialog.tsx b/src/components/dialogs/equipment-search-dialog.tsx index e4b8785693..b2cd924ce1 100644 --- a/src/components/dialogs/equipment-search-dialog.tsx +++ b/src/components/dialogs/equipment-search-dialog.tsx @@ -27,6 +27,7 @@ interface EquipmentSearchDialogProps { onSelectionChange: (equipment: EquipmentInfos) => void; equipmentType: EquipmentType; currentNodeUuid: UUID; + currentRootNetworkUuid: UUID; } /** @@ -36,6 +37,7 @@ interface EquipmentSearchDialogProps { * @param {Function} onSelectionChange: callback when the selection changes * @param {String} equipmentType: the type of equipment we want to search * @param {String} currentNodeUuid: the node selected + * @param {String} currentRootNetworkUuid: the root network UUID */ const EquipmentSearchDialog: FC = ({ open, @@ -43,6 +45,7 @@ const EquipmentSearchDialog: FC = ({ onSelectionChange, equipmentType, currentNodeUuid, + currentRootNetworkUuid, }) => { const intl = useIntl(); const studyUuid = useSelector((state: AppState) => state.studyUuid); @@ -50,6 +53,7 @@ const EquipmentSearchDialog: FC = ({ // @ts-expect-error TODO: manage null case studyUuid: studyUuid, nodeUuid: currentNodeUuid, + currentRootNetworkUuid: currentRootNetworkUuid, inUpstreamBuiltParentNode: true, equipmentType: equipmentType, }); diff --git a/src/components/dialogs/export-dialog.jsx b/src/components/dialogs/export-dialog.jsx index ad04428fc0..0f58990a4c 100644 --- a/src/components/dialogs/export-dialog.jsx +++ b/src/components/dialogs/export-dialog.jsx @@ -47,7 +47,7 @@ const STRING_LIST = 'STRING_LIST'; * @param {String} title Title of the dialog */ -const ExportDialog = ({ open, onClose, onClick, studyUuid, nodeUuid, title }) => { +const ExportDialog = ({ open, onClose, onClick, studyUuid, nodeUuid, rootNetworkUuid, title }) => { const [formatsWithParameters, setFormatsWithParameters] = useState([]); const [selectedFormat, setSelectedFormat] = useState(''); const [loading, setLoading] = useState(false); @@ -122,7 +122,7 @@ const ExportDialog = ({ open, onClose, onClick, studyUuid, nodeUuid, title }) => }, []); const handleExportClick = () => { if (selectedFormat) { - const downloadUrl = getExportUrl(studyUuid, nodeUuid, selectedFormat); + const downloadUrl = getExportUrl(studyUuid, nodeUuid, rootNetworkUuid, selectedFormat); let suffix; const urlSearchParams = new URLSearchParams(); if (Object.keys(currentParameters).length > 0) { @@ -245,6 +245,7 @@ ExportDialog.propTypes = { onClick: PropTypes.func.isRequired, studyUuid: PropTypes.string, nodeUuid: PropTypes.string, + rootNetworkUuid: PropTypes.string, title: PropTypes.string.isRequired, }; diff --git a/src/components/dialogs/form-search-copy-hook.js b/src/components/dialogs/form-search-copy-hook.js index 9c41e7cff6..7f7a911a15 100644 --- a/src/components/dialogs/form-search-copy-hook.js +++ b/src/components/dialogs/form-search-copy-hook.js @@ -11,7 +11,14 @@ import { useSnackMessage } from '@gridsuite/commons-ui'; import { EQUIPMENT_INFOS_TYPES } from '../utils/equipment-types'; import { fetchNetworkElementInfos } from '../../services/study/network'; -export const useFormSearchCopy = ({ studyUuid, currentNodeUuid, toFormValues, setFormValues, elementType }) => { +export const useFormSearchCopy = ({ + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + toFormValues, + setFormValues, + elementType, +}) => { const intl = useIntl(); const { snackInfo, snackError } = useSnackMessage(); @@ -23,6 +30,7 @@ export const useFormSearchCopy = ({ studyUuid, currentNodeUuid, toFormValues, se const fetchElementPromise = fetchNetworkElementInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, elementType, EQUIPMENT_INFOS_TYPES.FORM.type, element.id, diff --git a/src/components/dialogs/import-case-dialog.tsx b/src/components/dialogs/import-case-dialog.tsx new file mode 100644 index 0000000000..0c2949231c --- /dev/null +++ b/src/components/dialogs/import-case-dialog.tsx @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024, 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 { useIntl } from 'react-intl'; +import { ElementType, DirectoryItemSelector, TreeViewFinderNodeProps } from '@gridsuite/commons-ui'; +import { FunctionComponent } from 'react'; + +interface ImportCaseDialogProps { + open: boolean; + onClose: () => void; + onSelectCase: (selectedElement: TreeViewFinderNodeProps) => void; +} + +const ImportCaseDialog: FunctionComponent = ({ open, onClose, onSelectCase }) => { + const intl = useIntl(); + + const processSelectedElements = (selectedElements: TreeViewFinderNodeProps[]) => { + if (selectedElements && selectedElements.length > 0) { + const selectedCase = selectedElements[0]; // Assuming single selection + onSelectCase(selectedCase); + } + onClose(); + }; + + return ( + + ); +}; + +export default ImportCaseDialog; diff --git a/src/components/dialogs/network-modifications/battery/creation/battery-creation-dialog.jsx b/src/components/dialogs/network-modifications/battery/creation/battery-creation-dialog.jsx index d1cf5d35a1..340ce8abf7 100644 --- a/src/components/dialogs/network-modifications/battery/creation/battery-creation-dialog.jsx +++ b/src/components/dialogs/network-modifications/battery/creation/battery-creation-dialog.jsx @@ -93,7 +93,15 @@ const formSchema = yup .concat(creationPropertiesSchema) .required(); -const BatteryCreationDialog = ({ editData, currentNode, studyUuid, isUpdate, editDataFetchStatus, ...dialogProps }) => { +const BatteryCreationDialog = ({ + editData, + currentNode, + studyUuid, + currentRootNetworkUuid, + isUpdate, + editDataFetchStatus, + ...dialogProps +}) => { const currentNodeUuid = currentNode.id; const { snackError } = useSnackMessage(); @@ -135,6 +143,7 @@ const BatteryCreationDialog = ({ editData, currentNode, studyUuid, isUpdate, edi const searchCopy = useFormSearchCopy({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, toFormValues: (data) => data, setFormValues: fromSearchCopyToFormValues, elementType: EQUIPMENT_TYPES.BATTERY, @@ -235,7 +244,11 @@ const BatteryCreationDialog = ({ editData, currentNode, studyUuid, isUpdate, edi isDataFetching={isUpdate && editDataFetchStatus === FetchStatus.RUNNING} {...dialogProps} > - + @@ -253,6 +267,7 @@ BatteryCreationDialog.propTypes = { editData: PropTypes.object, studyUuid: PropTypes.string, currentNode: PropTypes.object, + currentRootNetworkUuid: PropTypes.string, isUpdate: PropTypes.bool, editDataFetchStatus: PropTypes.string, }; diff --git a/src/components/dialogs/network-modifications/battery/creation/battery-creation-form.jsx b/src/components/dialogs/network-modifications/battery/creation/battery-creation-form.jsx index ea4b5456e9..1095b1473b 100644 --- a/src/components/dialogs/network-modifications/battery/creation/battery-creation-form.jsx +++ b/src/components/dialogs/network-modifications/battery/creation/battery-creation-form.jsx @@ -24,8 +24,8 @@ import useVoltageLevelsListInfos from '../../../../../hooks/use-voltage-levels-l import GridItem from '../../../commons/grid-item'; import GridSection from '../../../commons/grid-section'; -const BatteryCreationForm = ({ studyUuid, currentNode }) => { - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode.id); +const BatteryCreationForm = ({ studyUuid, currentNode, currentRootNetworkUuid }) => { + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode.id, currentRootNetworkUuid); const batteryIdField = ( @@ -39,6 +39,7 @@ const BatteryCreationForm = ({ studyUuid, currentNode }) => { withPosition={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} /> ); diff --git a/src/components/dialogs/network-modifications/battery/modification/battery-modification-dialog.jsx b/src/components/dialogs/network-modifications/battery/modification/battery-modification-dialog.jsx index ff73c17176..2d900b99d8 100644 --- a/src/components/dialogs/network-modifications/battery/modification/battery-modification-dialog.jsx +++ b/src/components/dialogs/network-modifications/battery/modification/battery-modification-dialog.jsx @@ -110,6 +110,7 @@ const BatteryModificationDialog = ({ editData, defaultIdValue, currentNode, + currentRootNetworkUuid, studyUuid, isUpdate, editDataFetchStatus, @@ -199,6 +200,7 @@ const BatteryModificationDialog = ({ fetchNetworkElementInfos( studyUuid, currentNode.id, + currentRootNetworkUuid, EQUIPMENT_TYPES.BATTERY, EQUIPMENT_INFOS_TYPES.FORM.type, equipmentId, @@ -245,7 +247,7 @@ const BatteryModificationDialog = ({ setBatteryToModify(null); } }, - [studyUuid, currentNode, getValues, setValue, setValuesAndEmptyOthers, reset, editData] + [studyUuid, currentNode, currentRootNetworkUuid, getValues, setValue, setValuesAndEmptyOthers, reset, editData] ); useEffect(() => { @@ -323,6 +325,7 @@ const BatteryModificationDialog = ({ diff --git a/src/components/dialogs/network-modifications/delete-attaching-line/delete-attaching-line-form.jsx b/src/components/dialogs/network-modifications/delete-attaching-line/delete-attaching-line-form.jsx index faab7acd59..426ac10a35 100644 --- a/src/components/dialogs/network-modifications/delete-attaching-line/delete-attaching-line-form.jsx +++ b/src/components/dialogs/network-modifications/delete-attaching-line/delete-attaching-line-form.jsx @@ -20,20 +20,22 @@ import { fetchEquipmentsIds } from '../../../../services/study/network-map'; import GridSection from '../../commons/grid-section'; import GridItem from '../../commons/grid-item'; -const DeleteAttachingLineForm = ({ studyUuid, currentNode }) => { +const DeleteAttachingLineForm = ({ studyUuid, currentNode, currentRootNetworkUuid }) => { const [linesOptions, setLinesOptions] = useState([]); useEffect(() => { - fetchEquipmentsIds(studyUuid, currentNode.id, undefined, 'LINE', true).then((values) => { - setLinesOptions( - values - .sort((a, b) => a.localeCompare(b)) - .map((value) => { - return { id: value }; - }) - ); - }); - }, [studyUuid, currentNode?.id]); + fetchEquipmentsIds(studyUuid, currentNode.id, currentRootNetworkUuid, undefined, 'LINE', true).then( + (values) => { + setLinesOptions( + values + .sort((a, b) => a.localeCompare(b)) + .map((value) => { + return { id: value }; + }) + ); + } + ); + }, [studyUuid, currentNode?.id, currentRootNetworkUuid]); const lineToAttachTo1Field = ( - + ); @@ -144,6 +150,7 @@ const EquipmentDeletionDialog = ({ EquipmentDeletionDialog.propTypes = { studyUuid: PropTypes.string, currentNode: PropTypes.object, + currentRootNetworkUuid: PropTypes.string, editData: PropTypes.object, isUpdate: PropTypes.bool, defaultIdValue: PropTypes.string, diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx index 78dda4deaa..162818b4e2 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx @@ -25,7 +25,7 @@ import { fetchEquipmentsIds } from '../../../../services/study/network-map'; import useGetLabelEquipmentTypes from '../../../../hooks/use-get-label-equipment-types'; import GridItem from '../../commons/grid-item'; -const DeleteEquipmentForm = ({ studyUuid, currentNode, editData }) => { +const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, editData }) => { const { snackError } = useSnackMessage(); const editedIdRef = useRef(null); const currentTypeRef = useRef(null); @@ -66,7 +66,7 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, editData }) => { currentTypeRef.current = watchType; } let ignore = false; - fetchEquipmentsIds(studyUuid, currentNode?.id, undefined, watchType, true) + fetchEquipmentsIds(studyUuid, currentNode?.id, currentRootNetworkUuid, undefined, watchType, true) .then((vals) => { // check race condition here if (!ignore) { @@ -83,10 +83,10 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, editData }) => { ignore = true; }; } - }, [studyUuid, currentNode?.id, watchType, snackError]); + }, [studyUuid, currentNode?.id, currentRootNetworkUuid, watchType, snackError]); useEffect(() => { - if (studyUuid && currentNode?.id) { + if (studyUuid && currentNode?.id && currentRootNetworkUuid) { if (editData?.equipmentId) { if (editedIdRef.current === null) { // The first time we render an edition, we want to merge the @@ -104,6 +104,7 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, editData }) => { hvdcLccSpecificUpdate( studyUuid, currentNode?.id, + currentRootNetworkUuid, watchEquipmentId, watchEquipmentId === editedIdRef.current ? editData : null ); @@ -111,7 +112,16 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, editData }) => { setValue(DELETION_SPECIFIC_DATA, null); } } - }, [studyUuid, currentNode?.id, watchEquipmentId, snackError, setValue, hvdcLccSpecificUpdate, editData]); + }, [ + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + watchEquipmentId, + snackError, + setValue, + hvdcLccSpecificUpdate, + editData, + ]); const handleChange = useCallback(() => { setValue(EQUIPMENT_ID, null); diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js index 3837004c80..fdc1975186 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js @@ -78,8 +78,8 @@ const useHvdcLccDeletion = () => { ); const specificUpdate = useCallback( - (studyUuid, nodeId, equipmentId, editData) => { - fetchHvdcLineWithShuntCompensators(studyUuid, nodeId, equipmentId) + (studyUuid, nodeId, currentRootNetworkUuid, equipmentId, editData) => { + fetchHvdcLineWithShuntCompensators(studyUuid, nodeId, currentRootNetworkUuid, equipmentId) .then((hvdcLineData) => { updateMcsLists(hvdcLineData, editData); }) diff --git a/src/components/dialogs/network-modifications/generator/creation/generator-creation-dialog.jsx b/src/components/dialogs/network-modifications/generator/creation/generator-creation-dialog.jsx index da967689ed..dee749311d 100644 --- a/src/components/dialogs/network-modifications/generator/creation/generator-creation-dialog.jsx +++ b/src/components/dialogs/network-modifications/generator/creation/generator-creation-dialog.jsx @@ -127,6 +127,7 @@ const GeneratorCreationDialog = ({ editData, currentNode, studyUuid, + currentRootNetworkUuid, isUpdate, editDataFetchStatus, ...dialogProps @@ -193,6 +194,7 @@ const GeneratorCreationDialog = ({ const searchCopy = useFormSearchCopy({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, toFormValues: (data) => data, setFormValues: fromSearchCopyToFormValues, elementType: EQUIPMENT_TYPES.GENERATOR, @@ -336,6 +338,7 @@ const GeneratorCreationDialog = ({ equipmentType={'GENERATOR'} onSelectionChange={searchCopy.handleSelectionChange} currentNodeUuid={currentNodeUuid} + currentRootNetworkUuid={currentRootNetworkUuid} /> diff --git a/src/components/dialogs/network-modifications/generator/creation/generator-creation-form.jsx b/src/components/dialogs/network-modifications/generator/creation/generator-creation-form.jsx index 2b85c99e1a..d636544778 100644 --- a/src/components/dialogs/network-modifications/generator/creation/generator-creation-form.jsx +++ b/src/components/dialogs/network-modifications/generator/creation/generator-creation-form.jsx @@ -37,9 +37,9 @@ import useVoltageLevelsListInfos from '../../../../../hooks/use-voltage-levels-l import GridItem from '../../../commons/grid-item'; import GridSection from '../../../commons/grid-section'; -const GeneratorCreationForm = ({ studyUuid, currentNode }) => { +const GeneratorCreationForm = ({ studyUuid, currentNode, currentRootNetworkUuid }) => { const currentNodeUuid = currentNode?.id; - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid, currentRootNetworkUuid); const generatorIdField = ( @@ -65,6 +65,7 @@ const GeneratorCreationForm = ({ studyUuid, currentNode }) => { withPosition={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} /> ); @@ -132,6 +133,7 @@ const GeneratorCreationForm = ({ studyUuid, currentNode }) => { diff --git a/src/components/dialogs/network-modifications/generator/modification/generator-modification-dialog.jsx b/src/components/dialogs/network-modifications/generator/modification/generator-modification-dialog.jsx index da501f240f..06486408ca 100644 --- a/src/components/dialogs/network-modifications/generator/modification/generator-modification-dialog.jsx +++ b/src/components/dialogs/network-modifications/generator/modification/generator-modification-dialog.jsx @@ -135,6 +135,7 @@ const GeneratorModificationDialog = ({ editData, // contains data when we try to edit an existing hypothesis from the current node's list defaultIdValue, // Used to pre-select an equipmentId when calling this dialog from the SLD currentNode, + currentRootNetworkUuid, studyUuid, isUpdate, editDataFetchStatus, @@ -242,6 +243,7 @@ const GeneratorModificationDialog = ({ fetchNetworkElementInfos( studyUuid, currentNode.id, + currentRootNetworkUuid, EQUIPMENT_TYPES.GENERATOR, EQUIPMENT_INFOS_TYPES.FORM.type, equipmentId, @@ -284,7 +286,7 @@ const GeneratorModificationDialog = ({ setGeneratorToModify(null); } }, - [studyUuid, currentNode, reset, getValues, setValue, setValuesAndEmptyOthers, editData] + [studyUuid, currentNode, currentRootNetworkUuid, reset, getValues, setValue, setValuesAndEmptyOthers, editData] ); useEffect(() => { @@ -391,6 +393,7 @@ const GeneratorModificationDialog = ({ { const currentNodeUuid = currentNode?.id; const intl = useIntl(); - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid, currentRootNetworkUuid); const energySourceLabelId = getEnergySourceLabel(generatorToModify?.energySource); const previousEnergySourceLabel = energySourceLabelId @@ -181,6 +182,7 @@ const GeneratorModificationForm = ({ withPosition={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} isEquipmentModification={true} previousValues={generatorToModify} /> @@ -232,6 +234,7 @@ const GeneratorModificationForm = ({ { - if (studyUuid && currentNode.id) { - fetchVoltageLevelsListInfos(studyUuid, currentNode.id) + if (studyUuid && currentNode.id && currentRootNetworkUuid) { + fetchVoltageLevelsListInfos(studyUuid, currentNode.id, currentRootNetworkUuid) .then((values) => { setVoltageLevelOptions(values.sort((a, b) => a.id.localeCompare(b.id))); }) @@ -89,7 +90,7 @@ const RegulatingTerminalModificationDialog = ({ console.error('Error fetching voltage levels: ', error); }); } - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const onSubmit = useCallback( (voltageRegulationGenerator) => { @@ -125,6 +126,7 @@ const RegulatingTerminalModificationDialog = ({ voltageLevelOptions={voltageLevelOptions} equipmentSectionTypeDefaultValue={''} currentNodeUuid={currentNode.id} + currentRootNetworkUuid={currentRootNetworkUuid} studyUuid={studyUuid} previousRegulatingTerminalValue={previousData?.regulatingTerminalVlId} previousEquipmentSectionTypeValue={getTapChangerEquipmentSectionTypeValue(previousData)} @@ -161,6 +163,7 @@ RegulatingTerminalModificationDialog.propTypes = { data: PropTypes.object, studyUuid: PropTypes.string, currentNode: PropTypes.object, + currentRootNetworkUuid: PropTypes.string, onModifyRegulatingTerminalGenerator: PropTypes.func, }; diff --git a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-converter-station.tsx b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-converter-station.tsx index 8776060cf9..1d0c3fe1da 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-converter-station.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-converter-station.tsx @@ -211,6 +211,7 @@ interface LccConverterStationProps { id: string; stationLabel: string; currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; studyUuid: UUID; } @@ -219,8 +220,9 @@ export default function LccConverterStation({ stationLabel, currentNode, studyUuid, + currentRootNetworkUuid, }: Readonly) { - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode?.id); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode?.id, currentRootNetworkUuid); const stationIdField = ; @@ -233,6 +235,7 @@ export default function LccConverterStation({ withPosition={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} previousValues={undefined} /> ); diff --git a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-dialog.tsx b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-dialog.tsx index 3ab63a1239..75463ba9d3 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-dialog.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-dialog.tsx @@ -114,6 +114,7 @@ export interface LccCreationDialogProps extends Partial { editData: LccCreationInfos; currentNode: CurrentTreeNode; studyUuid: UUID; + currentRootNetworkUuid: UUID; isUpdate: boolean; editDataFetchStatus: FetchStatus; } @@ -122,6 +123,7 @@ export function LccCreationDialog({ editData, currentNode, studyUuid, + currentRootNetworkUuid, isUpdate, editDataFetchStatus, ...dialogProps @@ -159,6 +161,7 @@ export function LccCreationDialog({ const searchCopy = useFormSearchCopy({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, toFormValues: fromSearchCopyToFormValues, setFormValues: (data: LccCreationSchemaForm) => { reset(data, { keepDefaultValues: true }); @@ -267,13 +270,19 @@ export function LccCreationDialog({ }} {...dialogProps} > - + diff --git a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-form.tsx b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-form.tsx index c15b9074f3..3e2a84e55f 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-form.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-form.tsx @@ -16,8 +16,14 @@ interface LccCreationFormProps { tabIndex: number; studyUuid: UUID; currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; } -export default function LccCreationForm({ tabIndex, studyUuid, currentNode }: Readonly) { +export default function LccCreationForm({ + tabIndex, + studyUuid, + currentRootNetworkUuid, + currentNode, +}: Readonly) { return ( <> diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/creation/two-windings-transformer-creation-dialog.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/creation/two-windings-transformer-creation-dialog.jsx index e59cd0ba9d..0172505323 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/creation/two-windings-transformer-creation-dialog.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/creation/two-windings-transformer-creation-dialog.jsx @@ -152,6 +152,7 @@ const TwoWindingsTransformerCreationDialog = ({ editData, studyUuid, currentNode, + currentRootNetworkUuid, isUpdate, editDataFetchStatus, ...dialogProps @@ -403,6 +404,7 @@ const TwoWindingsTransformerCreationDialog = ({ const searchCopy = useFormSearchCopy({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, toFormValues: (data) => data, setFormValues: fromSearchCopyToFormValues, elementType: EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, @@ -673,6 +675,7 @@ const TwoWindingsTransformerCreationDialog = ({ equipmentType={EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER} onSelectionChange={searchCopy.handleSelectionChange} currentNodeUuid={currentNodeUuid} + currentRootNetworkUuid={currentRootNetworkUuid} /> @@ -683,6 +686,8 @@ TwoWindingsTransformerCreationDialog.propTypes = { editData: PropTypes.object, studyUuid: PropTypes.string, currentNode: PropTypes.object, + currentRootNetworkUuid: PropTypes.string, + isUpdate: PropTypes.bool, editDataFetchStatus: PropTypes.string, }; diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx index 50b554707d..335174232a 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx @@ -162,6 +162,7 @@ export const TwoWindingsTransformerModificationDialogTab = { * @param studyUuid the study we are currently working on * @param defaultIdValue the default two windings transformer id * @param currentNode The node we are currently working on + * @param currentRootNetworkUuid The current root network uuid we are currently working on * @param isUpdate check if edition form * @param editData the data to edit * @param editDataFetchStatus indicates the status of fetching EditData @@ -171,6 +172,7 @@ const TwoWindingsTransformerModificationDialog = ({ studyUuid, defaultIdValue, // Used to pre-select an equipmentId when calling this dialog from the SLD currentNode, + currentRootNetworkUuid, isUpdate, editData, // contains data when we try to edit an existing hypothesis from the current node's list editDataFetchStatus, @@ -189,7 +191,7 @@ const TwoWindingsTransformerModificationDialog = ({ resolver: yupResolver(formSchema), }); const { reset, getValues, setValue } = formMethods; - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid, currentRootNetworkUuid); const computeRatioTapChangerRegulationMode = (ratioTapChangerFormValues) => { if (ratioTapChangerFormValues?.[REGULATING]?.value == null) { @@ -581,6 +583,7 @@ const TwoWindingsTransformerModificationDialog = ({ fetchNetworkElementInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, EQUIPMENT_INFOS_TYPES.FORM.type, equipmentId, @@ -633,7 +636,16 @@ const TwoWindingsTransformerModificationDialog = ({ reset(emptyFormData, { keepDefaultValues: true }); } }, - [studyUuid, currentNodeUuid, selectedId, editData, reset, getValues, setConnectivityValue] + [ + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + selectedId, + editData, + reset, + getValues, + setConnectivityValue, + ] ); useEffect(() => { @@ -680,6 +692,7 @@ const TwoWindingsTransformerModificationDialog = ({ data, setFormValues: fromExternalDataToFormValues, elementType: EQUIPMENT_TYPES.VOLTAGE_LEVEL, @@ -261,13 +263,18 @@ const VoltageLevelCreationDialog = ({ isDataFetching={isUpdate && editDataFetchStatus === FetchStatus.RUNNING} {...dialogProps} > - + @@ -278,6 +285,7 @@ VoltageLevelCreationDialog.propTypes = { editData: PropTypes.object, studyUuid: PropTypes.string, currentNode: PropTypes.object, + currentRootNetworkUuid: PropTypes.string, isUpdate: PropTypes.bool, onCreateVoltageLevel: PropTypes.func, editDataFetchStatus: PropTypes.string, diff --git a/src/components/dialogs/network-modifications/voltage-level/creation/voltage-level-creation-form.jsx b/src/components/dialogs/network-modifications/voltage-level/creation/voltage-level-creation-form.jsx index aa221b626c..cc77cf57f3 100644 --- a/src/components/dialogs/network-modifications/voltage-level/creation/voltage-level-creation-form.jsx +++ b/src/components/dialogs/network-modifications/voltage-level/creation/voltage-level-creation-form.jsx @@ -34,7 +34,7 @@ import { useWatch } from 'react-hook-form'; import GridItem from '../../../commons/grid-item'; import GridSection from '../../../commons/grid-section'; -const VoltageLevelCreationForm = ({ currentNode, studyUuid }) => { +const VoltageLevelCreationForm = ({ currentNode, studyUuid, currentRootNetworkUuid }) => { const currentNodeUuid = currentNode?.id; const [substations, setSubstations] = useState([]); @@ -42,12 +42,14 @@ const VoltageLevelCreationForm = ({ currentNode, studyUuid }) => { const watchSectionCount = useWatch({ name: SECTION_COUNT }); useEffect(() => { - if (studyUuid && currentNodeUuid) { - fetchEquipmentsIds(studyUuid, currentNodeUuid, undefined, 'SUBSTATION', true).then((values) => { - setSubstations(values.sort((a, b) => a.localeCompare(b))); - }); + if (studyUuid && currentNodeUuid && currentRootNetworkUuid) { + fetchEquipmentsIds(studyUuid, currentNodeUuid, currentRootNetworkUuid, undefined, 'SUBSTATION', true).then( + (values) => { + setSubstations(values.sort((a, b) => a.localeCompare(b))); + } + ); } - }, [studyUuid, currentNodeUuid]); + }, [studyUuid, currentNodeUuid, currentRootNetworkUuid]); const voltageLevelIdField = ( diff --git a/src/components/dialogs/network-modifications/voltage-level/modification/voltage-level-modification-dialog.jsx b/src/components/dialogs/network-modifications/voltage-level/modification/voltage-level-modification-dialog.jsx index cdbb0ab781..c836995db6 100644 --- a/src/components/dialogs/network-modifications/voltage-level/modification/voltage-level-modification-dialog.jsx +++ b/src/components/dialogs/network-modifications/voltage-level/modification/voltage-level-modification-dialog.jsx @@ -76,6 +76,7 @@ const VoltageLevelModificationDialog = ({ editData, // contains data when we try to edit an existing hypothesis from the current node's list defaultIdValue, // Used to pre-select an equipmentId when calling this dialog from the network map currentNode, + currentRootNetworkUuid, studyUuid, isUpdate, editDataFetchStatus, @@ -119,6 +120,7 @@ const VoltageLevelModificationDialog = ({ fetchNetworkElementInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, EQUIPMENT_TYPES.VOLTAGE_LEVEL, EQUIPMENT_INFOS_TYPES.FORM.type, equipmentId, @@ -156,7 +158,7 @@ const VoltageLevelModificationDialog = ({ reset(emptyFormData, { keepDefaultValues: true }); } }, - [studyUuid, currentNodeUuid, reset, getValues, editData] + [studyUuid, currentNodeUuid, currentRootNetworkUuid, reset, getValues, editData] ); useEffect(() => { @@ -222,6 +224,7 @@ const VoltageLevelModificationDialog = ({ state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetwork = useSelector((state: AppState) => state.currentRootNetwork); const intl = useIntl(); const equipmentsRef = useRef>(null); @@ -87,11 +88,11 @@ const EquipmentFilter = forwardRef { - if (!studyUuid || !currentNode?.id) { + if (!currentRootNetwork || !studyUuid || !currentNode?.id) { return; } // Load voltage level IDs - fetchVoltageLevelsMapInfos(studyUuid, currentNode.id) + fetchVoltageLevelsMapInfos(studyUuid, currentNode.id, currentRootNetwork) .then((voltageLevels: VoltageLevel[]) => { const vlMap = new Map(); const nvSet = new Set(); @@ -111,7 +112,7 @@ const EquipmentFilter = forwardRef setCountries(countryCodes)) .catch((error) => { snackError({ @@ -119,11 +120,11 @@ const EquipmentFilter = forwardRef { - if (!studyUuid || !currentNode?.id) { + if (!studyUuid || !currentRootNetwork || !currentNode?.id) { return; } const expertFilter = buildExpertFilter( @@ -134,10 +135,11 @@ const EquipmentFilter = forwardRef( const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const [allModels, setAllModels] = useState([]); const [allVariables, setAllVariables] = useState< @@ -218,22 +219,24 @@ const ModelFilter = forwardRef( // fetch all associated models and variables for current node and study useEffect(() => { - if (!currentNode?.id) { + if (!currentNode?.id || !currentRootNetworkUuid) { return; } - fetchDynamicSimulationModels(studyUuid, currentNode.id).then((models: DynamicSimulationModelBack[]) => { - setAllModels( - models.map((model) => ({ - name: model.modelName, - equipmentType: model.equipmentType, - })) - ); + fetchDynamicSimulationModels(studyUuid, currentNode.id, currentRootNetworkUuid).then( + (models: DynamicSimulationModelBack[]) => { + setAllModels( + models.map((model) => ({ + name: model.modelName, + equipmentType: model.equipmentType, + })) + ); - // transform models to variables tree representation - const variablesTree = modelsToVariablesTree(models); - setAllVariables(variablesTree); - }); - }, [studyUuid, currentNode?.id]); + // transform models to variables tree representation + const variablesTree = modelsToVariablesTree(models); + setAllVariables(variablesTree); + } + ); + }, [studyUuid, currentNode?.id, currentRootNetworkUuid]); // expose some api for the component by using ref useImperativeHandle( diff --git a/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx b/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx index d49e1009fa..fd16f8dc0d 100644 --- a/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx +++ b/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx @@ -143,6 +143,7 @@ export const SensitivityAnalysisParameters: FunctionComponent state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const [sensitivityAnalysisParams, setSensitivityAnalysisParams] = useState(params); const resetSensitivityAnalysisParameters = useCallback(() => { @@ -224,13 +225,14 @@ export const SensitivityAnalysisParameters: FunctionComponent { // TODO: not easy to fix any here since values[SubTabsValues] have each time different type which causes problems with "filter" // "none of those signatures are compatible with each other - if (!currentNode) { + if (!currentNode || !currentRootNetworkUuid) { return; } setLaunchLoader(true); getSensitivityAnalysisFactorsCount( studyUuid, currentNode.id, + currentRootNetworkUuid, arrayFormName === SENSI_INJECTIONS_SET, formatFilteredParams(row) ) @@ -248,7 +250,7 @@ export const SensitivityAnalysisParameters: FunctionComponent { if (watchVoltageLevelId) { - fetchVoltageLevelEquipments(studyUuid, currentNodeUuid, undefined, watchVoltageLevelId, true).then( - (values) => { - setEquipmentsOptions(values); - } - ); + fetchVoltageLevelEquipments( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + undefined, + watchVoltageLevelId, + true + ).then((values) => { + setEquipmentsOptions(values); + }); } else { setEquipmentsOptions([]); } - }, [watchVoltageLevelId, id, studyUuid, currentNodeUuid]); + }, [watchVoltageLevelId, id, studyUuid, currentNodeUuid, currentRootNetworkUuid]); const resetEquipment = useCallback(() => { setValue(`${id}.${EQUIPMENT}`, null); diff --git a/src/components/dialogs/root-network-creation-dialog.tsx b/src/components/dialogs/root-network-creation-dialog.tsx new file mode 100644 index 0000000000..a1ed735034 --- /dev/null +++ b/src/components/dialogs/root-network-creation-dialog.tsx @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2024, 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 { FormattedMessage, useIntl } from 'react-intl'; +import { + CustomFormProvider, + ElementType, + fetchDirectoryElementPath, + TreeViewFinderNodeProps, + useSnackMessage, +} from '@gridsuite/commons-ui'; +import { UUID } from 'crypto'; +import { useCallback, useEffect, useState } from 'react'; +import { Grid, Button } from '@mui/material'; +import { UniqueNameInput } from './commons/unique-name-input'; +import { CASE_NAME, CASE_ID, NAME } from '../utils/field-constants'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import yup from '../utils/yup-config'; +import { useSelector } from 'react-redux'; +import BasicModificationDialog from './commons/basicModificationDialog'; +import { AppState } from 'redux/reducer'; +import ImportCaseDialog from './import-case-dialog'; + +export interface FormData { + [NAME]: string; + [CASE_NAME]: string; + [CASE_ID]: string; +} + +interface RootNetworkCreationDialogProps { + open: boolean; + onSave: (data: FormData) => void; + onClose: () => void; + type: ElementType; + titleId: string; + dialogProps: any; + prefixIdForGeneratedName?: string; +} + +const formSchema = yup + .object() + .shape({ + [NAME]: yup.string().trim().required(), + [CASE_NAME]: yup.string().required(), + [CASE_ID]: yup.string().required(), + }) + .required(); + +const emptyFormData: FormData = { + [NAME]: '', + [CASE_NAME]: '', + [CASE_ID]: '', +}; + +const RootNetworkCreationDialog: React.FC = ({ + open, + onSave, + onClose, + type, + titleId, + dialogProps = undefined, + prefixIdForGeneratedName, +}) => { + const intl = useIntl(); + const studyUuid = useSelector((state: AppState) => state.studyUuid); + const { snackError } = useSnackMessage(); + + const [destinationFolder, setDestinationFolder] = useState(); + const [selectedCase, setSelectedCase] = useState(null); + const [caseSelectorOpen, setCaseSelectorOpen] = useState(false); + + const formMethods = useForm({ + defaultValues: emptyFormData, + resolver: yupResolver(formSchema), + }); + + const { reset, setValue } = formMethods; + const disableSave = + // Form validation must pass + !selectedCase || // A case must be selected + !formMethods.getValues(NAME); // NAME must not be empty + + // Clear form and reset selected case + const clear = useCallback(() => { + reset(emptyFormData); + setSelectedCase(null); // Reset the selected case on clear + }, [reset]); + + // Fetch default directory based on study UUID + const fetchDefaultDirectoryForStudy = useCallback(() => { + if (!studyUuid) { + return; + } + fetchDirectoryElementPath(studyUuid).then((res) => { + if (!res || res.length < 2) { + snackError({ + messageTxt: 'unknown study directory', + headerId: 'studyDirectoryFetchingError', + }); + return; + } + const parentFolderIndex = res.length - 2; + const { elementUuid, elementName } = res[parentFolderIndex]; + setDestinationFolder({ + id: elementUuid, + name: elementName, + }); + }); + }, [studyUuid, snackError]); + + // Auto-generate a name with prefix and current date + useEffect(() => { + if (prefixIdForGeneratedName) { + const getCurrentDateTime = () => new Date().toISOString(); + const formattedMessage = intl.formatMessage({ + id: prefixIdForGeneratedName, + }); + const dateTime = getCurrentDateTime(); + reset( + { + ...emptyFormData, + [NAME]: `${formattedMessage}-${dateTime}`, + }, + { keepDefaultValues: true } + ); + } + }, [prefixIdForGeneratedName, intl, reset]); + + useEffect(() => { + if (open && studyUuid) { + fetchDefaultDirectoryForStudy(); + } + }, [fetchDefaultDirectoryForStudy, studyUuid, open]); + + // Open case selector + const handleCaseSelection = () => { + setCaseSelectorOpen(true); + }; + + // Set selected case when a case is selected + const onSelectCase = (selectedCase: TreeViewFinderNodeProps) => { + setSelectedCase(selectedCase); + setValue(NAME, selectedCase.name); // Set the name from the selected case + setValue(CASE_NAME, selectedCase.name); + setValue(CASE_ID, selectedCase.id as string); + setCaseSelectorOpen(false); + }; + + const handleSave = useCallback( + (values: FormData) => { + if (selectedCase) { + // Save data, including CASE_NAME and CASE_ID + const creationData1: FormData = { + ...values, + [CASE_NAME]: selectedCase.name, + [CASE_ID]: selectedCase.id as UUID, + }; + onSave(creationData1); + } else { + snackError({ + messageTxt: 'Please select a case before saving.', + headerId: 'caseNotSelectedError', + }); + } + }, + [onSave, selectedCase, snackError] + ); + + // Case selection component + const caseSelection = ( + + + + + + ); + + return ( + + + + + + + {type === ElementType.ROOT_NETWORK && caseSelection} + + + setCaseSelectorOpen(false)} + onSelectCase={onSelectCase} + /> + + + ); +}; + +export default RootNetworkCreationDialog; diff --git a/src/components/dialogs/set-points/voltage-regulation.jsx b/src/components/dialogs/set-points/voltage-regulation.jsx index 6d239eaeae..4c04c58aea 100644 --- a/src/components/dialogs/set-points/voltage-regulation.jsx +++ b/src/components/dialogs/set-points/voltage-regulation.jsx @@ -19,6 +19,7 @@ import GridItem from '../commons/grid-item'; const VoltageRegulation = ({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, voltageLevelOptions, previousValues, isEquipmentModification, @@ -78,6 +79,7 @@ const VoltageRegulation = ({ voltageLevelOptions={voltageLevelOptions} equipmentSectionTypeDefaultValue={''} currentNodeUuid={currentNodeUuid} + currentRootNetworkUuid={currentRootNetworkUuid} studyUuid={studyUuid} previousRegulatingTerminalValue={previousValues?.regulatingTerminalVlId} previousEquipmentSectionTypeValue={ diff --git a/src/components/graph/menus/dynamic-simulation/event-modification-scenario-editor.tsx b/src/components/graph/menus/dynamic-simulation/event-modification-scenario-editor.tsx index 9941fb1ff3..4ebf6fbdde 100644 --- a/src/components/graph/menus/dynamic-simulation/event-modification-scenario-editor.tsx +++ b/src/components/graph/menus/dynamic-simulation/event-modification-scenario-editor.tsx @@ -34,6 +34,7 @@ const EventModificationScenarioEditor = () => { const { snackError } = useSnackMessage(); const [events, setEvents] = useState([]); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const currentNodeIdRef = useRef(); // initial empty to get first update const [pendingState, setPendingState] = useState(false); @@ -92,11 +93,11 @@ const EventModificationScenarioEditor = () => { const doFetchEvents = useCallback(() => { // Do not fetch modifications on the root node - if (currentNode?.type !== 'NETWORK_MODIFICATION' || !studyUuid) { + if (currentNode?.type !== 'NETWORK_MODIFICATION' || !studyUuid || !currentRootNetworkUuid) { return; } setLaunchLoader(true); - fetchDynamicSimulationEvents(studyUuid, currentNode.id) + fetchDynamicSimulationEvents(studyUuid, currentNode.id, currentRootNetworkUuid) .then((res) => { // Check if during asynchronous request currentNode has already changed // otherwise accept fetch results @@ -117,7 +118,15 @@ const EventModificationScenarioEditor = () => { setLaunchLoader(false); dispatch(setModificationsInProgress(false)); }); - }, [currentNode?.type, currentNode?.id, studyUuid, updateSelectedItems, snackError, dispatch]); + }, [ + currentNode?.type, + currentRootNetworkUuid, + currentNode?.id, + studyUuid, + updateSelectedItems, + snackError, + dispatch, + ]); useEffect(() => { // first time with currentNode initialized then fetch events @@ -164,17 +173,19 @@ const EventModificationScenarioEditor = () => { const isAnyNodeBuilding = useIsAnyNodeBuilding(); const doDeleteEvent = useCallback(() => { - if (!studyUuid || !currentNode?.id) { + if (!studyUuid || !currentNode?.id || !currentRootNetworkUuid) { return; } const selectedEvents = [...selectedItems]; - deleteDynamicSimulationEvents(studyUuid, currentNode.id, selectedEvents).catch((errMsg) => { - snackError({ - messageTxt: errMsg, - headerId: 'DynamicSimulationEventDeleteError', - }); - }); - }, [currentNode?.id, selectedItems, snackError, studyUuid]); + deleteDynamicSimulationEvents(studyUuid, currentNode.id, currentRootNetworkUuid, selectedEvents).catch( + (errMsg) => { + snackError({ + messageTxt: errMsg, + headerId: 'DynamicSimulationEventDeleteError', + }); + } + ); + }, [currentNode?.id, selectedItems, snackError, studyUuid, currentRootNetworkUuid]); const doEditEvent = (event: Event) => { setEditDialogOpen({ diff --git a/src/components/graph/menus/editable-title.tsx b/src/components/graph/menus/editable-title.tsx index 3a1a4d44a7..7aa4089c61 100644 --- a/src/components/graph/menus/editable-title.tsx +++ b/src/components/graph/menus/editable-title.tsx @@ -40,19 +40,30 @@ interface EditableTitleProps { name: string; onClose: () => void; onChange?: (value: string) => void; + isCloseIconVisible?: boolean; } -export const EditableTitle: FunctionComponent = ({ name, onClose, onChange }) => { +export const EditableTitle: FunctionComponent = ({ + name, + isCloseIconVisible = true, + onClose, + onChange, +}) => { const [openEditTitle, setOpenEditTitle] = useState(false); const intl = useIntl(); return ( - setOpenEditTitle(true)} disabled={onChange === undefined}> + setOpenEditTitle(true)} + disabled={onChange === undefined} + > - + { const [deleteInProgress, setDeleteInProgress] = useState(false); const [modificationsToRestore, setModificationsToRestore] = useState([]); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const currentNodeIdRef = useRef(); // initial empty to get first update const [pendingState, setPendingState] = useState(false); @@ -263,6 +264,7 @@ const NetworkModificationNodeEditor = () => { onValidated={handleValidatedDialog} currentNode={currentNode} studyUuid={studyUuid} + currentRootNetworkUuid={currentRootNetworkUuid} editData={editData} isUpdate={isUpdate} editDataFetchStatus={editDataFetchStatus} diff --git a/src/components/graph/menus/node-editor.tsx b/src/components/graph/menus/node-editor.tsx index 6aceda8cfa..462e2a2b22 100644 --- a/src/components/graph/menus/node-editor.tsx +++ b/src/components/graph/menus/node-editor.tsx @@ -59,6 +59,7 @@ const NodeEditor = () => { name={currentTreeNode?.data?.label ?? ''} onClose={closeModificationsDrawer} onChange={changeNodeName} + isCloseIconVisible={true} /> diff --git a/src/components/graph/menus/root-network-editor.tsx b/src/components/graph/menus/root-network-editor.tsx new file mode 100644 index 0000000000..e16612c4c2 --- /dev/null +++ b/src/components/graph/menus/root-network-editor.tsx @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2024, 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 PropTypes from 'prop-types'; +import { EditableTitle } from './editable-title'; +import { Box } from '@mui/material'; +import { AppState } from '../../../redux/reducer'; +import RootNetworkNodeEditor from './root-network-node-editor'; +import { useSelector } from 'react-redux'; + +const styles = { + paper: () => ({ + height: '100%', + display: 'flex', + flexDirection: 'column', + elevation: 3, + }), +}; + +const RootNetworkEditor = () => { + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); + + const closeModificationsDrawer = () => {}; + + return ( + + + + + ); +}; + +RootNetworkEditor.propTypes = { + className: PropTypes.string, +}; + +export default RootNetworkEditor; diff --git a/src/components/graph/menus/root-network-node-editor.tsx b/src/components/graph/menus/root-network-node-editor.tsx new file mode 100644 index 0000000000..1d3038191c --- /dev/null +++ b/src/components/graph/menus/root-network-node-editor.tsx @@ -0,0 +1,456 @@ +/** + * Copyright (c) 2024, 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 { CheckBoxList, ElementType, Parameter, useSnackMessage } from '@gridsuite/commons-ui'; + +import FileUpload from '@mui/icons-material/FileUpload'; + +import DeleteIcon from '@mui/icons-material/Delete'; +import { Box, Checkbox, CircularProgress, Theme, Toolbar, Tooltip, Typography, Badge } from '@mui/material'; +import IconButton from '@mui/material/IconButton'; + +import { useCallback, useEffect, useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; + +import { UUID } from 'crypto'; +import { AppState } from 'redux/reducer'; +import { RootNetworkMetadata } from './network-modification-menu.type'; + +import { + CaseImportParameters, + GetCaseImportParametersReturn, + getCaseImportParameters, +} from 'services/network-conversion'; +import { createRootNetwork, deleteRootNetworks, fetchRootNetworks } from 'services/root-network'; +import RemoveRedEyeIcon from '@mui/icons-material/RemoveRedEye'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import { setCurrentRootNetwork } from 'redux/actions'; +import RootNetworkCreationDialog, { FormData } from 'components/dialogs/root-network-creation-dialog'; + +export const styles = { + listContainer: (theme: Theme) => ({ + overflowY: 'auto', + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + paddingBottom: theme.spacing(8), + }), + listItem: { paddingLeft: 0, paddingTop: 0, paddingBottom: 0 }, + checkBoxLabel: { flexGrow: '1' }, + disabledRootNetwork: { opacity: 0.4 }, + checkBoxIcon: { minWidth: 0, padding: 0 }, + checkboxButton: { + padding: 0.5, + margin: 0, + display: 'flex', + alignItems: 'center', + }, + rootNetworksTitle: (theme: Theme) => ({ + display: 'flex', + alignItems: 'center', + margin: theme.spacing(0), + padding: theme.spacing(1), + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + overflow: 'hidden', + }), + toolbar: (theme: Theme) => ({ + '&': { + // Necessary to overrides some @media specific styles that are defined elsewhere + padding: 0, + minHeight: 0, + }, + border: theme.spacing(1), + margin: 0, + flexShrink: 0, + }), + toolbarIcon: (theme: Theme) => ({ + marginRight: theme.spacing(1), + }), + toolbarCheckbox: (theme: Theme) => ({ + marginLeft: theme.spacing(1.5), + }), + filler: { + flexGrow: 1, + }, + circularProgress: (theme: Theme) => ({ + marginRight: theme.spacing(2), + color: theme.palette.primary.contrastText, + }), + toolbarCircularProgress: (theme: Theme) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + marginLeft: theme.spacing(1.25), + marginRight: theme.spacing(2), + color: theme.palette.secondary.main, + }), + notification: (theme: Theme) => ({ + flex: 1, + alignContent: 'center', + justifyContent: 'center', + marginTop: theme.spacing(4), + textAlign: 'center', + color: theme.palette.primary.main, + }), + icon: (theme: Theme) => ({ + width: theme.spacing(3), + }), + iconEdit: (theme: Theme) => ({ + marginRight: theme.spacing(1), + }), +}; + +export function isChecked(s1: number) { + return s1 !== 0; +} + +export function isPartial(s1: number, s2: number) { + if (s1 === 0) { + return false; + } + return s1 !== s2; +} + +const RootNetworkNodeEditor = () => { + const notificationIdList = useSelector((state: AppState) => state.notificationIdList); + const studyUuid = useSelector((state: AppState) => state.studyUuid); + const { snackInfo, snackError, snackWarning } = useSnackMessage(); + const [rootNetworks, setRootNetworks] = useState([]); + const [createInProgress, setCreateInProgress] = useState(false); + const [deleteInProgress, setDeleteInProgress] = useState(false); + const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetwork = useSelector((state: AppState) => state.currentRootNetwork); + + const [pendingState, setPendingState] = useState(false); + + const [selectedItems, setSelectedItems] = useState([]); + + const [rootNetworkCreationDialogOpen, setRootNetworkCreationDialogOpen] = useState(false); + const dispatch = useDispatch(); + const studyUpdatedForce = useSelector((state: AppState) => state.studyUpdated); + const [messageId, setMessageId] = useState(''); + const [launchLoader, setLaunchLoader] = useState(false); + + const updateSelectedItems = useCallback((rootNetworks: RootNetworkMetadata[]) => { + const toKeepIdsSet = new Set(rootNetworks.map((e) => e.rootNetworkUuid)); + setSelectedItems((oldselectedItems) => oldselectedItems.filter((s) => toKeepIdsSet.has(s.rootNetworkUuid))); + }, []); + + const dofetchRootNetworks = useCallback(() => { + setLaunchLoader(true); + if (studyUuid) { + fetchRootNetworks(studyUuid) + .then((res: RootNetworkMetadata[]) => { + updateSelectedItems(res); + setRootNetworks(res); + }) + .catch((error) => { + snackError({ + messageTxt: error.message, + }); + }) + .finally(() => { + setPendingState(false); + setLaunchLoader(false); + }); + } + }, [studyUuid, updateSelectedItems, snackError]); + + useEffect(() => { + if (studyUpdatedForce.eventData.headers) { + if (studyUpdatedForce.eventData.headers['updateType'] === 'rootNetworksUpdated') { + setMessageId('updateRootNetworksList'); + dofetchRootNetworks(); + } + } + }, [studyUpdatedForce, dofetchRootNetworks]); + + useEffect(() => { + if (rootNetworks.length === 0) { + dofetchRootNetworks(); + } + }, [dofetchRootNetworks, rootNetworks]); + + const openRootNetworkCreationDialog = useCallback(() => { + setRootNetworkCreationDialogOpen(true); + }, []); + + const doDeleteRootNetwork = useCallback(() => { + const selectedRootNetworksUuid = selectedItems.map((item) => item.rootNetworkUuid); + const itemsToDelete = selectedRootNetworksUuid.filter((uuid) => uuid !== currentRootNetwork); + + // If there are no items to delete, show warning + if (itemsToDelete.length === 0) { + snackWarning({ headerId: 'warnDeleteCurrentRootNetwork' }); + return; + } + + if (studyUuid && currentRootNetwork) { + deleteRootNetworks(studyUuid, itemsToDelete) + .then(() => { + setDeleteInProgress(true); + }) + .catch((errmsg) => { + snackError({ + messageTxt: errmsg, + headerId: 'errDeleteRootNetworkMsg', + }); + }) + .finally(() => { + setDeleteInProgress(false); + if (selectedRootNetworksUuid.includes(currentRootNetwork)) { + snackWarning({ + headerId: 'warnDeleteCurrentRootNetwork', + }); + } + }); + } + }, [selectedItems, snackError, snackWarning, studyUuid, currentRootNetwork]); + + const toggleSelectAllRootNetworks = useCallback(() => { + setSelectedItems((oldVal) => (oldVal.length === 0 ? rootNetworks : [])); + }, [rootNetworks]); + + const isLoading = useCallback(() => { + return notificationIdList.filter((notification) => notification === currentNode?.id).length > 0; + }, [notificationIdList, currentNode?.id]); + + const intl = useIntl(); + const getRootNetworkLabel = (rootNetwork: RootNetworkMetadata): string => { + if (!rootNetwork) { + return ''; + } + return intl.formatMessage({ id: 'RootNetwork' }) + ': ' + rootNetwork.rootNetworkUuid; + }; + + const handleSecondaryAction = useCallback( + (rootNetwork: RootNetworkMetadata) => { + const isCurrentRootNetwork = rootNetwork.rootNetworkUuid === currentRootNetwork; + + return ( + + { + if (rootNetwork.rootNetworkUuid !== currentRootNetwork) { + dispatch(setCurrentRootNetwork(rootNetwork.rootNetworkUuid)); + } + }} + > + {isCurrentRootNetwork ? ( + + + + ) : ( + + )} + + + ); + }, + [currentRootNetwork, dispatch] + ); + + const renderRootNetworksList = () => { + return ( + ({ + label: { + ...(rootNetwork.isCreating && { ...styles.disabledRootNetwork }), + ...styles.checkBoxLabel, + }, + checkBoxIcon: styles.checkBoxIcon, + checkboxButton: styles.checkboxButton, + }), + }} + onItemClick={(rootNetwork) => { + console.log(rootNetwork.rootNetworkUuid, 'on click'); + }} + selectedItems={selectedItems} + onSelectionChange={setSelectedItems} + items={rootNetworks} + getItemId={(val) => val.rootNetworkUuid} + getItemLabel={getRootNetworkLabel} + divider + secondaryAction={handleSecondaryAction} + /> + ); + }; + + const renderRootNetworksListTitleLoading = () => { + return ( + + + + + + + + + ); + }; + + const renderRootNetworksListTitleUpdating = () => { + return ( + + + + + + + + + ); + }; + + const renderRootNetworksListTitle = () => { + return ( + + + {pendingState && } + + + + + + ); + }; + + const renderRootNetworkCreationDialog = () => { + return ( + setRootNetworkCreationDialogOpen(false)} + onSave={doCreateRootNetwork} + type={ElementType.ROOT_NETWORK} + titleId={'CreateRootNetwork'} + dialogProps={undefined} + /> + ); + }; + + function formatCaseImportParameters(params: CaseImportParameters[]): CaseImportParameters[] { + // sort possible values alphabetically to display select options sorted + return params?.map((parameter) => ({ + ...parameter, + possibleValues: parameter.possibleValues?.sort((a: any, b: any) => a.localeCompare(b)), + })); + } + + function customizeCurrentParameters(params: Parameter[]): Record { + return params.reduce((obj, parameter) => { + // we check if the parameter is for extensions. If so, we select all possible values by default. + // the only way for the moment to check if the parameter is for extension, is by checking his name. + // TODO: implement a cleaner way to determine the extensions field + if (parameter.type === 'STRING_LIST' && parameter.name?.endsWith('extensions')) { + return { ...obj, [parameter.name]: parameter.possibleValues.toString() }; + } + return obj; + }, {} as Record); + } + + const doCreateRootNetwork = ({ name, caseName, caseId }: FormData) => { + setCreateInProgress(true); + + getCaseImportParameters(caseId as UUID) + .then((params: GetCaseImportParametersReturn) => { + // Format the parameters + const formattedParams = formatCaseImportParameters(params.parameters); + const customizedCurrentParameters = customizeCurrentParameters(formattedParams as Parameter[]); + // Call createRootNetwork with formatted parameters + return createRootNetwork(caseId as UUID, params.formatName, studyUuid, customizedCurrentParameters); + }) + .then(() => { + snackInfo({ + headerId: 'rootNetworkCreated', + headerValues: { + rootNetworkName: name, + }, + }); + }) + .catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'errCreateRootNetworksMsg', + }); + }) + .finally(() => { + setCreateInProgress(false); + }); + }; + const renderPaneSubtitle = () => { + if (isLoading() && messageId) { + return renderRootNetworksListTitleLoading(); + } + if (launchLoader) { + return renderRootNetworksListTitleUpdating(); + } + return renderRootNetworksListTitle(); + }; + + return ( + <> + + + + + }> + + + + + + + + + + + {deleteInProgress ?? ( + }> + + + + + )} + + {rootNetworkCreationDialogOpen && renderRootNetworkCreationDialog()} + {renderPaneSubtitle()} + + {renderRootNetworksList()} + + ); +}; + +export default RootNetworkNodeEditor; diff --git a/src/components/map-viewer.jsx b/src/components/map-viewer.jsx index 4844d5fd1c..84c9ceef39 100644 --- a/src/components/map-viewer.jsx +++ b/src/components/map-viewer.jsx @@ -130,6 +130,7 @@ export const Content = () => ( const MapViewer = ({ studyUuid, currentNode, + currentRootNetworkUuid, view, openDiagramView, tableEquipment, @@ -258,7 +259,11 @@ const MapViewer = ({ }} > - + {/* Map */} @@ -305,6 +310,7 @@ const MapViewer = ({ lineFlowAlertThreshold={networkVisuParams.mapParameters.lineFlowAlertThreshold} openVoltageLevel={openVoltageLevel} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} onChangeTab={onChangeTab} showInSpreadsheet={showInSpreadsheet} setErrorMessage={setErrorMessage} @@ -332,6 +338,7 @@ const MapViewer = ({ studyUuid={studyUuid} showInSpreadsheet={showInSpreadsheet} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} visible={ !isInDrawingMode && view === StudyView.MAP && diff --git a/src/components/menus/bus-menu.tsx b/src/components/menus/bus-menu.tsx index d5cfdb9f46..05ecd19d4e 100644 --- a/src/components/menus/bus-menu.tsx +++ b/src/components/menus/bus-menu.tsx @@ -75,6 +75,7 @@ export const BusMenu: FunctionComponent = ({ // to check is node editable const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const studyUuid = useSelector((state: AppState) => state.studyUuid); const isAnyNodeBuilding = useIsAnyNodeBuilding(); const isNodeEditable = useMemo( @@ -86,6 +87,7 @@ export const BusMenu: FunctionComponent = ({ fetchNetworkElementInfos( studyUuid, currentNode?.id, + currentRootNetworkUuid, EQUIPMENT_TYPES.BUSBAR_SECTION, EQUIPMENT_INFOS_TYPES.OPERATING_STATUS.type, busId, @@ -95,7 +97,7 @@ export const BusMenu: FunctionComponent = ({ setEquipmentInfos(value); } }); - }, [studyUuid, currentNode?.id, busId]); + }, [studyUuid, currentRootNetworkUuid, currentNode?.id, busId]); const computationStarting = useSelector((state: AppState) => state.computationStarting); diff --git a/src/components/menus/operating-status-menu.tsx b/src/components/menus/operating-status-menu.tsx index d05f94001e..a164a96d58 100644 --- a/src/components/menus/operating-status-menu.tsx +++ b/src/components/menus/operating-status-menu.tsx @@ -63,6 +63,7 @@ export type MenuBranchProps = { onOpenDynamicSimulationEventDialog?: (id: string, type: EquipmentType | null, dialogTitle: string) => void; currentNode?: CurrentTreeNode; studyUuid?: UUID; + currentRootNetworkUuid?: UUID; modificationInProgress?: boolean; setModificationInProgress?: (progress: boolean) => void; }; @@ -81,6 +82,7 @@ const withOperatingStatusMenu = onOpenDynamicSimulationEventDialog, currentNode, studyUuid, + currentRootNetworkUuid, modificationInProgress, setModificationInProgress, }: MenuBranchProps) => { @@ -103,6 +105,7 @@ const withOperatingStatusMenu = fetchNetworkElementInfos( studyUuid, currentNode?.id, + currentRootNetworkUuid, equipmentType, EQUIPMENT_INFOS_TYPES.OPERATING_STATUS.type, equipment.id, @@ -113,7 +116,7 @@ const withOperatingStatusMenu = } }); } - }, [studyUuid, currentNode?.id, equipmentType, equipment?.id]); + }, [studyUuid, currentNode?.id, currentRootNetworkUuid, equipmentType, equipment?.id]); const isNodeEditable = useMemo( function () { @@ -403,6 +406,7 @@ withOperatingStatusMenu.propTypes = { onOpenDynamicSimulationEventDialog: PropTypes.func.isRequired, currentNode: PropTypes.object, studyUuid: PropTypes.string.isRequired, + currentRootNetworkUuid: PropTypes.string.isRequired, modificationInProgress: PropTypes.func, setModificationInProgress: PropTypes.func, }; diff --git a/src/components/network-modification-tree-pane.jsx b/src/components/network-modification-tree-pane.jsx index 9536aeb095..8edf91e237 100644 --- a/src/components/network-modification-tree-pane.jsx +++ b/src/components/network-modification-tree-pane.jsx @@ -51,6 +51,7 @@ const styles = { height: '100%', display: 'flex', flexDirection: 'row', + // backgroundColor: 'yellow', }, }; @@ -75,7 +76,7 @@ const noNodeSelectionForCopy = { const HTTP_MAX_NODE_BUILDS_EXCEEDED_MESSAGE = 'MAX_NODE_BUILDS_EXCEEDED'; -export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) => { +export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay, currentRootNetworkUuid }) => { const dispatch = useDispatch(); const intlRef = useIntlRef(); const { snackError, snackWarning, snackInfo } = useSnackMessage(); @@ -137,7 +138,8 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) const currentNode = useSelector((state) => state.currentTreeNode); const currentNodeRef = useRef(); currentNodeRef.current = currentNode; - + const currentRootNetworkRef = useRef(); + currentRootNetworkRef.current = currentRootNetworkUuid; const selectionForCopy = useSelector((state) => state.nodeSelectionForCopy); const nodeSelectionForCopyRef = useRef(); nodeSelectionForCopyRef.current = selectionForCopy; @@ -152,13 +154,15 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) const updateNodes = useCallback( (updatedNodesIds) => { - Promise.all(updatedNodesIds.map((nodeId) => fetchNetworkModificationTreeNode(studyUuid, nodeId))).then( - (values) => { - dispatch(networkModificationTreeNodesUpdated(values)); - } - ); + Promise.all( + updatedNodesIds.map((nodeId) => + fetchNetworkModificationTreeNode(studyUuid, nodeId, currentRootNetworkUuid) + ) + ).then((values) => { + dispatch(networkModificationTreeNodesUpdated(values)); + }); }, - [studyUuid, dispatch] + [studyUuid, currentRootNetworkUuid, dispatch] ); const isSubtreeImpacted = useCallback( @@ -213,18 +217,20 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) useEffect(() => { if (studyUpdatedForce.eventData.headers) { if (studyUpdatedForce.eventData.headers['updateType'] === UpdateType.NODE_CREATED) { - fetchNetworkModificationTreeNode(studyUuid, studyUpdatedForce.eventData.headers['newNode']).then( - (node) => { - dispatch( - networkModificationTreeNodeAdded( - node, - studyUpdatedForce.eventData.headers['parentNode'], - studyUpdatedForce.eventData.headers['insertMode'], - studyUpdatedForce.eventData.headers['referenceNodeUuid'] - ) - ); - } - ); + fetchNetworkModificationTreeNode( + studyUuid, + studyUpdatedForce.eventData.headers['newNode'], + currentRootNetworkUuid + ).then((node) => { + dispatch( + networkModificationTreeNodeAdded( + node, + studyUpdatedForce.eventData.headers['parentNode'], + studyUpdatedForce.eventData.headers['insertMode'], + studyUpdatedForce.eventData.headers['referenceNodeUuid'] + ) + ); + }); if (isSubtreeImpacted([studyUpdatedForce.eventData.headers['parentNode']])) { resetNodeClipboard(); @@ -246,18 +252,20 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) JSON.parse(studyUpdatedForce.eventData.payload) ); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodeMoved') { - fetchNetworkModificationTreeNode(studyUuid, studyUpdatedForce.eventData.headers['movedNode']).then( - (node) => { - dispatch( - networkModificationTreeNodeMoved( - node, - studyUpdatedForce.eventData.headers['parentNode'], - studyUpdatedForce.eventData.headers['insertMode'], - studyUpdatedForce.eventData.headers['referenceNodeUuid'] - ) - ); - } - ); + fetchNetworkModificationTreeNode( + studyUuid, + studyUpdatedForce.eventData.headers['movedNode'], + currentRootNetworkUuid + ).then((node) => { + dispatch( + networkModificationTreeNodeMoved( + node, + studyUpdatedForce.eventData.headers['parentNode'], + studyUpdatedForce.eventData.headers['insertMode'], + studyUpdatedForce.eventData.headers['referenceNodeUuid'] + ) + ); + }); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'subtreeMoved') { fetchNetworkModificationSubtree(studyUuid, studyUpdatedForce.eventData.headers['movedNode']).then( (nodes) => { @@ -296,7 +304,10 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) } } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodeRenamed') { updateNodes([studyUpdatedForce.eventData.headers['node']]); - } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodeBuildStatusUpdated') { + } else if ( + studyUpdatedForce.eventData.headers['updateType'] === 'nodeBuildStatusUpdated' && + studyUpdatedForce.eventData.headers['rootNetwork'] === currentRootNetworkRef.current + ) { updateNodes(studyUpdatedForce.eventData.headers['nodes']); if ( studyUpdatedForce.eventData.headers['nodes'].some((nodeId) => nodeId === currentNodeRef.current?.id) @@ -323,6 +334,7 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) snackInfo, dispatch, broadcastChannel, + currentRootNetworkUuid, isSubtreeImpacted, resetNodeClipboard, ]); @@ -416,19 +428,19 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) const handleUnbuildNode = useCallback( (element) => { - unbuildNode(studyUuid, element.id).catch((error) => { + unbuildNode(studyUuid, element.id, currentRootNetworkUuid).catch((error) => { snackError({ messageTxt: error.message, headerId: 'NodeUnbuildingError', }); }); }, - [studyUuid, snackError] + [studyUuid, currentRootNetworkUuid, snackError] ); const handleBuildNode = useCallback( (element) => { - buildNode(studyUuid, element.id).catch((error) => { + buildNode(studyUuid, element.id, currentRootNetworkUuid).catch((error) => { if (error.status === 403 && error.message.includes(HTTP_MAX_NODE_BUILDS_EXCEEDED_MESSAGE)) { // retrieve last word of the message (ex: "MAX_NODE_BUILDS_EXCEEDED max allowed built nodes : 2" -> 2) let limit = error.message.split(/[: ]+/).pop(); @@ -444,7 +456,7 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) } }); }, - [studyUuid, snackError] + [studyUuid, currentRootNetworkUuid, snackError] ); const [openExportDialog, setOpenExportDialog] = useState(false); @@ -587,6 +599,7 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) onClose={() => setOpenExportDialog(false)} onClick={handleClickExportStudy} studyUuid={studyUuid} + rootNetworkUuid={currentRootNetworkUuid} nodeUuid={activeNode?.id} title={intlRef.current.formatMessage({ id: 'exportNetwork', diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index 0f480ffb9f..497d5d7988 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -28,6 +28,7 @@ import { snapGrid, } from './graph/layout'; import TreeControlButton from './graph/util/tree-control-button'; +import RootNetworkPanel from './root-network-panel'; import { updateNodesColumnPositions } from '../services/study/tree-subtree.ts'; import { useSnackMessage } from '@gridsuite/commons-ui'; @@ -355,6 +356,9 @@ const NetworkModificationTree = ({ {isMinimapOpen && } + + {/* root Network Panel */} + ); diff --git a/src/components/network/gs-map-equipments.ts b/src/components/network/gs-map-equipments.ts index 942436232f..b1fd512072 100644 --- a/src/components/network/gs-map-equipments.ts +++ b/src/components/network/gs-map-equipments.ts @@ -24,11 +24,35 @@ export default class GSMapEquipments extends MapEquipments { errHandler?: UseSnackMessageReturn['snackError']; intlRef: RefObject; - initEquipments(studyUuid: UUID, currentNodeUuid: UUID) { - const fetchSubstationsMapInfosPromise = fetchSubstationsMapInfos(studyUuid, currentNodeUuid, undefined, false); - const fetchLinesMapInfosPromise = fetchLinesMapInfos(studyUuid, currentNodeUuid, undefined, false); - const fetchTieLinesMapInfosPromise = fetchTieLinesMapInfos(studyUuid, currentNodeUuid, undefined, false); - const fetchHvdcLinesMapInfosPromise = fetchHvdcLinesMapInfos(studyUuid, currentNodeUuid, undefined, false); + initEquipments(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + const fetchSubstationsMapInfosPromise = fetchSubstationsMapInfos( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + undefined, + false + ); + const fetchLinesMapInfosPromise = fetchLinesMapInfos( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + undefined, + false + ); + const fetchTieLinesMapInfosPromise = fetchTieLinesMapInfos( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + undefined, + false + ); + const fetchHvdcLinesMapInfosPromise = fetchHvdcLinesMapInfos( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + undefined, + false + ); this.dispatch(setMapEquipementsInitialized(false)); @@ -101,6 +125,7 @@ export default class GSMapEquipments extends MapEquipments { constructor( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, errHandler: UseSnackMessageReturn['snackError'], dispatch: Dispatch, intlRef: RefObject @@ -109,14 +134,43 @@ export default class GSMapEquipments extends MapEquipments { this.dispatch = dispatch; this.errHandler = errHandler; this.intlRef = intlRef; - this.initEquipments(studyUuid, currentNodeUuid); + this.initEquipments(studyUuid, currentNodeUuid, currentRootNetworkUuid); } - reloadImpactedSubstationsEquipments(studyUuid: UUID, currentNode: any, substationsIds: string[] | null) { - const updatedSubstations = fetchSubstationsMapInfos(studyUuid, currentNode?.id, substationsIds, true); - const updatedLines = fetchLinesMapInfos(studyUuid, currentNode?.id, substationsIds, true); - const updatedTieLines = fetchTieLinesMapInfos(studyUuid, currentNode?.id, substationsIds, true); - const updatedHvdcLines = fetchHvdcLinesMapInfos(studyUuid, currentNode?.id, substationsIds, true); + reloadImpactedSubstationsEquipments( + studyUuid: UUID, + currentNode: any, + currentRootNetworkUuid: UUID, + substationsIds: string[] | null + ) { + const updatedSubstations = fetchSubstationsMapInfos( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + substationsIds, + true + ); + const updatedLines = fetchLinesMapInfos( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + substationsIds, + true + ); + const updatedTieLines = fetchTieLinesMapInfos( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + substationsIds, + true + ); + const updatedHvdcLines = fetchHvdcLinesMapInfos( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + substationsIds, + true + ); updatedSubstations.catch((error) => { console.error(error.message); if (this.errHandler) { diff --git a/src/components/network/network-map-tab.tsx b/src/components/network/network-map-tab.tsx index 769a6ffbbd..128f50f019 100644 --- a/src/components/network/network-map-tab.tsx +++ b/src/components/network/network-map-tab.tsx @@ -84,6 +84,7 @@ type NetworkMapTabProps = { networkMapRef: React.RefObject; studyUuid: UUID; currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; visible: boolean; lineFullPath: boolean; lineParallelPath: boolean; @@ -105,6 +106,7 @@ export const NetworkMapTab = ({ /* redux can be use as redux*/ studyUuid, currentNode, + currentRootNetworkUuid, /* visual*/ visible, lineFullPath, @@ -155,6 +157,7 @@ export const NetworkMapTab = ({ const basicDataReady = mapEquipments && geoData; const lineFullPathRef = useRef(); + const rootNetworkRef = useRef(); /* This Set stores the geo data that are collected from the server AFTER the initialization. @@ -209,6 +212,7 @@ export const NetworkMapTab = ({ open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} isUpdate={true} defaultIdValue={equipmentToModify?.id} onClose={() => closeModificationDialog()} @@ -222,6 +226,7 @@ export const NetworkMapTab = ({ open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} isUpdate={true} defaultIdValue={equipmentToModify?.id} onClose={() => closeModificationDialog()} @@ -235,6 +240,7 @@ export const NetworkMapTab = ({ open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} defaultIdValue={equipmentToModify?.id} isUpdate={true} onClose={() => closeModificationDialog()} @@ -255,6 +261,7 @@ export const NetworkMapTab = ({ open={true} studyUuid={studyUuid} currentNode={currentNode} + currentRootNetworkUuid={currentRootNetworkUuid} defaultIdValue={equipmentToModify?.id} isUpdate={true} onClose={() => closeModificationDialog()} @@ -418,15 +425,25 @@ export const NetworkMapTab = ({ const getMissingEquipmentsPositions = useCallback( ( notFoundEquipmentsIds: string[], - fetchEquipmentCB: (studyUuid: UUID, nodeId: UUID, equipmentIds: string[]) => Promise + fetchEquipmentCB: ( + studyUuid: UUID, + nodeId: UUID, + currentRootNetworkUuid: UUID, + equipmentIds: string[] + ) => Promise ) => { if (notFoundEquipmentsIds.length === 0 || !currentNodeRef.current) { return Promise.resolve([]); } - return fetchEquipmentCB(studyUuid, currentNodeRef.current!.id, notFoundEquipmentsIds); + return fetchEquipmentCB( + studyUuid, + currentNodeRef.current!.id, + currentRootNetworkUuid, + notFoundEquipmentsIds + ); }, - [studyUuid] + [studyUuid, currentRootNetworkUuid] ); const updateSubstationsTemporaryGeoData = useCallback( @@ -572,23 +589,33 @@ export const NetworkMapTab = ({ updateLinesTemporaryGeoData, ]); + const handleChange = useCallback( + (newValues: unknown[]) => { + setFilteredNominalVoltages(newValues); + onNominalVoltagesChange(newValues); + }, + [setFilteredNominalVoltages, onNominalVoltagesChange] + ); + // loads all root node geo-data then saves them in redux // it will be considered as the source of truth to check whether we need to fetch geo-data for a specific equipment or not const loadRootNodeGeoData = useCallback(() => { console.info(`Loading geo data of study '${studyUuid}'...`); dispatch(setMapDataLoading(true)); - - const substationPositionsDone = fetchSubstationPositions(studyUuid, rootNodeId).then((data) => { - console.info(`Received substations of study '${studyUuid}'...`); - const newGeoData = new GeoData(new Map(), geoDataRef.current?.linePositionsById || new Map()); - newGeoData.setSubstationPositions(data); - setGeoData(newGeoData); - geoDataRef.current = newGeoData; - }); + geoDataRef.current = null; + const substationPositionsDone = fetchSubstationPositions(studyUuid, rootNodeId, currentRootNetworkUuid).then( + (data) => { + console.info(`Received substations of study '${studyUuid}'...`); + const newGeoData = new GeoData(new Map(), geoDataRef.current?.linePositionsById || new Map()); + newGeoData.setSubstationPositions(data); + setGeoData(newGeoData); + geoDataRef.current = newGeoData; + } + ); const linePositionsDone = !lineFullPath ? Promise.resolve() - : fetchLinePositions(studyUuid, rootNodeId).then((data) => { + : fetchLinePositions(studyUuid, rootNodeId, currentRootNetworkUuid).then((data) => { console.info(`Received lines of study '${studyUuid}'...`); const newGeoData = new GeoData(geoDataRef.current?.substationPositionsById || new Map(), new Map()); newGeoData.setLinePositions(data); @@ -599,6 +626,12 @@ export const NetworkMapTab = ({ Promise.all([substationPositionsDone, linePositionsDone]) .then(() => { temporaryGeoDataIdsRef.current = new Set(); + networkMapRef.current?.centerMapNetwork(); + // when reloading root node map equipments (when switching of root network), nominal voltages are reloaded + // we check them all in NominalVoltageFilter by default + if (mapEquipments) { + handleChange(mapEquipments.getNominalVoltages()); + } setIsRootNodeGeoDataLoaded(true); }) .catch(function (error) { @@ -611,7 +644,17 @@ export const NetworkMapTab = ({ .finally(() => { dispatch(setMapDataLoading(false)); }); - }, [rootNodeId, lineFullPath, studyUuid, dispatch, snackError]); + }, [ + mapEquipments, + rootNodeId, + currentRootNetworkUuid, + lineFullPath, + studyUuid, + dispatch, + snackError, + networkMapRef, + handleChange, + ]); const loadGeoData = useCallback(() => { if (studyUuid && currentNodeRef.current) { @@ -646,11 +689,18 @@ export const NetworkMapTab = ({ if (!isNodeBuilt(currentNode) || !studyUuid) { return; } - const gSMapEquipments = new GSMapEquipments(studyUuid, currentNode?.id, snackError, dispatch, intlRef); + const gSMapEquipments = new GSMapEquipments( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + snackError, + dispatch, + intlRef + ); if (gSMapEquipments) { dispatch(resetMapReloaded()); } - }, [currentNode, dispatch, intlRef, snackError, studyUuid]); + }, [currentNode, currentRootNetworkUuid, dispatch, intlRef, snackError, studyUuid]); const reloadMapEquipments = useCallback( (currentNodeAtReloadCalling: CurrentTreeNode | null, substationsIds: UUID[] | undefined) => { @@ -663,7 +713,12 @@ export const NetworkMapTab = ({ } const { updatedSubstations, updatedLines, updatedTieLines, updatedHvdcLines } = mapEquipments - ? mapEquipments.reloadImpactedSubstationsEquipments(studyUuid, currentNode, substationsIds ?? null) + ? mapEquipments.reloadImpactedSubstationsEquipments( + studyUuid, + currentNode, + currentRootNetworkUuid, + substationsIds ?? null + ) : { updatedSubstations: Promise.resolve([]), updatedLines: Promise.resolve([]), @@ -700,7 +755,7 @@ export const NetworkMapTab = ({ dispatch(setMapDataLoading(false)); }); }, - [currentNode, dispatch, mapEquipments, studyUuid] + [currentNode, currentRootNetworkUuid, dispatch, mapEquipments, studyUuid] ); const updateMapEquipments = useCallback( @@ -766,7 +821,11 @@ export const NetworkMapTab = ({ useEffect(() => { if (isInitialized && studyUpdatedForce.eventData.headers) { - if (studyUpdatedForce.eventData.headers[UPDATE_TYPE_HEADER] === 'loadflowResult') { + const currentRootNetwork = studyUpdatedForce.eventData.headers['rootNetwork']; + if ( + studyUpdatedForce.eventData.headers[UPDATE_TYPE_HEADER] === 'loadflowResult' && + currentRootNetwork === currentRootNetworkUuid + ) { reloadMapEquipments(currentNodeRef.current, undefined).catch((e) => snackError({ messageTxt: e.message, @@ -774,7 +833,7 @@ export const NetworkMapTab = ({ ); } } - }, [isInitialized, studyUpdatedForce, reloadMapEquipments, snackError]); + }, [isInitialized, studyUpdatedForce, currentRootNetworkUuid, reloadMapEquipments, snackError]); useEffect(() => { if (!mapEquipments || refIsMapManualRefreshEnabled.current) { @@ -863,7 +922,21 @@ export const NetworkMapTab = ({ if (isInitialized && lineFullPath && !prevLineFullPath) { loadGeoData(); } - }, [isInitialized, lineFullPath, loadGeoData]); + }, [isInitialized, lineFullPath, loadGeoData, currentRootNetworkUuid]); + + // Effect to handle changes in currentRootNetworkUuid + useEffect(() => { + const prevRootNetworkPath = rootNetworkRef.current; + rootNetworkRef.current = currentRootNetworkUuid; + + if (currentRootNetworkUuid && currentRootNetworkUuid !== prevRootNetworkPath && rootNodeId) { + loadRootNodeGeoData(); + // set initialized to false to trigger "missing geo-data fetching" + setInitialized(false); + // set isRootNodeGeoDataLoaded to false so "missing geo-data fetching" waits for root node geo-data to be fully fetched before triggering + setIsRootNodeGeoDataLoaded(false); + } + }, [currentRootNetworkUuid, loadRootNodeGeoData, rootNodeId]); let choiceVoltageLevelsSubstation: EquipmentMap | null = null; if (choiceVoltageLevelsSubstationId) { @@ -983,11 +1056,6 @@ export const NetworkMapTab = ({ /> ); - function handleChange(newValues: unknown[]) { - setFilteredNominalVoltages(newValues); - onNominalVoltagesChange(newValues); - } - function renderNominalVoltageFilter() { return ( diff --git a/src/components/network/selection-creation-panel/use-save-map.ts b/src/components/network/selection-creation-panel/use-save-map.ts index b87547dc28..021db245d2 100644 --- a/src/components/network/selection-creation-panel/use-save-map.ts +++ b/src/components/network/selection-creation-panel/use-save-map.ts @@ -27,6 +27,7 @@ export const useSaveMap = (): UseSaveMapOutput => { const intl = useIntl(); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNodeUuid = useSelector((state: AppState) => state.currentTreeNode?.id); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const { snackInfo, snackError, snackWarning } = useSnackMessage(); const [pendingState, setPendingState] = useState(false); @@ -63,6 +64,7 @@ export const useSaveMap = (): UseSaveMapOutput => { // @ts-expect-error TODO: manage null case studyUuid, currentNodeUuid, + currentRootNetworkUuid, selectedEquipmentsIds, nominalVoltages ); @@ -79,6 +81,7 @@ export const useSaveMap = (): UseSaveMapOutput => { // @ts-expect-error TODO: manage null case studyUuid, currentNodeUuid, + currentRootNetworkUuid, equipments, nominalVoltages ); @@ -110,7 +113,7 @@ export const useSaveMap = (): UseSaveMapOutput => { } return true; // success }, - [currentNodeUuid, intl, snackError, snackInfo, snackWarning, studyUuid] + [currentNodeUuid, currentRootNetworkUuid, intl, snackError, snackInfo, snackWarning, studyUuid] ); return { pendingState, onSaveSelection }; diff --git a/src/components/report-viewer-tab.jsx b/src/components/report-viewer-tab.jsx index 2cbdda0805..0bd1c41faa 100644 --- a/src/components/report-viewer-tab.jsx +++ b/src/components/report-viewer-tab.jsx @@ -36,6 +36,7 @@ const styles = { * @param studyId : string study id * @param visible : boolean window visible * @param currentNode : object visualized node + * @param currentRootNetwork : current rootnetwork uuid * @param disabled : boolean disabled * @returns {*} node * @constructor diff --git a/src/components/result-view-tab.tsx b/src/components/result-view-tab.tsx index 5a6ba447d5..6061a10e39 100644 --- a/src/components/result-view-tab.tsx +++ b/src/components/result-view-tab.tsx @@ -46,6 +46,7 @@ const styles = { interface IResultViewTabProps { studyUuid: UUID; currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; openVoltageLevelDiagram: (voltageLevelId: string) => void; disabled: boolean; view: string; @@ -62,6 +63,7 @@ export interface IService { * control results views * @param studyUuid : string uuid of study * @param currentNode : object current node + * @param currentRootNetworkUuid : uuid of current root network * @param openVoltageLevelDiagram : function * @param resultTabIndexRedirection : ResultTabIndexRedirection to specific tab [RootTab, LevelOneTab, ...] * @param disabled @@ -71,6 +73,7 @@ export interface IService { export const ResultViewTab: FunctionComponent = ({ studyUuid, currentNode, + currentRootNetworkUuid, openVoltageLevelDiagram, disabled, view, @@ -92,10 +95,14 @@ export const ResultViewTab: FunctionComponent = ({ const renderLoadFlowResult = useMemo(() => { return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const renderSecurityAnalysisResult = useMemo(() => { return ( @@ -103,59 +110,86 @@ export const ResultViewTab: FunctionComponent = ({ ); - }, [studyUuid, currentNode, openVoltageLevelDiagram]); + }, [studyUuid, currentNode, currentRootNetworkUuid, openVoltageLevelDiagram]); const renderVoltageInitResult = useMemo(() => { return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const renderSensitivityAnalysisResult = useMemo(() => { return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const renderNonEvacuatedEnergyResult = useMemo(() => { + console.log('ùùùùùù renderNonEvacuatedEnergyResult ', currentRootNetworkUuid); return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const renderShortCircuitAnalysisResult = useMemo(() => { return ( - + ); - }, [view, currentNode?.id, studyUuid]); + }, [view, currentNode?.id, studyUuid, currentRootNetworkUuid]); const renderDynamicSimulationResult = useMemo(() => { return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const renderStateEstimationResult = useMemo(() => { return ( - + ); - }, [studyUuid, currentNode]); + }, [studyUuid, currentNode, currentRootNetworkUuid]); const services: IService[] = useMemo(() => { return [ diff --git a/src/components/results/dynamicsimulation/dynamic-simulation-result-synthesis.tsx b/src/components/results/dynamicsimulation/dynamic-simulation-result-synthesis.tsx index e50218199d..cdb3d34192 100644 --- a/src/components/results/dynamicsimulation/dynamic-simulation-result-synthesis.tsx +++ b/src/components/results/dynamicsimulation/dynamic-simulation-result-synthesis.tsx @@ -42,68 +42,72 @@ const defaultColDef = { type DynamicSimulationResultSynthesisProps = { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; }; -const DynamicSimulationResultSynthesis = memo(({ nodeUuid, studyUuid }: DynamicSimulationResultSynthesisProps) => { - const intl = useIntl(); +const DynamicSimulationResultSynthesis = memo( + ({ nodeUuid, studyUuid, currentRootNetworkUuid }: DynamicSimulationResultSynthesisProps) => { + const intl = useIntl(); - const [result, isLoading] = useNodeData( - studyUuid, - nodeUuid, - fetchDynamicSimulationStatus, - dynamicSimulationResultInvalidations, - null, - (status: RunningStatus) => - status && [ - { - status, - }, - ] - ); + const [result, isLoading] = useNodeData( + studyUuid, + nodeUuid, + currentRootNetworkUuid, + fetchDynamicSimulationStatus, + dynamicSimulationResultInvalidations, + null, + (status: RunningStatus) => + status && [ + { + status, + }, + ] + ); - const columnDefs = useMemo( - () => [ - makeAgGridCustomHeaderColumn({ - headerName: intl.formatMessage({ + const columnDefs = useMemo( + () => [ + makeAgGridCustomHeaderColumn({ + headerName: intl.formatMessage({ + id: 'status', + }), id: 'status', + field: 'status', + width: MEDIUM_COLUMN_WIDTH, + cellRenderer: StatusCellRender, }), - id: 'status', - field: 'status', - width: MEDIUM_COLUMN_WIDTH, - cellRenderer: StatusCellRender, - }), - ], - [intl] - ); + ], + [intl] + ); - // messages to show when no data - const dynamicSimulationStatus = useSelector( - (state: AppState) => state.computingStatus[ComputingType.DYNAMIC_SIMULATION] - ); - const messages = useIntlResultStatusMessages(intl, true); - const overlayMessage = useMemo( - () => getNoRowsMessage(messages, result, dynamicSimulationStatus, !isLoading), - [messages, result, dynamicSimulationStatus, isLoading] - ); + // messages to show when no data + const dynamicSimulationStatus = useSelector( + (state: AppState) => state.computingStatus[ComputingType.DYNAMIC_SIMULATION] + ); + const messages = useIntlResultStatusMessages(intl, true); + const overlayMessage = useMemo( + () => getNoRowsMessage(messages, result, dynamicSimulationStatus, !isLoading), + [messages, result, dynamicSimulationStatus, isLoading] + ); - const rowDataToShow = useMemo(() => (overlayMessage ? [] : result), [result, overlayMessage]); + const rowDataToShow = useMemo(() => (overlayMessage ? [] : result), [result, overlayMessage]); - return ( - <> - {isLoading && ( - - - - )} - - - ); -}); + return ( + <> + {isLoading && ( + + + + )} + + + ); + } +); export default DynamicSimulationResultSynthesis; diff --git a/src/components/results/dynamicsimulation/dynamic-simulation-result-tab.jsx b/src/components/results/dynamicsimulation/dynamic-simulation-result-tab.jsx index 11faf3c179..1e9698ba0c 100644 --- a/src/components/results/dynamicsimulation/dynamic-simulation-result-tab.jsx +++ b/src/components/results/dynamicsimulation/dynamic-simulation-result-tab.jsx @@ -25,7 +25,7 @@ const TAB_INDEX_TIMELINE = 'DynamicSimulationTabTimeline'; const TAB_INDEX_STATUS = 'DynamicSimulationTabStatus'; const TAB_INDEX_LOGS = 'ComputationResultsLogs'; -const DynamicSimulationResultTab = ({ studyUuid, nodeUuid }) => { +const DynamicSimulationResultTab = ({ studyUuid, nodeUuid, currentRootNetworkUuid }) => { const intl = useIntl(); const [tabIndex, setTabIndex] = useState(TAB_INDEX_TIME_SERIES); @@ -66,13 +66,25 @@ const DynamicSimulationResultTab = ({ studyUuid, nodeUuid }) => { - + - + - + diff --git a/src/components/results/dynamicsimulation/dynamic-simulation-result-time-series.jsx b/src/components/results/dynamicsimulation/dynamic-simulation-result-time-series.jsx index 9ade8cc765..4ca9633495 100644 --- a/src/components/results/dynamicsimulation/dynamic-simulation-result-time-series.jsx +++ b/src/components/results/dynamicsimulation/dynamic-simulation-result-time-series.jsx @@ -36,8 +36,8 @@ const styles = { }, }; -const DynamicSimulationResultTimeSeries = memo(({ nodeUuid, studyUuid }) => { - const [result, loadTimeSeries, isLoading] = useResultTimeSeries(nodeUuid, studyUuid); +const DynamicSimulationResultTimeSeries = memo(({ nodeUuid, studyUuid, currentRootNetworkUuid }) => { + const [result, loadTimeSeries, isLoading] = useResultTimeSeries(nodeUuid, studyUuid, currentRootNetworkUuid); // tab id is automatically increased and reset to zero when there is no tab. const [tabIncId, setTabIncId] = useState(1); @@ -182,6 +182,7 @@ const DynamicSimulationResultTimeSeries = memo(({ nodeUuid, studyUuid }) => { DynamicSimulationResultTimeSeries.propTypes = { nodeUuid: PropTypes.string, studyUuid: PropTypes.string, + currentRootNetworkUuid: PropTypes.string, }; export default DynamicSimulationResultTimeSeries; diff --git a/src/components/results/dynamicsimulation/dynamic-simulation-result-timeline.tsx b/src/components/results/dynamicsimulation/dynamic-simulation-result-timeline.tsx index 62e790345c..f452c2b985 100644 --- a/src/components/results/dynamicsimulation/dynamic-simulation-result-timeline.tsx +++ b/src/components/results/dynamicsimulation/dynamic-simulation-result-timeline.tsx @@ -65,138 +65,142 @@ const defaultColDef = { type DynamicSimulationResultTimelineProps = { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; }; -const DynamicSimulationResultTimeline = memo(({ studyUuid, nodeUuid }: DynamicSimulationResultTimelineProps) => { - const intl = useIntl(); - const gridRef = useRef(null); +const DynamicSimulationResultTimeline = memo( + ({ studyUuid, nodeUuid, currentRootNetworkUuid }: DynamicSimulationResultTimelineProps) => { + const intl = useIntl(); + const gridRef = useRef(null); - const [timelines, isLoading] = useNodeData( - studyUuid, - nodeUuid, - fetchDynamicSimulationResultTimeline, - dynamicSimulationResultInvalidations - ); + const [timelines, isLoading] = useNodeData( + studyUuid, + nodeUuid, + currentRootNetworkUuid, + fetchDynamicSimulationResultTimeline, + dynamicSimulationResultInvalidations + ); - const { onSortChanged, sortConfig } = useAgGridSort(DYNAMIC_SIMULATION_RESULT_SORT_STORE, TIMELINE); + const { onSortChanged, sortConfig } = useAgGridSort(DYNAMIC_SIMULATION_RESULT_SORT_STORE, TIMELINE); - const { updateFilter, filterSelector } = useAggridLocalRowFilter(gridRef, { - filterType: DYNAMIC_SIMULATION_RESULT_STORE_FIELD, - filterTab: TIMELINE, - // @ts-expect-error TODO: found how to have Action type in props type - filterStoreAction: setDynamicSimulationResultFilter, - }); + const { updateFilter, filterSelector } = useAggridLocalRowFilter(gridRef, { + filterType: DYNAMIC_SIMULATION_RESULT_STORE_FIELD, + filterTab: TIMELINE, + // @ts-expect-error TODO: found how to have Action type in props type + filterStoreAction: setDynamicSimulationResultFilter, + }); - const sortAndFilterProps = useMemo( - () => ({ - sortProps: { - onSortChanged, - sortConfig, - }, - filterProps: { - updateFilter, - filterSelector, - }, - }), - [onSortChanged, sortConfig, updateFilter, filterSelector] - ); - - // columns are defined from fields in {@link TimelineEvent} types - const columnDefs = useMemo( - () => [ - makeAgGridCustomHeaderColumn({ - headerName: intl.formatMessage({ - id: 'DynamicSimulationTimelineEventTime', - }), - field: COL_TIME, - width: MIN_COLUMN_WIDTH, - numeric: true, - fractionDigits: 2, - id: 'agNumberColumnFilter', - filter: 'agNumberColumnFilter', - filterComponent: CustomAggridComparatorFilter, - filterComponentParams: { - filterParams: { - ...sortAndFilterProps.filterProps, - filterDataType: FILTER_DATA_TYPES.NUMBER, - filterComparators: Object.values(FILTER_NUMBER_COMPARATORS), - }, - }, - cellRenderer: NumberCellRenderer, + const sortAndFilterProps = useMemo( + () => ({ sortProps: { - ...sortAndFilterProps.sortProps, + onSortChanged, + sortConfig, + }, + filterProps: { + updateFilter, + filterSelector, }, }), - makeAgGridCustomHeaderColumn({ - headerName: intl.formatMessage({ - id: 'DynamicSimulationTimelineEventModelName', + [onSortChanged, sortConfig, updateFilter, filterSelector] + ); + + // columns are defined from fields in {@link TimelineEvent} types + const columnDefs = useMemo( + () => [ + makeAgGridCustomHeaderColumn({ + headerName: intl.formatMessage({ + id: 'DynamicSimulationTimelineEventTime', + }), + field: COL_TIME, + width: MIN_COLUMN_WIDTH, + numeric: true, + fractionDigits: 2, + id: 'agNumberColumnFilter', + filter: 'agNumberColumnFilter', + filterComponent: CustomAggridComparatorFilter, + filterComponentParams: { + filterParams: { + ...sortAndFilterProps.filterProps, + filterDataType: FILTER_DATA_TYPES.NUMBER, + filterComparators: Object.values(FILTER_NUMBER_COMPARATORS), + }, + }, + cellRenderer: NumberCellRenderer, + sortProps: { + ...sortAndFilterProps.sortProps, + }, }), - id: COL_MODEL_NAME, - field: COL_MODEL_NAME, - width: MEDIUM_COLUMN_WIDTH, - filterComponent: CustomAggridComparatorFilter, - filterComponentParams: { - filterParams: { - ...sortAndFilterProps.filterProps, - filterDataType: FILTER_DATA_TYPES.TEXT, - filterComparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], + makeAgGridCustomHeaderColumn({ + headerName: intl.formatMessage({ + id: 'DynamicSimulationTimelineEventModelName', + }), + id: COL_MODEL_NAME, + field: COL_MODEL_NAME, + width: MEDIUM_COLUMN_WIDTH, + filterComponent: CustomAggridComparatorFilter, + filterComponentParams: { + filterParams: { + ...sortAndFilterProps.filterProps, + filterDataType: FILTER_DATA_TYPES.TEXT, + filterComparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], + }, + }, + sortProps: { + ...sortAndFilterProps.sortProps, }, - }, - sortProps: { - ...sortAndFilterProps.sortProps, - }, - }), - makeAgGridCustomHeaderColumn({ - headerName: intl.formatMessage({ - id: 'DynamicSimulationTimelineEventModelMessage', }), - id: COL_MESSAGE, - field: COL_MESSAGE, - width: LARGE_COLUMN_WIDTH, - filterComponent: CustomAggridComparatorFilter, - filterComponentParams: { - filterParams: { - ...sortAndFilterProps.filterProps, - filterDataType: FILTER_DATA_TYPES.TEXT, - filterComparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], + makeAgGridCustomHeaderColumn({ + headerName: intl.formatMessage({ + id: 'DynamicSimulationTimelineEventModelMessage', + }), + id: COL_MESSAGE, + field: COL_MESSAGE, + width: LARGE_COLUMN_WIDTH, + filterComponent: CustomAggridComparatorFilter, + filterComponentParams: { + filterParams: { + ...sortAndFilterProps.filterProps, + filterDataType: FILTER_DATA_TYPES.TEXT, + filterComparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], + }, }, - }, - sortProps: { - ...sortAndFilterProps.sortProps, - }, - }), - ], - [intl, sortAndFilterProps] - ); + sortProps: { + ...sortAndFilterProps.sortProps, + }, + }), + ], + [intl, sortAndFilterProps] + ); - // messages to show when no data - const dynamicSimulationStatus = useSelector( - (state: AppState) => state.computingStatus[ComputingType.DYNAMIC_SIMULATION] - ); - const messages = useIntlResultStatusMessages(intl, true); - const overlayMessage = useMemo( - () => getNoRowsMessage(messages, timelines, dynamicSimulationStatus, !isLoading), - [messages, timelines, dynamicSimulationStatus, isLoading] - ); + // messages to show when no data + const dynamicSimulationStatus = useSelector( + (state: AppState) => state.computingStatus[ComputingType.DYNAMIC_SIMULATION] + ); + const messages = useIntlResultStatusMessages(intl, true); + const overlayMessage = useMemo( + () => getNoRowsMessage(messages, timelines, dynamicSimulationStatus, !isLoading), + [messages, timelines, dynamicSimulationStatus, isLoading] + ); - const rowDataToShow = useMemo(() => (overlayMessage ? [] : timelines), [timelines, overlayMessage]); + const rowDataToShow = useMemo(() => (overlayMessage ? [] : timelines), [timelines, overlayMessage]); - return ( - <> - {isLoading && ( - - - - )} - - - ); -}); + return ( + <> + {isLoading && ( + + + + )} + + + ); + } +); export default DynamicSimulationResultTimeline; diff --git a/src/components/results/dynamicsimulation/hooks/useResultTimeSeries.ts b/src/components/results/dynamicsimulation/hooks/useResultTimeSeries.ts index e02ee41fa3..fa1ba7e9c9 100644 --- a/src/components/results/dynamicsimulation/hooks/useResultTimeSeries.ts +++ b/src/components/results/dynamicsimulation/hooks/useResultTimeSeries.ts @@ -14,10 +14,11 @@ import { TimeSeriesMetadata } from '../types/dynamic-simulation-result.type'; import { dynamicSimulationResultInvalidations } from '../utils/dynamic-simulation-result-utils'; import { fetchDynamicSimulationTimeSeriesMetadata } from '../../../../services/dynamic-simulation'; -const useResultTimeSeries = (nodeUuid: UUID, studyUuid: UUID) => { +const useResultTimeSeries = (nodeUuid: UUID, studyUuid: UUID, currentRootNetworkUuid: UUID) => { const [result, isLoading] = useNodeData( studyUuid, nodeUuid, + currentRootNetworkUuid, fetchDynamicSimulationTimeSeriesMetadata, dynamicSimulationResultInvalidations, null, @@ -50,7 +51,12 @@ const useResultTimeSeries = (nodeUuid: UUID, studyUuid: UUID) => { (indexValue) => result.timeseriesMetadatas[indexValue].name ); - return fetchDynamicSimulationResultTimeSeries(studyUuid, nodeUuid, timeSeriesNamesToLoad) + return fetchDynamicSimulationResultTimeSeries( + studyUuid, + nodeUuid, + currentRootNetworkUuid, + timeSeriesNamesToLoad + ) .then((newlyLoadedTimeSeries) => { // insert one by one newly loaded timeserie into the cache for (const newSeries of newlyLoadedTimeSeries) { @@ -77,7 +83,7 @@ const useResultTimeSeries = (nodeUuid: UUID, studyUuid: UUID) => { }); } }, - [studyUuid, nodeUuid, result, snackError] + [studyUuid, nodeUuid, currentRootNetworkUuid, result, snackError] ); return [result, lazyLoadTimeSeriesCb, isLoading]; diff --git a/src/components/results/loadflow/load-flow-result-tab.tsx b/src/components/results/loadflow/load-flow-result-tab.tsx index 99de33add2..a9ed33be42 100644 --- a/src/components/results/loadflow/load-flow-result-tab.tsx +++ b/src/components/results/loadflow/load-flow-result-tab.tsx @@ -68,7 +68,11 @@ export interface GlobalFilter { limitViolationsTypes?: LimitTypes[]; } -export const LoadFlowResultTab: FunctionComponent = ({ studyUuid, nodeUuid }) => { +export const LoadFlowResultTab: FunctionComponent = ({ + studyUuid, + nodeUuid, + currentRootNetworkUuid, +}) => { const { snackError } = useSnackMessage(); const intl = useIntl(); const loadflowResultInvalidations = ['loadflowResult']; @@ -94,7 +98,7 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu // load countries useEffect(() => { - fetchAllCountries(studyUuid, nodeUuid) + fetchAllCountries(studyUuid, nodeUuid, currentRootNetworkUuid) .then((countryCodes) => { setCountriesFilter( countryCodes.map((countryCode: string) => ({ @@ -109,7 +113,8 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu headerId: 'FetchCountryError', }); }); - fetchAllNominalVoltages(studyUuid, nodeUuid) + + fetchAllNominalVoltages(studyUuid, nodeUuid, currentRootNetworkUuid) .then((nominalVoltages) => { setVoltageLevelsFilter( nominalVoltages.map((nominalV: number) => ({ @@ -124,7 +129,7 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu headerId: 'FetchNominalVoltagesError', }); }); - }, [nodeUuid, studyUuid, snackError, loadFlowStatus]); + }, [nodeUuid, studyUuid, currentRootNetworkUuid, snackError, loadFlowStatus]); const getGlobalFilterParameter = useCallback( (globalFilter: GlobalFilter | undefined) => { @@ -174,7 +179,7 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu value: limitTypeValues, }); } - return fetchLimitViolations(studyUuid, nodeUuid, { + return fetchLimitViolations(studyUuid, nodeUuid, currentRootNetworkUuid, { sort: sortConfig.map((sort) => ({ ...sort, colId: FROM_COLUMN_TO_FIELD_LIMIT_VIOLATION_RESULT[sort.colId], @@ -182,14 +187,24 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu filters: mapFieldsToColumnsFilter(updatedFilters, mappingFields(tabIndex)), globalFilters: getGlobalFilterParameter(globalFilter), }); - }, [studyUuid, nodeUuid, sortConfig, filterSelector, tabIndex, globalFilter, getGlobalFilterParameter, intl]); + }, [ + studyUuid, + nodeUuid, + currentRootNetworkUuid, + sortConfig, + filterSelector, + tabIndex, + globalFilter, + getGlobalFilterParameter, + intl, + ]); const fetchloadflowResultWithParameters = useCallback(() => { - return fetchLoadFlowResult(studyUuid, nodeUuid, { + return fetchLoadFlowResult(studyUuid, nodeUuid, currentRootNetworkUuid, { sort: sortConfig, filters: filterSelector, }); - }, [studyUuid, nodeUuid, sortConfig, filterSelector]); + }, [studyUuid, nodeUuid, currentRootNetworkUuid, sortConfig, filterSelector]); const fetchResult = useMemo(() => { if (tabIndex === 0 || tabIndex === 1) { @@ -202,6 +217,7 @@ export const LoadFlowResultTab: FunctionComponent = ({ studyUu const [loadflowResult, isLoadingResult, setResult] = useNodeData( studyUuid, nodeUuid, + currentRootNetworkUuid, fetchResult, loadflowResultInvalidations ); diff --git a/src/components/results/loadflow/load-flow-result-utils.ts b/src/components/results/loadflow/load-flow-result-utils.ts index 14cdd8588d..cd115a7ae8 100644 --- a/src/components/results/loadflow/load-flow-result-utils.ts +++ b/src/components/results/loadflow/load-flow-result-utils.ts @@ -178,17 +178,24 @@ export const useFetchFiltersEnums = (): { }); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetwork = useSelector((state: AppState) => state.currentRootNetwork); const loadFlowStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.LOAD_FLOW]); useEffect(() => { - if (loadFlowStatus !== RunningStatus.SUCCEED || !studyUuid || !currentNode?.id) { + if (loadFlowStatus !== RunningStatus.SUCCEED || !studyUuid || !currentNode?.id || !currentRootNetwork) { return; } const filterTypes = ['computation-status', 'limit-types', 'branch-sides']; const promises = filterTypes.map((filterType) => - fetchAvailableFilterEnumValues(studyUuid, currentNode.id, computingType.LOAD_FLOW, filterType) + fetchAvailableFilterEnumValues( + studyUuid, + currentNode.id, + currentRootNetwork, + computingType.LOAD_FLOW, + filterType + ) ); setLoading(true); @@ -206,7 +213,7 @@ export const useFetchFiltersEnums = (): { .finally(() => { setLoading(false); }); - }, [loadFlowStatus, studyUuid, currentNode?.id]); + }, [loadFlowStatus, studyUuid, currentNode?.id, currentRootNetwork]); return { loading, result, error }; }; diff --git a/src/components/results/loadflow/load-flow-result.type.ts b/src/components/results/loadflow/load-flow-result.type.ts index 15e0ebe17c..9bf133dafe 100644 --- a/src/components/results/loadflow/load-flow-result.type.ts +++ b/src/components/results/loadflow/load-flow-result.type.ts @@ -41,6 +41,7 @@ export enum LimitTypes { export interface LoadFlowTabProps { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; } export interface LoadflowResultTap { diff --git a/src/components/results/securityanalysis/security-analysis-export-button.tsx b/src/components/results/securityanalysis/security-analysis-export-button.tsx index 24ef42436d..10ff5d2edc 100644 --- a/src/components/results/securityanalysis/security-analysis-export-button.tsx +++ b/src/components/results/securityanalysis/security-analysis-export-button.tsx @@ -18,13 +18,14 @@ import { PERMANENT_LIMIT_NAME } from '../common/utils'; interface SecurityAnalysisExportButtonProps { studyUuid: UUID; nodeUuid: UUID; + rootNetworkUuid: UUID; csvHeaders?: string[]; resultType: RESULT_TYPE; disabled?: boolean; } export const SecurityAnalysisExportButton: FunctionComponent = (props) => { - const { studyUuid, nodeUuid, csvHeaders, disabled, resultType } = props; + const { studyUuid, nodeUuid, rootNetworkUuid, csvHeaders, disabled, resultType } = props; const { snackError } = useSnackMessage(); const [isCsvExportLoading, setIsCsvExportLoading] = useState(false); @@ -70,6 +71,7 @@ export const SecurityAnalysisExportButton: FunctionComponent setIsCsvExportLoading(false)); - }, [resultType, csvHeaders, enumValueTranslations, studyUuid, nodeUuid, snackError, intl]); + }, [resultType, csvHeaders, enumValueTranslations, studyUuid, nodeUuid, rootNetworkUuid, snackError, intl]); return ( = ({ studyUuid, nodeUuid, + currentRootNetworkUuid, openVoltageLevelDiagram, }) => { const intl = useIntl(); @@ -164,14 +165,25 @@ export const SecurityAnalysisResultTab: FunctionComponent { }); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const securityAnalysisStatus = useSelector( (state: AppState) => state.computingStatus[ComputingType.SECURITY_ANALYSIS] ); useEffect(() => { - if (securityAnalysisStatus !== RunningStatus.SUCCEED || !studyUuid || !currentNode?.id) { + if ( + securityAnalysisStatus !== RunningStatus.SUCCEED || + !studyUuid || + !currentNode?.id || + !currentRootNetworkUuid + ) { return; } @@ -694,7 +700,13 @@ export const useFetchFiltersEnums = () => { ]; const promises = filterTypes.map((filterType) => - fetchAvailableFilterEnumValues(studyUuid, currentNode.id, computingType.SECURITY_ANALYSIS, filterType) + fetchAvailableFilterEnumValues( + studyUuid, + currentNode.id, + currentRootNetworkUuid, + computingType.SECURITY_ANALYSIS, + filterType + ) ); setLoading(true); @@ -726,7 +738,7 @@ export const useFetchFiltersEnums = () => { .finally(() => { setLoading(false); }); - }, [securityAnalysisStatus, studyUuid, currentNode?.id]); + }, [securityAnalysisStatus, studyUuid, currentNode?.id, currentRootNetworkUuid]); return { loading, result, error }; }; diff --git a/src/components/results/securityanalysis/security-analysis.type.ts b/src/components/results/securityanalysis/security-analysis.type.ts index e72a2b1e09..cad7a24441 100644 --- a/src/components/results/securityanalysis/security-analysis.type.ts +++ b/src/components/results/securityanalysis/security-analysis.type.ts @@ -122,6 +122,7 @@ export interface SecurityAnalysisNmkResult { export interface SecurityAnalysisTabProps { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; openVoltageLevelDiagram: (id: string) => void; } diff --git a/src/components/results/securityanalysis/use-security-analysis-column-defs.tsx b/src/components/results/securityanalysis/use-security-analysis-column-defs.tsx index 5be37f39d3..f448f02ed0 100644 --- a/src/components/results/securityanalysis/use-security-analysis-column-defs.tsx +++ b/src/components/results/securityanalysis/use-security-analysis-column-defs.tsx @@ -54,7 +54,7 @@ export const useSecurityAnalysisColumnsDefs: UseSecurityAnalysisColumnsDefsProps const { snackError } = useSnackMessage(); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); - + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const nodeUuid = currentNode?.id; const getEnumLabel = useCallback( @@ -69,7 +69,7 @@ export const useSecurityAnalysisColumnsDefs: UseSecurityAnalysisColumnsDefsProps // for nmk views, click handler on subjectId cell const onClickNmKConstraint = useCallback( (row: SecurityAnalysisNmkTableRow, column?: ColDef) => { - if (studyUuid && nodeUuid) { + if (studyUuid && nodeUuid && currentRootNetworkUuid) { if (column?.field === 'subjectId') { let vlId: string | undefined = ''; const { subjectId, side } = row || {}; @@ -86,6 +86,7 @@ export const useSecurityAnalysisColumnsDefs: UseSecurityAnalysisColumnsDefsProps fetchVoltageLevelIdForLineOrTransformerBySide( studyUuid, nodeUuid, + currentRootNetworkUuid, subjectId ?? '', getBranchSide(side) ?? BranchSide.ONE ) @@ -115,7 +116,7 @@ export const useSecurityAnalysisColumnsDefs: UseSecurityAnalysisColumnsDefsProps } } }, - [nodeUuid, openVoltageLevelDiagram, snackError, studyUuid, intl] + [nodeUuid, currentRootNetworkUuid, openVoltageLevelDiagram, snackError, studyUuid, intl] ); // for nmk views, custom view for subjectId cell diff --git a/src/components/results/sensitivity-analysis/non-evacuated-energy/non-evacuated-energy-result-tab.tsx b/src/components/results/sensitivity-analysis/non-evacuated-energy/non-evacuated-energy-result-tab.tsx index 5dd8f96aad..b246242d49 100644 --- a/src/components/results/sensitivity-analysis/non-evacuated-energy/non-evacuated-energy-result-tab.tsx +++ b/src/components/results/sensitivity-analysis/non-evacuated-energy/non-evacuated-energy-result-tab.tsx @@ -41,7 +41,11 @@ const styles = { export const NON_EVACUATED_ENERGY_RESULT_INVALIDATIONS = ['nonEvacuatedEnergyResult']; -export const NonEvacuatedEnergyResultTab: FunctionComponent = ({ studyUuid, nodeUuid }) => { +export const NonEvacuatedEnergyResultTab: FunctionComponent = ({ + studyUuid, + nodeUuid, + currentRootNetworkUuid, +}) => { const [tabIndex, setTabIndex] = useState(0); const RESULTS_TAB_INDEX = 0; @@ -54,6 +58,7 @@ export const NonEvacuatedEnergyResultTab: FunctionComponent { setOptions(res); }) @@ -122,7 +123,7 @@ const PagedSensitivityAnalysisResult = ({ }), }); }); - }, [nOrNkIndex, sensiKind, studyUuid, nodeUuid, snackError, intl]); + }, [nOrNkIndex, sensiKind, studyUuid, currentRootNetworkUuid, nodeUuid, snackError, intl]); const fetchResult = useCallback(() => { const sortSelector = sortConfig?.length @@ -150,7 +151,7 @@ const PagedSensitivityAnalysisResult = ({ ...sortSelector, }; setIsLoading(true); - fetchSensitivityAnalysisResult(studyUuid, nodeUuid, selector) + fetchSensitivityAnalysisResult(studyUuid, nodeUuid, currentRootNetworkUuid, selector) .then((res) => { const { filteredSensitivitiesCount = 0 } = res || {}; @@ -168,7 +169,19 @@ const PagedSensitivityAnalysisResult = ({ .finally(() => { setIsLoading(false); }); - }, [nOrNkIndex, sensiKind, page, rowsPerPage, filterSelector, sortConfig, studyUuid, nodeUuid, snackError, intl]); + }, [ + nOrNkIndex, + sensiKind, + page, + rowsPerPage, + filterSelector, + sortConfig, + studyUuid, + nodeUuid, + currentRootNetworkUuid, + snackError, + intl, + ]); useEffect(() => { if (sensiStatus === RunningStatus.RUNNING) { diff --git a/src/components/results/sensitivity-analysis/sensitivity-analysis-result-tab.jsx b/src/components/results/sensitivity-analysis/sensitivity-analysis-result-tab.jsx index 32b9558ccb..88b7181018 100644 --- a/src/components/results/sensitivity-analysis/sensitivity-analysis-result-tab.jsx +++ b/src/components/results/sensitivity-analysis/sensitivity-analysis-result-tab.jsx @@ -46,7 +46,7 @@ function getDisplayedColumns(params) { return params.api.columnModel.columnDefs.map((c) => c.headerComponentParams.displayName); } -const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid }) => { +const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid, currentRootNetworkUuid }) => { const { snackError } = useSnackMessage(); const intl = useIntl(); const [nOrNkIndex, setNOrNkIndex] = useState(0); @@ -111,7 +111,7 @@ const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid }) => { const handleExportResultAsCsv = useCallback(() => { setIsCsvExportLoading(true); setIsCsvExportSuccessful(false); - exportSensitivityResultsAsCsv(studyUuid, nodeUuid, { + exportSensitivityResultsAsCsv(studyUuid, nodeUuid, currentRootNetworkUuid, { csvHeaders: csvHeaders, resultTab: SensitivityResultTabs[nOrNkIndex].id, sensitivityFunctionType: FUNCTION_TYPES[sensiKind], @@ -132,7 +132,7 @@ const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid }) => { setIsCsvExportSuccessful(false); }) .finally(() => setIsCsvExportLoading(false)); - }, [snackError, studyUuid, nodeUuid, intl, nOrNkIndex, sensiKind, csvHeaders]); + }, [snackError, studyUuid, nodeUuid, currentRootNetworkUuid, intl, nOrNkIndex, sensiKind, csvHeaders]); return ( <> @@ -163,6 +163,7 @@ const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid }) => { sensiKind={sensiKind} studyUuid={studyUuid} nodeUuid={nodeUuid} + currentRootNetworkUuid={currentRootNetworkUuid} page={page} setPage={setPage} sortProps={{ @@ -194,6 +195,7 @@ const SensitivityAnalysisResultTab = ({ studyUuid, nodeUuid }) => { SensitivityAnalysisResultTab.propTypes = { studyUuid: PropTypes.string.isRequired, nodeUuid: PropTypes.string.isRequired, + currentRootNetworkUuid: PropTypes.string.isRequired, }; export default SensitivityAnalysisResultTab; diff --git a/src/components/results/shortcircuit/shortcircuit-analysis-export-button.tsx b/src/components/results/shortcircuit/shortcircuit-analysis-export-button.tsx index cb4af1de17..c88733ffe5 100644 --- a/src/components/results/shortcircuit/shortcircuit-analysis-export-button.tsx +++ b/src/components/results/shortcircuit/shortcircuit-analysis-export-button.tsx @@ -18,13 +18,14 @@ import { BranchSide } from 'components/utils/constants'; interface ShortCircuitExportButtonProps { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; csvHeaders?: string[]; analysisType: number; disabled?: boolean; } export const ShortCircuitExportButton: FunctionComponent = (props) => { - const { studyUuid, nodeUuid, csvHeaders, disabled = false, analysisType } = props; + const { studyUuid, nodeUuid, currentRootNetworkUuid, csvHeaders, disabled = false, analysisType } = props; const { snackError } = useSnackMessage(); const [isCsvExportLoading, setIsCsvExportLoading] = useState(false); @@ -62,7 +63,14 @@ export const ShortCircuitExportButton: FunctionComponent { setIsCsvExportLoading(true); setIsCsvExportSuccessful(false); - downloadShortCircuitResultZippedCsv(studyUuid, nodeUuid, analysisType, csvHeaders, enumValueTranslations) + downloadShortCircuitResultZippedCsv( + studyUuid, + nodeUuid, + currentRootNetworkUuid, + analysisType, + csvHeaders, + enumValueTranslations + ) .then((response) => { response.blob().then((fileBlob: Blob) => { downloadZipFile( @@ -84,7 +92,16 @@ export const ShortCircuitExportButton: FunctionComponent setIsCsvExportLoading(false)); - }, [studyUuid, nodeUuid, intl, snackError, csvHeaders, analysisType, enumValueTranslations]); + }, [ + studyUuid, + nodeUuid, + currentRootNetworkUuid, + intl, + snackError, + csvHeaders, + analysisType, + enumValueTranslations, + ]); return ( = ({ studyUuid, nodeUuid, + currentRootNetworkUuid, view, }) => { const lastCompletedComputation = useSelector((state: AppState) => state.lastCompletedComputation); @@ -144,6 +146,7 @@ export const ShortCircuitAnalysisResultTab: FunctionComponent state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const isOneBusShortCircuitAnalysisType = analysisType === ShortCircuitAnalysisType.ONE_BUS; @@ -119,7 +120,9 @@ export const ShortCircuitAnalysisResult: FunctionComponent { - if (analysisStatus !== RunningStatus.SUCCEED || !studyUuid || !currentNode?.id) { + if (analysisStatus !== RunningStatus.SUCCEED || !studyUuid || !currentNode?.id || !currentRootNetworkUuid) { return; } @@ -195,7 +200,13 @@ export const ShortCircuitAnalysisResult: FunctionComponent - fetchAvailableFilterEnumValues(studyUuid, currentNode.id, currentComputingType, filter) + fetchAvailableFilterEnumValues( + studyUuid, + currentNode.id, + currentRootNetworkUuid, + currentComputingType, + filter + ) ); Promise.all(promises) @@ -219,7 +230,15 @@ export const ShortCircuitAnalysisResult: FunctionComponent = ({ studyUuid, nodeUuid }) => { +export const StateEstimationResultTab: FunctionComponent = ({ + studyUuid, + nodeUuid, + currentRootNetworkUuid, +}) => { const intl = useIntl(); const stateEstimationResultInvalidations = ['stateEstimationResult']; @@ -53,8 +57,8 @@ export const StateEstimationResultTab: FunctionComponent { - return fetchStateEstimationResult(studyUuid, nodeUuid); - }, [studyUuid, nodeUuid]); + return fetchStateEstimationResult(studyUuid, nodeUuid, currentRootNetworkUuid); + }, [studyUuid, nodeUuid, currentRootNetworkUuid]); const fetchResult = useMemo(() => { return fetchEstimResults; @@ -63,6 +67,7 @@ export const StateEstimationResultTab: FunctionComponent ({ + paper: { + position: 'absolute', + top: 16, + left: 16, + width: '40%', + height: '40%', + display: 'flex', + flexDirection: 'column', + borderRadius: '8px', + boxShadow: '0 6px 15px rgba(0,0,0,0.15)', // Softer shadow + zIndex: 10, + overflow: 'hidden', // Ensure no overflow + }, + contentBox: { + flex: 1, // Take up all available space + display: 'flex', + flexDirection: 'column', + position: 'relative', // Enable absolute positioning for child elements + }, +}); + +const RootNetworkPanel: FunctionComponent = ({ studyId }) => { + return ( + styles(theme).paper}> + styles(theme).contentBox}> + + + + ); +}; + +export default RootNetworkPanel; diff --git a/src/components/run-button-container.jsx b/src/components/run-button-container.jsx index 58a2610ede..ab943c0bce 100644 --- a/src/components/run-button-container.jsx +++ b/src/components/run-button-container.jsx @@ -35,7 +35,7 @@ import { startStateEstimation, stopStateEstimation } from '../services/study/sta import { OptionalServicesNames, OptionalServicesStatus } from './utils/optional-services'; import { useOptionalServiceStatus } from '../hooks/use-optional-service-status'; -export function RunButtonContainer({ studyUuid, currentNode, disabled }) { +export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkUuid, disabled }) { const loadFlowStatus = useSelector((state) => state.computingStatus[ComputingType.LOAD_FLOW]); const securityAnalysisStatus = useSelector((state) => state.computingStatus[ComputingType.SECURITY_ANALYSIS]); @@ -116,7 +116,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { // close the contingency list selection window setShowContingencyListSelector(false); }, - () => startSecurityAnalysis(studyUuid, currentNode?.id, contingencyListNames), + () => startSecurityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid, contingencyListNames), () => {}, null, null @@ -130,7 +130,13 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { // close the dialog setShowDynamicSimulationParametersSelector(false); }, - () => startDynamicSimulation(studyUuid, currentNode?.id, dynamicSimulationConfiguration), + () => + startDynamicSimulation( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + dynamicSimulationConfiguration + ), () => {}, null, 'DynamicSimulationRunError' @@ -152,7 +158,13 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { startComputationAsync( ComputingType.LOAD_FLOW, null, - () => startLoadFlow(studyUuid, currentNode?.id, limitReductionParam / 100.0), + () => + startLoadFlow( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + limitReductionParam / 100.0 + ), () => {}, null, 'startLoadFlowError' @@ -169,7 +181,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.SECURITY_ANALYSIS, () => - stopSecurityAnalysis(studyUuid, currentNode?.id) + stopSecurityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -179,7 +191,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { startComputationAsync( ComputingType.SENSITIVITY_ANALYSIS, null, - () => startSensitivityAnalysis(studyUuid, currentNode?.id), + () => startSensitivityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid), () => {}, null, 'startSensitivityAnalysisError' @@ -187,7 +199,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.SENSITIVITY_ANALYSIS, () => - stopSensitivityAnalysis(studyUuid, currentNode?.id) + stopSensitivityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -198,7 +210,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { ComputingType.NON_EVACUATED_ENERGY_ANALYSIS, null, () => { - return startNonEvacuatedEnergy(studyUuid, currentNode?.id); + return startNonEvacuatedEnergy(studyUuid, currentNode?.id, currentRootNetworkUuid); }, () => {}, null, @@ -207,7 +219,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.NON_EVACUATED_ENERGY_ANALYSIS, () => - stopNonEvacuatedEnergy(studyUuid, currentNode?.id) + stopNonEvacuatedEnergy(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -217,7 +229,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { startComputationAsync( ComputingType.SHORT_CIRCUIT, null, - () => startShortCircuitAnalysis(studyUuid, currentNode?.id), + () => startShortCircuitAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid), () => {}, null, 'startShortCircuitError' @@ -225,7 +237,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.SHORT_CIRCUIT, () => - stopShortCircuitAnalysis(studyUuid, currentNode?.id) + stopShortCircuitAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -239,7 +251,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { setShowDynamicSimulationParametersSelector(true); } else { // start server side dynamic simulation directly - return startDynamicSimulation(studyUuid, currentNode?.id); + return startDynamicSimulation(studyUuid, currentNode?.id, currentRootNetworkUuid); } }) .catch((error) => { @@ -251,7 +263,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.DYNAMIC_SIMULATION, () => - stopDynamicSimulation(studyUuid, currentNode?.id) + stopDynamicSimulation(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -261,7 +273,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { startComputationAsync( ComputingType.VOLTAGE_INITIALIZATION, null, - () => startVoltageInit(studyUuid, currentNode?.id), + () => startVoltageInit(studyUuid, currentNode?.id, currentRootNetworkUuid), () => {}, null, 'startVoltageInitError' @@ -269,7 +281,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.VOLTAGE_INITIALIZATION, () => - stopVoltageInit(studyUuid, currentNode?.id) + stopVoltageInit(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, @@ -280,7 +292,7 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { ComputingType.STATE_ESTIMATION, null, () => { - return startStateEstimation(studyUuid, currentNode?.id); + return startStateEstimation(studyUuid, currentNode?.id, currentRootNetworkUuid); }, () => {}, null, @@ -289,12 +301,20 @@ export function RunButtonContainer({ studyUuid, currentNode, disabled }) { }, actionOnRunnable() { actionOnRunnables(ComputingType.STATE_ESTIMATION, () => - stopStateEstimation(studyUuid, currentNode?.id) + stopStateEstimation(studyUuid, currentNode?.id, currentRootNetworkUuid) ); }, }, }; - }, [dispatch, snackError, startComputationAsync, studyUuid, limitReductionParam, currentNode?.id]); + }, [ + dispatch, + snackError, + startComputationAsync, + studyUuid, + limitReductionParam, + currentNode?.id, + currentRootNetworkUuid, + ]); // running status is refreshed more often, so we memoize it apart const getRunningStatus = useCallback( diff --git a/src/components/spreadsheet/config/spreadsheet.type.ts b/src/components/spreadsheet/config/spreadsheet.type.ts index 53c1181a45..f8b115365a 100644 --- a/src/components/spreadsheet/config/spreadsheet.type.ts +++ b/src/components/spreadsheet/config/spreadsheet.type.ts @@ -9,7 +9,12 @@ import type { UUID } from 'crypto'; import type { EQUIPMENT_TYPES } from '../../utils/equipment-types'; import type { CustomAggridFilterParams, CustomColDef } from '../../custom-aggrid/custom-aggrid-header.type'; -export type EquipmentFetcher = (studyUuid: UUID, currentNodeUuid: UUID, substationsIds: string[]) => Promise; +export type EquipmentFetcher = ( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds: string[] +) => Promise; export type SpreadsheetEquipmentType = Exclude< EQUIPMENT_TYPES, diff --git a/src/components/spreadsheet/table-wrapper.tsx b/src/components/spreadsheet/table-wrapper.tsx index 5b0343d716..3a57ff8554 100644 --- a/src/components/spreadsheet/table-wrapper.tsx +++ b/src/components/spreadsheet/table-wrapper.tsx @@ -150,6 +150,7 @@ const styles = { interface TableWrapperProps { studyUuid: string; currentNode: CurrentTreeNode; + currentRootNetworkUuid: string; equipmentId: string; equipmentType: SpreadsheetEquipmentType; equipmentChanged: boolean; @@ -159,6 +160,7 @@ interface TableWrapperProps { const TableWrapper: FunctionComponent = ({ studyUuid, currentNode, + currentRootNetworkUuid, equipmentId, equipmentType, equipmentChanged, @@ -1116,6 +1118,7 @@ const TableWrapper: FunctionComponent = ({ fetchNetworkElementInfos( studyUuid, currentNode.id, + currentRootNetworkUuid, lastModifiedEquipment.metadata.equipmentType, EQUIPMENT_INFOS_TYPES.TAB.type, lastModifiedEquipment.id, @@ -1135,7 +1138,15 @@ const TableWrapper: FunctionComponent = ({ }); } } - }, [lastModifiedEquipment, currentNode.id, studyUuid, studyUpdatedForce, formatFetchedEquipmentHandler, dispatch]); + }, [ + lastModifiedEquipment, + currentNode.id, + studyUuid, + currentRootNetworkUuid, + studyUpdatedForce, + formatFetchedEquipmentHandler, + dispatch, + ]); //this listener is called for each cell modified const handleCellEditingStopped = useCallback( diff --git a/src/components/spreadsheet/use-spreadsheet-equipments.ts b/src/components/spreadsheet/use-spreadsheet-equipments.ts index 2dee4d89d6..9cccdca1cd 100644 --- a/src/components/spreadsheet/use-spreadsheet-equipments.ts +++ b/src/components/spreadsheet/use-spreadsheet-equipments.ts @@ -25,7 +25,7 @@ import { fetchAllEquipments } from 'services/study/network-map'; export type EquipmentProps = { type: SpreadsheetEquipmentType; - fetchers: Array<(studyUuid: UUID, currentNodeId: UUID) => Promise>; + fetchers: Array<(studyUuid: UUID, currentNodeId: UUID, currentRootNetworkUuid: UUID) => Promise>; }; type FormatFetchedEquipments = (equipments: Identifiable[]) => Identifiable[]; @@ -39,6 +39,7 @@ export const useSpreadsheetEquipments = ( const equipments = allEquipments[equipment.type]; const studyUuid = useSelector((state: AppState) => state.studyUuid); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const currentNode = useSelector((state: AppState) => state.currentTreeNode); const [errorMessage, setErrorMessage] = useState(); const [isFetching, setIsFetching] = useState(false); @@ -82,11 +83,13 @@ export const useSpreadsheetEquipments = ( } resetImpactedElementTypes(); } - if (impactedSubstationsIds.length > 0 && studyUuid && currentNode?.id) { + if (impactedSubstationsIds.length > 0 && studyUuid && currentRootNetworkUuid && currentNode?.id) { // The formatting of the fetched equipments is done in the reducer - fetchAllEquipments(studyUuid, currentNode.id, impactedSubstationsIds).then((values) => { - dispatch(updateEquipments(values)); - }); + fetchAllEquipments(studyUuid, currentRootNetworkUuid, currentNode.id, impactedSubstationsIds).then( + (values) => { + dispatch(updateEquipments(values)); + } + ); resetImpactedSubstationsIds(); } if (deletedEquipments.length > 0) { @@ -118,6 +121,7 @@ export const useSpreadsheetEquipments = ( deletedEquipments, impactedElementTypes, studyUuid, + currentRootNetworkUuid, currentNode?.id, dispatch, allEquipments, @@ -127,10 +131,12 @@ export const useSpreadsheetEquipments = ( ]); useEffect(() => { - if (shouldFetchEquipments && studyUuid && currentNode?.id) { + if (shouldFetchEquipments && studyUuid && currentRootNetworkUuid && currentNode?.id) { setErrorMessage(null); setIsFetching(true); - Promise.all(equipment.fetchers.map((fetcher) => fetcher(studyUuid, currentNode?.id))) + Promise.all( + equipment.fetchers.map((fetcher) => fetcher(studyUuid, currentNode?.id, currentRootNetworkUuid)) + ) .then((results) => { let fetchedEquipments = results.flat(); if (formatFetchedEquipments) { @@ -144,7 +150,15 @@ export const useSpreadsheetEquipments = ( setIsFetching(false); }); } - }, [equipment, shouldFetchEquipments, studyUuid, currentNode?.id, dispatch, formatFetchedEquipments]); + }, [ + equipment, + shouldFetchEquipments, + studyUuid, + currentRootNetworkUuid, + currentNode?.id, + dispatch, + formatFetchedEquipments, + ]); return { equipments, errorMessage, isFetching }; }; diff --git a/src/components/spreadsheet/utils/equipment-table-editors.tsx b/src/components/spreadsheet/utils/equipment-table-editors.tsx index 4610128193..2ff665cda2 100644 --- a/src/components/spreadsheet/utils/equipment-table-editors.tsx +++ b/src/components/spreadsheet/utils/equipment-table-editors.tsx @@ -111,6 +111,7 @@ export const GeneratorRegulatingTerminalEditor = forwardRef( open={openGeneratorPopup} onClose={handleCancelRegulatingTerminalPopup} currentNode={gridContext.currentNode} + currentRootNetworkUuid={gridContext.currentRootNetworkUuid} studyUuid={gridContext.studyUuid} onModifyRegulatingTerminalGenerator={(updatedRegulatedTerminal: any) => { handleSaveRegulatingTerminalPopup(updatedRegulatedTerminal); @@ -188,6 +189,7 @@ export const TWTRegulatingTerminalEditor = forwardRef( open={openTWTRegulatingTerminalPopup} onClose={handleCancelRegulatingTerminalPopup} currentNode={gridContext.currentNode} + currentRootNetworkUuid={gridContext.currentRootNetworkUuid} studyUuid={gridContext.studyUuid} onModifyRegulatingTerminalGenerator={(updatedRegulatedTerminal: any) => { handleSaveRegulatingTerminalPopup(updatedRegulatedTerminal); diff --git a/src/components/study-container.jsx b/src/components/study-container.jsx index 65ed6b1c93..459294497f 100644 --- a/src/components/study-container.jsx +++ b/src/components/study-container.jsx @@ -21,7 +21,10 @@ import { resetEquipmentsPostLoadflow, setStudyIndexationStatus, limitReductionModified, + setCurrentRootNetwork, } from '../redux/actions'; +import { fetchRootNetworks } from 'services/root-network'; + import WaitingLoader from './utils/waiting-loader'; import { useIntlRef, useSnackMessage } from '@gridsuite/commons-ui'; import NetworkModificationTreeModel from './graph/network-modification-tree-model'; @@ -48,14 +51,27 @@ import { StudyIndexationStatus } from 'redux/reducer'; import { fetchDirectoryElementPath } from '@gridsuite/commons-ui'; import { NodeType } from './graph/tree-node.type'; -function isWorthUpdate(studyUpdatedForce, fetcher, lastUpdateRef, nodeUuidRef, nodeUuid, invalidations) { +function isWorthUpdate( + studyUpdatedForce, + fetcher, + lastUpdateRef, + nodeUuidRef, + rootNetworkUuidRef, + nodeUuid, + rootNetworkUuid, + invalidations +) { const headers = studyUpdatedForce?.eventData?.headers; const updateType = headers?.[UPDATE_TYPE_HEADER]; const node = headers?.['node']; const nodes = headers?.['nodes']; + if (nodeUuidRef.current !== nodeUuid) { return true; } + if (rootNetworkUuidRef.current !== rootNetworkUuid) { + return true; + } if (fetcher && lastUpdateRef.current?.fetcher !== fetcher) { return true; } @@ -78,21 +94,31 @@ function isWorthUpdate(studyUpdatedForce, fetcher, lastUpdateRef, nodeUuidRef, n return false; } -export function useNodeData(studyUuid, nodeUuid, fetcher, invalidations, defaultValue, resultConversion) { +export function useNodeData( + studyUuid, + nodeUuid, + currentRootNetworkUuid, + fetcher, + invalidations, + defaultValue, + resultConversion +) { const [result, setResult] = useState(defaultValue); const [isPending, setIsPending] = useState(false); const [errorMessage, setErrorMessage] = useState(undefined); const nodeUuidRef = useRef(); + const rootNetworkUuidRef = useRef(); const studyUpdatedForce = useSelector((state) => state.studyUpdated); const lastUpdateRef = useRef(); const update = useCallback(() => { nodeUuidRef.current = nodeUuid; + rootNetworkUuidRef.current = currentRootNetworkUuid; setIsPending(true); setErrorMessage(undefined); - fetcher(studyUuid, nodeUuid) + fetcher(studyUuid, nodeUuid, currentRootNetworkUuid) .then((res) => { - if (nodeUuidRef.current === nodeUuid) { + if (nodeUuidRef.current === nodeUuid && rootNetworkUuidRef.current === currentRootNetworkUuid) { setResult(resultConversion ? resultConversion(res) : res); } }) @@ -101,11 +127,11 @@ export function useNodeData(studyUuid, nodeUuid, fetcher, invalidations, default setResult(RunningStatus.FAILED); }) .finally(() => setIsPending(false)); - }, [nodeUuid, fetcher, studyUuid, resultConversion]); + }, [nodeUuid, fetcher, currentRootNetworkUuid, studyUuid, resultConversion]); /* initial fetch and update */ useEffect(() => { - if (!studyUuid || !nodeUuid || !fetcher) { + if (!studyUuid || !nodeUuid || !currentRootNetworkUuid || !fetcher) { return; } const isUpdateForUs = isWorthUpdate( @@ -113,19 +139,26 @@ export function useNodeData(studyUuid, nodeUuid, fetcher, invalidations, default fetcher, lastUpdateRef, nodeUuidRef, + rootNetworkUuidRef, nodeUuid, + currentRootNetworkUuid, invalidations ); lastUpdateRef.current = { studyUpdatedForce, fetcher }; - if (nodeUuidRef.current !== nodeUuid || isUpdateForUs) { + if ( + nodeUuidRef.current !== nodeUuid || + rootNetworkUuidRef.current !== currentRootNetworkUuid || + isUpdateForUs + ) { update(); } - }, [update, fetcher, nodeUuid, invalidations, studyUpdatedForce, studyUuid]); + }, [update, fetcher, nodeUuid, invalidations, currentRootNetworkUuid, studyUpdatedForce, studyUuid]); return [result, isPending, setResult, errorMessage, update]; } function useStudy(studyUuidRequest) { + const dispatch = useDispatch(); const [studyUuid, setStudyUuid] = useState(undefined); const [pending, setPending] = useState(true); const [errMessage, setErrMessage] = useState(undefined); @@ -135,8 +168,32 @@ function useStudy(studyUuidRequest) { fetchStudyExists(studyUuidRequest) .then(() => { setStudyUuid(studyUuidRequest); + + // Fetch root networks and set the first one as the current root network + fetchRootNetworks(studyUuidRequest) + .then((rootNetworks) => { + if (rootNetworks && rootNetworks.length > 0) { + // Validate that currentRootNetwork is set + dispatch(setCurrentRootNetwork(rootNetworks[0].rootNetworkUuid)); + } else { + // Handle case where no root networks are available + setErrMessage( + intlRef.current.formatMessage( + { id: 'rootNetworkNotFound' }, + { studyUuid: studyUuidRequest } + ) + ); + } + }) + .catch((error) => { + // Handle errors when fetching root networks + setErrMessage( + intlRef.current.formatMessage({ id: 'rootNetworkNotFound' }, { error: error.message }) + ); + }); }) .catch((error) => { + // Handle errors when fetching study existence if (error.status === HttpStatusCode.NOT_FOUND) { setErrMessage( intlRef.current.formatMessage({ id: 'studyNotFound' }, { studyUuid: studyUuidRequest }) @@ -146,7 +203,7 @@ function useStudy(studyUuidRequest) { } }) .finally(() => setPending(false)); - }, [studyUuidRequest, intlRef]); + }, [studyUuidRequest, dispatch, intlRef]); return [studyUuid, pending, errMessage]; } @@ -191,10 +248,12 @@ export function StudyContainer({ view, onChangeTab }) { const dispatch = useDispatch(); const currentNode = useSelector((state) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state) => state.currentRootNetwork); const currentNodeRef = useRef(); + const currentRootNetworkRef = useRef(); - useAllComputingStatus(studyUuid, currentNode?.id); + useAllComputingStatus(studyUuid, currentNode?.id, currentRootNetworkUuid); const studyUpdatedForce = useSelector((state) => state.studyUpdated); @@ -210,10 +269,16 @@ export function StudyContainer({ view, onChangeTab }) { (eventData) => { const updateTypeHeader = eventData.headers[UPDATE_TYPE_HEADER]; const errorMessage = eventData.headers[ERROR_HEADER]; + const currentRootNetwork = eventData.headers['rootNetwork']; + const userId = eventData.headers[USER_HEADER]; if (userId != null && userId !== userName) { return; } + if (currentRootNetwork !== currentRootNetworkRef.current) { + return; + } + if (updateTypeHeader === 'loadflow_failed') { snackError({ headerId: 'LoadFlowError', @@ -283,6 +348,10 @@ export function StudyContainer({ view, onChangeTab }) { const sendAlert = useCallback( (eventData) => { const userId = eventData.headers[USER_HEADER]; + const currentRootNetwork = eventData.headers['rootNetwork']; + if (currentRootNetworkRef.current !== currentRootNetwork && currentRootNetwork) { + return; + } if (userId !== userName) { return; } @@ -437,14 +506,14 @@ export function StudyContainer({ view, onChangeTab }) { (initIndexationStatus) => { console.info(`Loading network modification tree of study '${studyUuid}'...`); - const networkModificationTree = fetchNetworkModificationTree(studyUuid); + const networkModificationTree = fetchNetworkModificationTree(studyUuid, currentRootNetworkUuid); networkModificationTree .then((tree) => { const networkModificationTreeModel = new NetworkModificationTreeModel(); networkModificationTreeModel.setTreeElements(tree); - fetchCaseName(studyUuid) + fetchCaseName(studyUuid, currentRootNetworkUuid) .then((res) => { if (res) { networkModificationTreeModel.setCaseName(res); @@ -486,12 +555,12 @@ export function StudyContainer({ view, onChangeTab }) { .finally(() => console.debug('Network modification tree loading finished')); // Note: studyUuid and dispatch don't change }, - [studyUuid, dispatch, snackError, snackWarning] + [studyUuid, currentRootNetworkUuid, dispatch, snackError, snackWarning] ); const checkStudyIndexation = useCallback(() => { setIsStudyIndexationPending(true); - return fetchStudyIndexationStatus(studyUuid) + return fetchStudyIndexationStatus(studyUuid, currentRootNetworkUuid) .then((status) => { switch (status) { case StudyIndexationStatus.INDEXED: { @@ -505,7 +574,7 @@ export function StudyContainer({ view, onChangeTab }) { } case StudyIndexationStatus.NOT_INDEXED: { dispatch(setStudyIndexationStatus(status)); - reindexAllStudy(studyUuid) + reindexAllStudy(studyUuid, currentRootNetworkUuid) .catch((error) => { // unknown error when trying to reindex study snackError({ @@ -536,11 +605,11 @@ export function StudyContainer({ view, onChangeTab }) { headerId: 'checkstudyIndexationError', }); }); - }, [studyUuid, dispatch, snackError]); + }, [studyUuid, currentRootNetworkUuid, dispatch, snackError]); const checkNetworkExistenceAndRecreateIfNotFound = useCallback( (successCallback) => { - fetchNetworkExistence(studyUuid) + fetchNetworkExistence(studyUuid, currentRootNetworkUuid) .then((response) => { if (response.status === HttpStatusCode.OK) { successCallback && successCallback(); @@ -550,7 +619,7 @@ export function StudyContainer({ view, onChangeTab }) { // response.state === NO_CONTENT // if network is not found, we try to recreate study network from existing case setIsStudyNetworkFound(false); - recreateStudyNetwork(studyUuid) + recreateStudyNetwork(studyUuid, currentRootNetworkUuid) .then(() => { snackWarning({ headerId: 'recreatingNetworkStudy', @@ -584,14 +653,17 @@ export function StudyContainer({ view, onChangeTab }) { ); }); }, - [studyUuid, checkStudyIndexation, loadTree, snackWarning, intlRef] + [studyUuid, currentRootNetworkUuid, checkStudyIndexation, loadTree, snackWarning, intlRef] ); useEffect(() => { - if (studyUuid && !isStudyNetworkFound) { + if ( + (studyUuid && currentRootNetworkUuid && !isStudyNetworkFound) || + (currentRootNetworkRef.current && currentRootNetworkRef.current !== currentRootNetworkUuid) + ) { checkNetworkExistenceAndRecreateIfNotFound(); } - }, [isStudyNetworkFound, checkNetworkExistenceAndRecreateIfNotFound, studyUuid]); + }, [isStudyNetworkFound, currentRootNetworkUuid, checkNetworkExistenceAndRecreateIfNotFound, studyUuid]); // study_network_recreation_done notification // checking another time if we can find network, if we do, we display a snackbar info @@ -626,6 +698,9 @@ export function StudyContainer({ view, onChangeTab }) { } let previousCurrentNode = currentNodeRef.current; currentNodeRef.current = currentNode; + + let previousCurrentRootNetwork = currentRootNetworkRef.current; + currentRootNetworkRef.current = currentRootNetworkUuid; // if only node renaming, do not reload network if (isNodeRenamed(previousCurrentNode, currentNode)) { return; @@ -635,15 +710,23 @@ export function StudyContainer({ view, onChangeTab }) { } // A modification has been added to the currentNode and this one has been built incrementally. // No need to load the network because reloadImpactedSubstationsEquipments will be executed in the notification useEffect. - if (isSameNode(previousCurrentNode, currentNode) && isNodeBuilt(previousCurrentNode)) { + if ( + previousCurrentRootNetwork === currentRootNetworkUuid && + isSameNode(previousCurrentNode, currentNode) && + isNodeBuilt(previousCurrentNode) + ) { return; } dispatch(resetEquipments()); - }, [currentNode, wsConnected, dispatch]); + }, [currentNode, currentRootNetworkUuid, wsConnected, dispatch]); useEffect(() => { if (studyUpdatedForce.eventData.headers) { - if (studyUpdatedForce.eventData.headers[UPDATE_TYPE_HEADER] === 'loadflowResult') { + const currentRootNetwork = studyUpdatedForce.eventData.headers['rootNetwork']; + if ( + studyUpdatedForce.eventData.headers[UPDATE_TYPE_HEADER] === 'loadflowResult' && + currentRootNetwork === currentRootNetworkRef.current + ) { dispatch(resetEquipmentsPostLoadflow()); } } @@ -740,6 +823,7 @@ export function StudyContainer({ view, onChangeTab }) { { +const StudyPane = ({ studyUuid, currentRootNetworkUuid, currentNode, setErrorMessage, ...props }) => { const [tableEquipment, setTableEquipment] = useState({ id: null, type: null, @@ -82,6 +82,7 @@ const StudyPane = ({ studyUuid, currentNode, setErrorMessage, ...props }) => { { { /> - { { const currentNode = useSelector((state) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state) => state.currentRootNetwork); const [equipmentInfo, setEquipmentInfo] = useState(null); const intl = useIntl(); const [localAnchorEl, setLocalAnchorEl] = useState(null); const [localAnchorPosition, setLocalAnchorPosition] = useState(null); const getNetworkElementInfos = useCallback( - (equipmentId, equipmentType, currentNodeId, studyUuid) => { + (equipmentId, equipmentType, currentRootNetworkUuid, currentNodeId, studyUuid) => { fetchNetworkElementInfos( studyUuid, currentNodeId, + currentRootNetworkUuid, equipmentType, EQUIPMENT_INFOS_TYPES.TOOLTIP.type, equipmentId, @@ -67,11 +69,11 @@ const EquipmentPopover = ({ studyUuid, anchorEl, anchorPosition, equipmentId, eq useEffect(() => { if (equipmentId && equipmentId !== '') { - debouncedNetworkElementInfos(equipmentId, equipmentType, currentNode.id, studyUuid); + debouncedNetworkElementInfos(equipmentId, equipmentType, currentRootNetworkUuid, currentNode.id, studyUuid); } else { setEquipmentInfo(null); } - }, [debouncedNetworkElementInfos, equipmentId, equipmentType, currentNode.id, studyUuid]); + }, [debouncedNetworkElementInfos, equipmentId, equipmentType, currentNode.id, studyUuid, currentRootNetworkUuid]); const handlePopoverClose = () => { setEquipmentInfo(null); diff --git a/src/components/top-bar-equipment-seach-dialog/custom-suffix-renderer.tsx b/src/components/top-bar-equipment-seach-dialog/custom-suffix-renderer.tsx index 4106782cb7..258f7fb209 100644 --- a/src/components/top-bar-equipment-seach-dialog/custom-suffix-renderer.tsx +++ b/src/components/top-bar-equipment-seach-dialog/custom-suffix-renderer.tsx @@ -28,13 +28,14 @@ export const CustomSuffixRenderer: FunctionComponent const dispatch = useDispatch(); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const networkAreaDiagramNbVoltageLevels = useSelector((state: AppState) => state.networkAreaDiagramNbVoltageLevels); const networkAreaDiagramDepth = useSelector((state: AppState) => state.networkAreaDiagramDepth); const centerOnSubstationCB = useCallback( (e: ReactMouseEvent) => { e.stopPropagation(); - if (!studyUuid || !currentNode) { + if (!studyUuid || !currentNode || !currentRootNetworkUuid) { return; } let substationIdPromise; @@ -42,7 +43,12 @@ export const CustomSuffixRenderer: FunctionComponent if (element.type === EQUIPMENT_TYPES.SUBSTATION) { substationIdPromise = Promise.resolve(element.id); } else { - substationIdPromise = fetchSubstationIdForVoltageLevel(studyUuid, currentNode.id, element.id); + substationIdPromise = fetchSubstationIdForVoltageLevel( + studyUuid, + currentNode.id, + currentRootNetworkUuid, + element.id + ); } substationIdPromise.then((substationId) => { dispatch(centerOnSubstation(substationId)); @@ -50,7 +56,7 @@ export const CustomSuffixRenderer: FunctionComponent e.stopPropagation(); }); }, - [studyUuid, currentNode, element.type, element.id, dispatch, onClose] + [studyUuid, currentNode, currentRootNetworkUuid, element.type, element.id, dispatch, onClose] ); const openNetworkAreaDiagramCB = useCallback( diff --git a/src/components/top-bar-equipment-seach-dialog/top-bar-equipment-search-dialog.tsx b/src/components/top-bar-equipment-seach-dialog/top-bar-equipment-search-dialog.tsx index 77b163c459..0151cde13f 100644 --- a/src/components/top-bar-equipment-seach-dialog/top-bar-equipment-search-dialog.tsx +++ b/src/components/top-bar-equipment-seach-dialog/top-bar-equipment-search-dialog.tsx @@ -42,6 +42,7 @@ export const TopBarEquipmentSearchDialog: FunctionComponent state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetwork); const [equipmentTypeFilter, setEquipmentTypeFilter] = useState(null); const { searchTerm, updateSearchTerm, equipmentsFound, isLoading } = useTopBarSearchMatchingEquipment({ @@ -49,6 +50,9 @@ export const TopBarEquipmentSearchDialog: FunctionComponent { - const { studyUuid, nodeUuid, inUpstreamBuiltParentNode, equipmentType } = props; + const { studyUuid, nodeUuid, currentRootNetworkUuid, inUpstreamBuiltParentNode, equipmentType } = props; const { getUseNameParameterKey, getNameOrId } = useNameOrId(); @@ -28,12 +29,13 @@ export const useSearchMatchingEquipments = (props: UseSearchMatchingEquipmentsPr searchEquipmentsInfos( studyUuid, nodeUuid, + currentRootNetworkUuid, newSearchTerm, getUseNameParameterKey, inUpstreamBuiltParentNode, equipmentType ), - [equipmentType, getUseNameParameterKey, inUpstreamBuiltParentNode, nodeUuid, studyUuid] + [equipmentType, getUseNameParameterKey, inUpstreamBuiltParentNode, nodeUuid, studyUuid, currentRootNetworkUuid] ); const { elementsFound, isLoading, searchTerm, updateSearchTerm } = useElementSearch({ diff --git a/src/components/top-bar-equipment-seach-dialog/use-top-bar-search-matching-equipments.tsx b/src/components/top-bar-equipment-seach-dialog/use-top-bar-search-matching-equipments.tsx index 3475adf117..1aec61fe8b 100644 --- a/src/components/top-bar-equipment-seach-dialog/use-top-bar-search-matching-equipments.tsx +++ b/src/components/top-bar-equipment-seach-dialog/use-top-bar-search-matching-equipments.tsx @@ -13,14 +13,16 @@ import { EquipmentType } from '@gridsuite/commons-ui'; interface UseTopBarSearchMatchingEquipmentProps { studyUuid: UUID; nodeUuid: UUID; + currentRootNetworkUuid: UUID; equipmentType?: EquipmentType; } export const useTopBarSearchMatchingEquipment = (props: UseTopBarSearchMatchingEquipmentProps) => { - const { nodeUuid, studyUuid, equipmentType } = props; + const { nodeUuid, studyUuid, currentRootNetworkUuid, equipmentType } = props; const { equipmentsFound, searchTerm, ...otherStates } = useSearchMatchingEquipments({ studyUuid: studyUuid, nodeUuid: nodeUuid, + currentRootNetworkUuid: currentRootNetworkUuid, equipmentType: equipmentType ?? undefined, }); diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts index 8bf2123b68..067a72f02f 100644 --- a/src/components/utils/field-constants.ts +++ b/src/components/utils/field-constants.ts @@ -19,6 +19,8 @@ export const DESTINATION_FOLDER = 'destinationFolder'; export const FOLDER_NAME = 'folderName'; export const FOLDER_ID = 'folderId'; export const DESCRIPTION = 'description'; +export const CASE_NAME = 'caseName'; +export const CASE_ID = 'caseId'; export const SUBSTATION_ID = 'substationId'; export const NOMINAL_VOLTAGE = 'nominalVoltage'; export const NOMINAL_V = 'nominalV'; diff --git a/src/components/voltage-init-result-tab.jsx b/src/components/voltage-init-result-tab.jsx index 9d2fdd288f..bfa4dc2491 100644 --- a/src/components/voltage-init-result-tab.jsx +++ b/src/components/voltage-init-result-tab.jsx @@ -15,12 +15,13 @@ import RunningStatus from './utils/running-status'; const voltageInitResultInvalidations = ['voltageInitResult']; -export const VoltageInitResultTab = ({ studyUuid, nodeUuid }) => { +export const VoltageInitResultTab = ({ studyUuid, nodeUuid, currentRootNetworkUuid }) => { const voltageInitStatus = useSelector((state) => state.computingStatus[ComputingType.VOLTAGE_INITIALIZATION]); const [voltageInitResult, isWaiting] = useNodeData( studyUuid, nodeUuid, + currentRootNetworkUuid, fetchVoltageInitResult, voltageInitResultInvalidations ); diff --git a/src/hooks/use-get-study-impacts.ts b/src/hooks/use-get-study-impacts.ts index f6c31ad8da..090f7d3050 100644 --- a/src/hooks/use-get-study-impacts.ts +++ b/src/hooks/use-get-study-impacts.ts @@ -40,6 +40,7 @@ export const useGetStudyImpacts = (): StudyImpactsWithReset => { useEffect(() => { if (studyUpdatedForce.type === NotificationType.STUDY) { + console.log('TEST ====== ', studyUpdatedForce); const { impactedSubstationsIds: substationsIds, deletedEquipments, diff --git a/src/hooks/use-report-fetcher.tsx b/src/hooks/use-report-fetcher.tsx index 0c48ef6c70..99ad7222e0 100644 --- a/src/hooks/use-report-fetcher.tsx +++ b/src/hooks/use-report-fetcher.tsx @@ -81,6 +81,7 @@ export const useReportFetcher = ( const [isLoading, setIsLoading] = useState(false); const studyUuid = useSelector((state: AppState) => state.studyUuid); const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const currentRootNetwork = useSelector((state: AppState) => state.currentRootNetwork); const treeModel = useSelector((state: AppState) => state.networkModificationTreeModel); const { snackError } = useSnackMessage(); @@ -116,11 +117,12 @@ export const useReportFetcher = ( const fetchRawParentReport = useCallback( (nodeOnlyReport?: boolean) => { - if (currentNode !== null && studyUuid) { + if (currentNode !== null && studyUuid && currentRootNetwork) { return fetch(() => fetchParentNodesReport( studyUuid, currentNode.id, + currentRootNetwork, nodeOnlyReport ?? true, getContainerDefaultSeverityList(), computingAndNetworkModificationType @@ -129,27 +131,43 @@ export const useReportFetcher = ( } return Promise.resolve(undefined); }, - [currentNode, fetch, computingAndNetworkModificationType, studyUuid] + [currentNode, currentRootNetwork, fetch, computingAndNetworkModificationType, studyUuid] ); const fetchReportLogs = useCallback( (reportId: string, severityList: string[], reportType: ReportType, messageFilter: string) => { - if (!studyUuid) { + if (!studyUuid || !currentRootNetwork) { return; } let fetchPromise: (severityList: string[], reportId: string) => Promise; if (reportType === ReportType.GLOBAL) { fetchPromise = (severityList: string[]) => - fetchNodeReportLogs(studyUuid, currentNode!.id, null, severityList, messageFilter, true); + fetchNodeReportLogs( + studyUuid, + currentNode!.id, + currentRootNetwork, + null, + severityList, + messageFilter, + true + ); } else { fetchPromise = (severityList: string[], reportId: string) => - fetchNodeReportLogs(studyUuid, currentNode!.id, reportId, severityList, messageFilter, false); + fetchNodeReportLogs( + studyUuid, + currentNode!.id, + currentRootNetwork, + reportId, + severityList, + messageFilter, + false + ); } return fetchPromise(severityList, reportId).then((r) => { return mapReportLogs(prettifyReportLogMessage(r, nodesNames)); }); }, - [currentNode, studyUuid, nodesNames] + [currentNode, currentRootNetwork, studyUuid, nodesNames] ); const fetchReportSeverities = useCallback( diff --git a/src/hooks/use-voltage-levels-list-infos.ts b/src/hooks/use-voltage-levels-list-infos.ts index ee4d2f4362..0a4fd827b7 100644 --- a/src/hooks/use-voltage-levels-list-infos.ts +++ b/src/hooks/use-voltage-levels-list-infos.ts @@ -9,16 +9,16 @@ import { UUID } from 'crypto'; import { useEffect, useState } from 'react'; import { fetchVoltageLevelsListInfos } from '../services/study/network'; -export default function useVoltageLevelsListInfos(studyUuid: UUID, nodeUuid: UUID) { +export default function useVoltageLevelsListInfos(studyUuid: UUID, nodeUuid: UUID, currentRootNetworkUuid: UUID) { const [voltageLevelsListInfos, setVoltageLevelsListInfos] = useState([]); useEffect(() => { - if (studyUuid && nodeUuid) { - fetchVoltageLevelsListInfos(studyUuid, nodeUuid).then((values) => { + if (studyUuid && nodeUuid && currentRootNetworkUuid) { + fetchVoltageLevelsListInfos(studyUuid, nodeUuid, currentRootNetworkUuid).then((values) => { setVoltageLevelsListInfos( values.sort((a: { id: string }, b: { id: string }) => a.id.localeCompare(b.id)) ); }); } - }, [studyUuid, nodeUuid]); + }, [studyUuid, nodeUuid, currentRootNetworkUuid]); return voltageLevelsListInfos; } diff --git a/src/redux/actions.ts b/src/redux/actions.ts index 58ed64eee1..33e88f423f 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -609,6 +609,17 @@ export function setCurrentTreeNode(currentTreeNode: CurrentTreeNode): CurrentTre }; } +export const CURRENT_ROOT_NETWORK = 'CURRENT_ROOT_NETWORK'; +export type CurrentRootNetworkAction = Readonly> & { + currentRootNetwork: UUID; +}; +export function setCurrentRootNetwork(currentRootNetwork: UUID): CurrentRootNetworkAction { + return { + type: CURRENT_ROOT_NETWORK, + currentRootNetwork: currentRootNetwork, + }; +} + export const NODE_SELECTION_FOR_COPY = 'NODE_SELECTION_FOR_COPY'; export type NodeSelectionForCopyAction = Readonly> & { nodeSelectionForCopy: NonNullable; diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 18ca908f9b..63468cedfc 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -184,6 +184,8 @@ import { UseNameAction, STATEESTIMATION_RESULT_FILTER, StateEstimationResultFilterAction, + CURRENT_ROOT_NETWORK, + CurrentRootNetworkAction, UPDATE_NETWORK_VISUALIZATION_PARAMETERS, UpdateNetworkVisualizationParametersAction, } from './actions'; @@ -314,6 +316,7 @@ export interface OneBusShortCircuitAnalysisDiagram { export interface StudyUpdatedEventDataHeader { studyUuid: UUID; parentNode: UUID; + rootNetwork: UUID; timestamp: number; updateType?: string; node?: UUID; @@ -443,6 +446,7 @@ export interface AppState extends CommonStoreState { studyUpdated: StudyUpdated; studyUuid: UUID | null; currentTreeNode: CurrentTreeNode | null; + currentRootNetwork: UUID | null; computingStatus: ComputingStatus; lastCompletedComputation: ComputingType | null; computationStarting: boolean; @@ -606,6 +610,7 @@ const initialTablesState: TablesState = { const initialState: AppState = { studyUuid: null, currentTreeNode: null, + currentRootNetwork: null, nodeSelectionForCopy: { sourceStudyUuid: null, nodeId: null, @@ -1144,6 +1149,11 @@ export const reducer = createReducer(initialState, (builder) => { state.reloadMap = true; }); + builder.addCase(CURRENT_ROOT_NETWORK, (state, action: CurrentRootNetworkAction) => { + state.currentRootNetwork = action.currentRootNetwork; + state.reloadMap = true; + }); + builder.addCase(NODE_SELECTION_FOR_COPY, (state, action: NodeSelectionForCopyAction) => { const nodeSelectionForCopy = action.nodeSelectionForCopy; if ( diff --git a/src/services/dynamic-simulation.ts b/src/services/dynamic-simulation.ts index 7998bd9fb7..e7a2a398a1 100644 --- a/src/services/dynamic-simulation.ts +++ b/src/services/dynamic-simulation.ts @@ -7,7 +7,7 @@ import { Event } from '../components/dialogs/dynamicsimulation/event/types/event.type'; import { backendFetch, backendFetchJson, getRequestParamFromList } from './utils'; -import { getStudyUrlWithNodeUuid } from './study'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './study'; import { UUID } from 'crypto'; import { TimelineEvent, @@ -30,55 +30,83 @@ export function fetchDynamicSimulationProviders() { export function fetchDynamicSimulationTimeSeriesMetadata( studyUuid: UUID, - currentNodeUuid: UUID + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID ): Promise { console.info( - `Fetching dynamic simulation time series's metadata on '${studyUuid}' and node '${currentNodeUuid}' ...` + `Fetching dynamic simulation time series's metadata on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` ); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/dynamic-simulation/result/timeseries/metadata'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/dynamic-simulation/result/timeseries/metadata'; console.debug(url); return backendFetchJson(url); } export function fetchDynamicSimulationResultTimeline( studyUuid: UUID, - currentNodeUuid: UUID + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID ): Promise { - console.info(`Fetching dynamic simulation timeline result on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/dynamic-simulation/result/timeline'; + console.info( + `Fetching dynamic simulation timeline result on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/dynamic-simulation/result/timeline'; console.debug(url); return backendFetchJson(url); } // --- Event API - BEGIN - -export function fetchDynamicSimulationEvents(studyUuid: UUID, nodeUuid: UUID): Promise { +//TODO: should not be linked to rootnetworkUUID +export function fetchDynamicSimulationEvents( + studyUuid: UUID, + nodeUuid: UUID, + currentRootNetworkUuid: UUID +): Promise { console.info(`Fetching dynamic simulation events on '${studyUuid}' and node '${nodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/dynamic-simulation/events'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + '/dynamic-simulation/events'; console.debug(url); return backendFetchJson(url); } -export function fetchDynamicSimulationEvent(studyUuid: UUID, nodeUuid: UUID, equipmentId: string): Promise { +export function fetchDynamicSimulationEvent( + studyUuid: UUID, + nodeUuid: UUID, + currentRootNetworkUuid: UUID, + equipmentId: string +): Promise { console.info( `Fetching dynamic simulation event with '${equipmentId}' on '${studyUuid}' and node '${nodeUuid}' ...` ); - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + `/dynamic-simulation/events?equipmentId=${equipmentId}`; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + `/dynamic-simulation/events?equipmentId=${equipmentId}`; console.debug(url); return backendFetchJson(url); } -export function saveDynamicSimulationEvent(studyUuid: UUID, nodeUuid: UUID, event: Event) { +export function saveDynamicSimulationEvent( + studyUuid: UUID, + nodeUuid: UUID, + currentRootNetworkUuid: UUID, + event: Event +) { console.info(`Saving dynamic simulation event on '${studyUuid}' and node '${nodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + `/dynamic-simulation/events`; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + `/dynamic-simulation/events`; console.debug(url); return backendFetch(url, { @@ -91,7 +119,12 @@ export function saveDynamicSimulationEvent(studyUuid: UUID, nodeUuid: UUID, even }); } -export function deleteDynamicSimulationEvents(studyUuid: UUID, nodeUuid: UUID, events: Event[]) { +export function deleteDynamicSimulationEvents( + studyUuid: UUID, + nodeUuid: UUID, + currentRootNetworkUuid: UUID, + events: Event[] +) { console.info(`Delete dynamic simulation events on '${studyUuid}' and node '${nodeUuid}' ...`); const eventIdsParams = getRequestParamFromList( @@ -99,7 +132,9 @@ export function deleteDynamicSimulationEvents(studyUuid: UUID, nodeUuid: UUID, e 'eventUuids' ); - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + `/dynamic-simulation/events?${eventIdsParams}`; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + `/dynamic-simulation/events?${eventIdsParams}`; console.debug(url); diff --git a/src/services/network-conversion.ts b/src/services/network-conversion.ts index ffa815c369..4f497df213 100644 --- a/src/services/network-conversion.ts +++ b/src/services/network-conversion.ts @@ -23,7 +23,7 @@ export interface GetCaseImportParametersReturn { parameters: CaseImportParameters[]; } -export function getCaseImportParameters(caseUuid: UUID): Promise { +export function getCaseImportParameters(caseUuid: UUID | undefined): Promise { console.info(`get import parameters for case '${caseUuid}' ...`); const getExportFormatsUrl = PREFIX_NETWORK_CONVERSION_SERVER_QUERIES + '/v1/cases/' + caseUuid + '/import-parameters'; diff --git a/src/services/root-network.ts b/src/services/root-network.ts new file mode 100644 index 0000000000..4d31ced499 --- /dev/null +++ b/src/services/root-network.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2024, 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 { backendFetch, backendFetchJson } from './utils'; +import { UUID } from 'crypto'; + +export const PREFIX_STUDY_QUERIES = import.meta.env.VITE_API_GATEWAY + '/study'; + +export function fetchRootNetworks(studyUuid: UUID) { + console.info('Fetching root network for studyUuid : ', studyUuid); + const urlSearchParams = new URLSearchParams(); + const rootNetworkssGetUrl = + `${PREFIX_STUDY_QUERIES}/v1/studies/${encodeURIComponent(studyUuid)}/root-networks` + + urlSearchParams.toString(); + + console.debug(rootNetworkssGetUrl); + return backendFetchJson(rootNetworkssGetUrl); +} + +export const createRootNetwork = ( + caseUuid: UUID | undefined, + caseFormat: string, + studyUuid: UUID | null, + importParameters: Record +) => { + if (!studyUuid || !caseUuid) { + throw new Error('studyUuid and caseUuid are required parameters.'); + } + + const createRootNetworkUrl = + PREFIX_STUDY_QUERIES + + `/v1/studies/${encodeURIComponent(studyUuid)}/root-networks?` + + `caseUuid=${encodeURIComponent(caseUuid)}&` + + `caseFormat=${encodeURIComponent(caseFormat)}`; + + console.debug(createRootNetworkUrl); + return backendFetch(createRootNetworkUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: importParameters ? JSON.stringify(importParameters) : '', + }); +}; + +export function deleteRootNetworks(studyUuid: UUID, rootNetworkUuids: UUID[]) { + const rootNetworkDeleteUrl = `${PREFIX_STUDY_QUERIES}/v1/studies/${encodeURIComponent(studyUuid)}/root-networks`; + + console.debug(rootNetworkDeleteUrl); + return backendFetch(rootNetworkDeleteUrl, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(rootNetworkUuids), + }); +} diff --git a/src/services/study/dynamic-simulation.ts b/src/services/study/dynamic-simulation.ts index 7ec438166d..177d06dd87 100644 --- a/src/services/study/dynamic-simulation.ts +++ b/src/services/study/dynamic-simulation.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid, PREFIX_STUDY_QUERIES } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES } from './index'; import { backendFetch, backendFetchJson, backendFetchText, getRequestParamFromList } from '../utils'; import { UUID } from 'crypto'; @@ -17,11 +17,20 @@ export function getDynamicMappings(studyUuid: UUID) { return backendFetchJson(url); } -export function startDynamicSimulation(studyUuid: UUID, currentNodeUuid: UUID, dynamicSimulationConfiguration?: any) { - console.info(`Running dynamic simulation on '${studyUuid}' and node '${currentNodeUuid}' ...`); - - const startDynamicSimulationUrl = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/dynamic-simulation/run`; - +export function startDynamicSimulation( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + dynamicSimulationConfiguration?: any +) { + console.info( + `Running dynamic simulation on '${studyUuid}' on root nerwork '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const startDynamicSimulationUrl = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/dynamic-simulation/run`; // add body const body = JSON.stringify(dynamicSimulationConfiguration ?? {}); @@ -37,16 +46,24 @@ export function startDynamicSimulation(studyUuid: UUID, currentNodeUuid: UUID, d }); } -export function stopDynamicSimulation(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping dynamic simulation on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const stopDynamicSimulationUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/dynamic-simulation/stop'; +export function stopDynamicSimulation(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping dynamic simulation on '${studyUuid}' for root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const stopDynamicSimulationUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/dynamic-simulation/stop'; console.debug(stopDynamicSimulationUrl); return backendFetch(stopDynamicSimulationUrl, { method: 'put' }); } -export function fetchDynamicSimulationStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching dynamic simulation status on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/dynamic-simulation/status'; +export function fetchDynamicSimulationStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching dynamic simulation status on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/dynamic-simulation/status'; console.debug(url); return backendFetchJson(url); } @@ -54,6 +71,7 @@ export function fetchDynamicSimulationStatus(studyUuid: UUID, currentNodeUuid: U export function fetchDynamicSimulationResultTimeSeries( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, timeSeriesNames: string[] ) { console.info(`Fetching dynamic simulation time series result on '${studyUuid}' and node '${currentNodeUuid}' ...`); @@ -62,19 +80,22 @@ export function fetchDynamicSimulationResultTimeSeries( const timeSeriesParams = getRequestParamFromList(timeSeriesNames, 'timeSeriesNames'); const urlSearchParams = new URLSearchParams(timeSeriesParams); - const url = `${getStudyUrlWithNodeUuid( + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, - currentNodeUuid + currentNodeUuid, + currentRootNetworkUuid )}/dynamic-simulation/result/timeseries?${urlSearchParams}`; console.debug(url); return backendFetchJson(url); } -export function fetchDynamicSimulationModels(studyUuid: UUID | null, nodeUuid: UUID) { - console.info(`Fetching dynamic simulation models on '${studyUuid}' and node '${nodeUuid}' ...`); - - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/dynamic-simulation/models'; +export function fetchDynamicSimulationModels(studyUuid: UUID | null, nodeUuid: UUID, rootNetworkUuid: UUID) { + console.info( + `Fetching dynamic simulation models on '${studyUuid}' on root network '${rootNetworkUuid}' and node '${nodeUuid}' ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, rootNetworkUuid) + '/dynamic-simulation/models'; console.debug(url); return backendFetchJson(url); } diff --git a/src/services/study/filter.ts b/src/services/study/filter.ts index 9718e4be24..4694948c34 100644 --- a/src/services/study/filter.ts +++ b/src/services/study/filter.ts @@ -7,7 +7,7 @@ import { backendFetchJson } from '../utils'; import { UUID } from 'crypto'; -import { getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { RuleGroupTypeExport } from '../../components/dialogs/filter/expert/expert-filter.type'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; @@ -28,12 +28,16 @@ export interface IdentifiableAttributes { export function evaluateJsonFilter( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, filter: ExpertFilter // at moment only ExpertFilter but in futur may add others filter types to compose a union type ): Promise { - console.info(`Get matched elements of study '${studyUuid}' and node '${currentNodeUuid}' ...`); + console.info( + `Get matched elements of study '${studyUuid}' with a root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); const evaluateFilterUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/filters/evaluate?inUpstreamBuiltParentNode=true'; + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/filters/evaluate?inUpstreamBuiltParentNode=true'; console.debug(evaluateFilterUrl); return backendFetchJson(evaluateFilterUrl, { method: 'post', diff --git a/src/services/study/geo-data.ts b/src/services/study/geo-data.ts index 05a2032b58..0c07c072dd 100644 --- a/src/services/study/geo-data.ts +++ b/src/services/study/geo-data.ts @@ -6,31 +6,46 @@ */ import { backendFetchJson, getQueryParamsList } from '../utils'; -import { getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { UUID } from 'crypto'; -export function fetchSubstationPositions(studyUuid: UUID, currentNodeUuid?: UUID, substationsIds?: string[]) { +export function fetchSubstationPositions( + studyUuid: UUID, + currentNodeUuid?: UUID, + currentRootNetworkUuid?: UUID, + substationsIds?: string[] +) { console.info( - `Fetching substation positions of study '${studyUuid}' and node '${currentNodeUuid}' with ids '${substationsIds}'...` + `Fetching substation positions of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' with ids '${substationsIds}'...` ); const paramsList = substationsIds && substationsIds.length > 0 ? '?' + getQueryParamsList(substationsIds, 'substationId') : ''; const fetchSubstationPositionsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/geo-data/substations' + paramsList; + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/geo-data/substations' + + paramsList; console.debug(fetchSubstationPositionsUrl); return backendFetchJson(fetchSubstationPositionsUrl); } -export function fetchLinePositions(studyUuid: UUID, currentNodeUuid: UUID | undefined, linesIds?: string[]) { +export function fetchLinePositions( + studyUuid: UUID, + currentNodeUuid: UUID | undefined, + currentRootNetworkUuid: UUID | undefined, + linesIds?: string[] +) { console.info( - `Fetching line positions of study '${studyUuid}' and node '${currentNodeUuid}' with ids '${linesIds}'...` + `Fetching line positions of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' with ids '${linesIds}'...` ); const paramsList = linesIds && linesIds.length > 0 ? '?' + getQueryParamsList(linesIds, 'lineId') : ''; - const fetchLinePositionsUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/geo-data/lines' + paramsList; + const fetchLinePositionsUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/geo-data/lines' + + paramsList; console.debug(fetchLinePositionsUrl); return backendFetchJson(fetchLinePositionsUrl); diff --git a/src/services/study/index.ts b/src/services/study/index.ts index 06bf8b943c..ba80e168fb 100644 --- a/src/services/study/index.ts +++ b/src/services/study/index.ts @@ -26,6 +26,15 @@ export const PREFIX_STUDY_QUERIES = import.meta.env.VITE_API_GATEWAY + '/study'; export const getStudyUrl = (studyUuid: UUID | null) => `${PREFIX_STUDY_QUERIES}/v1/studies/${safeEncodeURIComponent(studyUuid)}`; +export const getStudyUrlWithNodeUuidAndRootNetworkUuid = ( + studyUuid: string | null | undefined, + nodeUuid: string | undefined, + rootNetworkUuid: string | undefined | null +) => + `${PREFIX_STUDY_QUERIES}/v1/studies/${safeEncodeURIComponent(studyUuid)}/root-networks/${safeEncodeURIComponent( + rootNetworkUuid + )}/nodes/${safeEncodeURIComponent(nodeUuid)}`; + export const getStudyUrlWithNodeUuid = (studyUuid: string | null | undefined, nodeUuid: string | undefined) => `${PREFIX_STUDY_QUERIES}/v1/studies/${safeEncodeURIComponent(studyUuid)}/nodes/${safeEncodeURIComponent(nodeUuid)}`; @@ -46,13 +55,16 @@ export const fetchStudyExists = (studyUuid: UUID) => { export function getNetworkAreaDiagramUrl( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, voltageLevelsIds: UUID[], depth: number, withGeoData: boolean ) { - console.info(`Getting url of network area diagram of study '${studyUuid}' and node '${currentNodeUuid}'...`); + console.info( + `Getting url of network area diagram of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}'...` + ); return ( - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-area-diagram?' + new URLSearchParams({ depth: depth.toString(), @@ -66,6 +78,7 @@ export function getNetworkAreaDiagramUrl( export function fetchParentNodesReport( studyUuid: UUID | null, nodeUuid: UUID, + currentRootNetworkUuid: UUID, nodeOnlyReport: boolean, severityFilterList: string[], reportType: keyof typeof COMPUTING_AND_NETWORK_MODIFICATION_TYPE @@ -82,7 +95,7 @@ export function fetchParentNodesReport( ); let url = - getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + '/parent-nodes-report?nodeOnlyReport=' + (nodeOnlyReport ? 'true' : 'false') + '&reportType=' + @@ -97,6 +110,7 @@ export function fetchParentNodesReport( export function fetchNodeReportLogs( studyUuid: UUID | null, nodeUuid: UUID, + currentRootNetworkUuid: UUID, reportId: string | null, severityFilterList: string[], messageFilter: string, @@ -104,9 +118,13 @@ export function fetchNodeReportLogs( ) { let url; if (isGlobalLogs) { - url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/report/logs?'; + url = getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + '/report/logs?'; } else { - url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/report/' + reportId + '/logs?'; + url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + '/report/' + + reportId + + '/logs?'; } if (severityFilterList?.length) { url += '&' + getRequestParamFromList(severityFilterList, 'severityLevels'); @@ -136,6 +154,7 @@ export function fetchSvg(svgUrl: string) { export function searchEquipmentsInfos( studyUuid: UUID, nodeUuid: UUID, + currentRootNetworkUuid: UUID, searchTerm: string, getUseNameParameterKey: () => 'name' | 'id', inUpstreamBuiltParentNode?: boolean, @@ -152,11 +171,18 @@ export function searchEquipmentsInfos( urlSearchParams.append('equipmentType', equipmentType); } return backendFetchJson( - getStudyUrl(studyUuid) + '/nodes/' + encodeURIComponent(nodeUuid) + '/search?' + urlSearchParams.toString() + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + '/search?' + + urlSearchParams.toString() ); } -export function fetchContingencyCount(studyUuid: UUID, currentNodeUuid: UUID, contingencyListNames: string[]) { +export function fetchContingencyCount( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + contingencyListNames: string[] +) { console.info( `Fetching contingency count for ${contingencyListNames} on '${studyUuid}' and node '${currentNodeUuid}'...` ); @@ -164,7 +190,10 @@ export function fetchContingencyCount(studyUuid: UUID, currentNodeUuid: UUID, co const contingencyListNamesParams = getRequestParamFromList(contingencyListNames, 'contingencyListName'); const urlSearchParams = new URLSearchParams(contingencyListNamesParams); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/contingency-count?' + urlSearchParams; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/contingency-count?' + + urlSearchParams; console.debug(url); return backendFetchJson(url); @@ -213,23 +242,33 @@ export function getAvailableComponentLibraries(): Promise { return backendFetchJson(getAvailableComponentLibrariesUrl); } -export function unbuildNode(studyUuid: UUID, currentNodeUuid: UUID) { +export function unbuildNode(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info('Unbuild node ' + currentNodeUuid + ' of study ' + studyUuid + ' ...'); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/unbuild'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/unbuild'; console.debug(url); return backendFetchText(url, { method: 'post' }); } -export function buildNode(studyUuid: UUID, currentNodeUuid: UUID) { - console.info('Build node ' + currentNodeUuid + ' of study ' + studyUuid + ' ...'); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/build'; +export function buildNode(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + 'Build node ' + + currentNodeUuid + + ' on root network ' + + currentRootNetworkUuid + + ' of study ' + + studyUuid + + ' ...' + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/build'; console.debug(url); return backendFetchText(url, { method: 'post' }); } -export function fetchCaseName(studyUuid: UUID) { +export function fetchCaseName(studyUuid: UUID, rootNetworkUuid: UUID) { console.info('Fetching case name'); - const url = getStudyUrl(studyUuid) + '/case/name'; + const url = getStudyUrl(studyUuid) + '/root-networks/' + encodeURIComponent(rootNetworkUuid) + '/case/name'; console.debug(url); return backendFetchText(url); @@ -270,13 +309,15 @@ export function getServersInfos() { export function fetchAvailableFilterEnumValues( studyUuid: UUID, nodeUuid: UUID, + currentRootNetworkUuid: UUID, computingType: ComputingType, filterEnum: string ) { console.info('fetch available filter values'); - const url = `${getStudyUrlWithNodeUuid( + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, - nodeUuid + nodeUuid, + currentRootNetworkUuid )}/computation/result/enum-values?computingType=${encodeURIComponent(computingType)}&enumName=${encodeURIComponent( filterEnum )}`; diff --git a/src/services/study/loadflow.ts b/src/services/study/loadflow.ts index 08f6572a4e..1c39b0c86b 100644 --- a/src/services/study/loadflow.ts +++ b/src/services/study/loadflow.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid, PREFIX_STUDY_QUERIES } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES } from './index'; import { backendFetch, backendFetchJson, backendFetchText } from '../utils'; import { UUID } from 'crypto'; import { FilterSelectorType } from 'components/custom-aggrid/custom-aggrid-header.type'; @@ -60,33 +60,58 @@ export function setLoadFlowProvider(studyUuid: UUID, newProvider: string) { }); } -export function startLoadFlow(studyUuid: UUID, currentNodeUuid: UUID, limitReduction: number) { +export function startLoadFlow( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + limitReduction: number +) { console.info( - 'Running loadflow on ' + studyUuid + ' and node ' + currentNodeUuid + ' with limit reduction ' + limitReduction + 'Running loadflow on ' + + studyUuid + + ' on root network ' + + currentRootNetworkUuid + + ' and node ' + + currentNodeUuid + + ' with limit reduction ' + + limitReduction ); const startLoadFlowUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/loadflow/run?limitReduction=' + limitReduction.toString(); console.debug(startLoadFlowUrl); return backendFetch(startLoadFlowUrl, { method: 'put' }); } -export function stopLoadFlow(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping loadFlow on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const stopLoadFlowUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/loadflow/stop'; +export function stopLoadFlow(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping loadFlow on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const stopLoadFlowUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/loadflow/stop'; console.debug(stopLoadFlowUrl); return backendFetch(stopLoadFlowUrl, { method: 'put' }); } -export function fetchLoadFlowStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching loadFlow status on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/loadflow/status'; +export function fetchLoadFlowStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching loadFlow status on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/loadflow/status'; console.debug(url); return backendFetchText(url); } -export function fetchLoadFlowResult(studyUuid: UUID, currentNodeUuid: UUID, queryParams: QueryParams) { +export function fetchLoadFlowResult( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + queryParams: QueryParams +) { console.info(`Fetching loadflow result on '${studyUuid}' and node '${currentNodeUuid}' ...`); const { sort, filters } = queryParams || {}; const params = new URLSearchParams({}); @@ -96,13 +121,20 @@ export function fetchLoadFlowResult(studyUuid: UUID, currentNodeUuid: UUID, quer if (filters?.length) { params.append('filters', JSON.stringify(filters)); } - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/loadflow/result'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/loadflow/result'; const urlWithParams = `${url}?${params.toString()}`; console.debug(urlWithParams); return backendFetchJson(urlWithParams); } -export function fetchLimitViolations(studyUuid: UUID, currentNodeUuid: UUID, queryParams: QueryParams) { +export function fetchLimitViolations( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + queryParams: QueryParams +) { console.info(`Fetching limit violations ...`); const { sort, filters, globalFilters } = queryParams || {}; const params = new URLSearchParams({}); @@ -117,7 +149,9 @@ export function fetchLimitViolations(studyUuid: UUID, currentNodeUuid: UUID, que params.append('globalFilters', JSON.stringify(globalFilters)); } - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/limit-violations'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/limit-violations'; const urlWithParams = `${url}?${params.toString()}`; console.debug(urlWithParams); return backendFetchJson(urlWithParams); diff --git a/src/services/study/network-map.ts b/src/services/study/network-map.ts index acc7467796..1dd26113f9 100644 --- a/src/services/study/network-map.ts +++ b/src/services/study/network-map.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { backendFetchJson, backendFetchText, getQueryParamsList } from '../utils'; import { EQUIPMENT_INFOS_TYPES } from '../../components/utils/equipment-types'; import { EquipmentInfos, EquipmentType, createFilter } from '@gridsuite/commons-ui'; @@ -14,14 +14,19 @@ import { createContingencyList } from 'services/explore'; import { ContingencyList, createIdentifierContingencyList } from './contingency-list'; import { UUID } from 'crypto'; -export function fetchHvdcLineWithShuntCompensators(studyUuid: UUID, currentNodeUuid: UUID, hvdcLineId: string) { +export function fetchHvdcLineWithShuntCompensators( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + hvdcLineId: string +) { console.info( - `Fetching HVDC Line '${hvdcLineId}' with Shunt Compensators of study '${studyUuid}' and node '${currentNodeUuid}'...` + `Fetching HVDC Line '${hvdcLineId}' with Shunt Compensators of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}'...` ); const urlSearchParams = new URLSearchParams(); urlSearchParams.append('inUpstreamBuiltParentNode', 'true'); const fetchEquipmentsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-map' + '/hvdc-lines/' + hvdcLineId + @@ -31,13 +36,18 @@ export function fetchHvdcLineWithShuntCompensators(studyUuid: UUID, currentNodeU return backendFetchJson(fetchEquipmentsUrl); } -export function fetchAllEquipments(studyUuid: UUID, currentNodeUuid: UUID, substationsIds: string[]) { +export function fetchAllEquipments( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds: string[] +) { console.info( - `Fetching all equipments of study '${studyUuid}' and node '${currentNodeUuid}' with substations ids '${substationsIds}'...` + `Fetching all equipments of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' with substations ids '${substationsIds}'...` ); const fetchEquipmentsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-map/all' + '?' + getQueryParamsList(substationsIds, 'substationId'); @@ -48,12 +58,13 @@ export function fetchAllEquipments(studyUuid: UUID, currentNodeUuid: UUID, subst export function fetchVoltageLevelEquipments( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[], voltageLevelId: string, inUpstreamBuiltParentNode: boolean ) { console.info( - `Fetching equipments of study '${studyUuid}' and node '${currentNodeUuid}' and voltage level '${voltageLevelId}' with substations ids '${substationsIds}'...` + `Fetching equipments of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' and voltage level '${voltageLevelId}' with substations ids '${substationsIds}'...` ); const urlSearchParams = new URLSearchParams(); if (inUpstreamBuiltParentNode !== undefined) { @@ -61,7 +72,7 @@ export function fetchVoltageLevelEquipments( } const fetchEquipmentsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-map' + '/voltage-levels/' + encodeURIComponent(voltageLevelId) + @@ -76,6 +87,7 @@ export function fetchVoltageLevelEquipments( export function fetchEquipmentsIds( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[], equipmentType: EquipmentType, inUpstreamBuiltParentNode: boolean, @@ -93,7 +105,7 @@ export function fetchEquipmentsIds( const nominalVoltagesParamsList = nominalVoltages && nominalVoltages.length > 0 ? '&' + nominalVoltagesParams : ''; let fetchEquipmentsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-map/' + 'equipments-ids' + '?' + @@ -115,6 +127,7 @@ export function fetchEquipmentsIds( export function fetchVoltageLevelIdForLineOrTransformerBySide( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, equipmentId: string, side: string ) { @@ -126,20 +139,22 @@ export function fetchVoltageLevelIdForLineOrTransformerBySide( urlSearchParams.append('inUpstreamBuiltParentNode', 'true'); urlSearchParams.append('side', side); - const fetchEquipmentInfosUrl = `${getStudyUrlWithNodeUuid( + const fetchEquipmentInfosUrl = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, - currentNodeUuid + currentNodeUuid, + currentRootNetworkUuid )}/network-map/branch-or-3wt/${encodeURIComponent(equipmentId)}/voltage-level-id?${urlSearchParams.toString()}`; console.debug(fetchEquipmentInfosUrl); return backendFetchText(fetchEquipmentInfosUrl); } -export function fetchAllCountries(studyUuid: UUID, currentNodeUuid: UUID) { +export function fetchAllCountries(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info(`Fetching all countries of study '${studyUuid}' and node '${currentNodeUuid}' ...`); const fetchCountriesUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/network-map/countries?inUpstreamBuiltParentNode=true'; + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/network-map/countries?inUpstreamBuiltParentNode=true'; console.debug(fetchCountriesUrl); return backendFetchJson(fetchCountriesUrl); } @@ -171,6 +186,7 @@ export async function createMapFilter( destinationDirectoryId: UUID, studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, selectedEquipmentsIds: string[], nominalVoltages: number[] ) { @@ -195,6 +211,7 @@ export async function createMapFilter( const elementsIds = await fetchEquipmentsIds( studyUuid, currentNodeUuid, + currentRootNetworkUuid, selectedEquipmentsIds, equipmentType, false, @@ -220,6 +237,7 @@ export async function createMapContingencyList( destinationDirectoryId: UUID, studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, selectedEquipments: EquipmentInfos[], nominalVoltages: number[] ) { @@ -240,6 +258,7 @@ export async function createMapContingencyList( const elementsIds = await fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, selectedEquipmentsIds, equipmentType, EQUIPMENT_INFOS_TYPES.LIST.type, @@ -263,11 +282,11 @@ export async function createMapContingencyList( return createContingencyList(equipmentContingencyList, elementName, '', destinationDirectoryId); } -export function fetchAllNominalVoltages(studyUuid: UUID, currentNodeUuid: UUID) { +export function fetchAllNominalVoltages(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info(`Fetching all nominal voltages of study '${studyUuid}' and node '${currentNodeUuid}' ...`); const fetchNominalVoltagesUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network-map/nominal-voltages?inUpstreamBuiltParentNode=true'; console.debug(fetchNominalVoltagesUrl); return backendFetchJson(fetchNominalVoltagesUrl); diff --git a/src/services/study/network.ts b/src/services/study/network.ts index 951bcf737d..02cae2faa2 100644 --- a/src/services/study/network.ts +++ b/src/services/study/network.ts @@ -5,7 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrlWithNodeUuid, PREFIX_STUDY_QUERIES, safeEncodeURIComponent } from './index'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES, safeEncodeURIComponent } from './index'; + import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES } from '../../components/utils/equipment-types'; import { backendFetch, backendFetchJson, backendFetchText, getQueryParamsList, getUrlWithToken } from '../utils'; import { UUID } from 'crypto'; @@ -14,6 +15,7 @@ import { EquipmentType, GsLang } from '@gridsuite/commons-ui'; interface VoltageLevelSingleLineDiagram { studyUuid: UUID; currentNodeUuid: UUID; + currentRootNetworkUuid: UUID; voltageLevelId?: UUID; useName: boolean; centerLabel: boolean; @@ -26,6 +28,7 @@ interface VoltageLevelSingleLineDiagram { interface SubstationSingleLineDiagram { studyUuid: UUID; currentNodeUuid: UUID; + currentRootNetworkUuid: UUID; substationId: UUID; useName: boolean; centerLabel: boolean; @@ -39,6 +42,7 @@ interface SubstationSingleLineDiagram { export function getVoltageLevelSingleLineDiagram({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, voltageLevelId, useName, centerLabel, @@ -63,7 +67,7 @@ export function getVoltageLevelSingleLineDiagram({ queryParams.append('componentLibrary', String(componentLibrary)); } return ( - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/voltage-levels/' + safeEncodeURIComponent(voltageLevelId) + '/svg-and-metadata?' + @@ -71,15 +75,20 @@ export function getVoltageLevelSingleLineDiagram({ ); } -export function fetchSubstationIdForVoltageLevel(studyUuid: UUID, currentNodeUuid: UUID, voltageLevelId: string) { +export function fetchSubstationIdForVoltageLevel( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + voltageLevelId: string +) { console.info( - `Fetching substation ID for the voltage level '${voltageLevelId}' of study '${studyUuid}' and node '${currentNodeUuid}' + ' for voltage level '${voltageLevelId}'...` + `Fetching substation ID for the voltage level '${voltageLevelId}' of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' + ' for voltage level '${voltageLevelId}'...` ); const urlSearchParams = new URLSearchParams(); urlSearchParams.append('inUpstreamBuiltParentNode', 'true'); const fetchSubstationIdUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/voltage-levels/' + encodeURIComponent(voltageLevelId) + '/substation-id' + @@ -94,16 +103,17 @@ export function fetchSubstationIdForVoltageLevel(studyUuid: UUID, currentNodeUui export function fetchBusesOrBusbarSectionsForVoltageLevel( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, voltageLevelId: UUID ) { console.info( - `Fetching buses or busbar sections of study '${studyUuid}' and node '${currentNodeUuid}' + ' for voltage level '${voltageLevelId}'...` + `Fetching buses or busbar sections of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' + ' for voltage level '${voltageLevelId}'...` ); const urlSearchParams = new URLSearchParams(); urlSearchParams.append('inUpstreamBuiltParentNode', 'true'); const fetchBusbarSectionsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/voltage-levels/' + encodeURIComponent(voltageLevelId) + '/buses-or-busbar-sections' + @@ -118,6 +128,7 @@ export function fetchBusesOrBusbarSectionsForVoltageLevel( export function getSubstationSingleLineDiagram({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationId, useName, centerLabel, @@ -141,7 +152,7 @@ export function getSubstationSingleLineDiagram({ queryParams.append('componentLibrary', String(componentLibrary)); } return ( - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/substations/' + encodeURIComponent(substationId) + '/svg-and-metadata?' + @@ -153,6 +164,7 @@ export function getSubstationSingleLineDiagram({ export function fetchNetworkElementsInfos( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[] | undefined | null, elementType: string, infoType: string, @@ -163,7 +175,7 @@ export function fetchNetworkElementsInfos( const nominalVoltagesStr = nominalVoltages ? `[${nominalVoltages}]` : '[]'; console.info( - `Fetching network '${elementType}' elements '${infoType}' infos of study '${studyUuid}' and node '${currentNodeUuid}' with ${substationsCount} substations ids and ${nominalVoltagesStr} nominal voltages.` + `Fetching network '${elementType}' elements '${infoType}' infos of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' with ${substationsCount} substations ids and ${nominalVoltagesStr} nominal voltages.` ); const nominalVoltagesParams = getQueryParamsList(nominalVoltages, 'nominalVoltages'); @@ -178,7 +190,7 @@ export function fetchNetworkElementsInfos( urlSearchParams.append('elementType', elementType); const fetchElementsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/elements' + '?' + urlSearchParams + @@ -195,13 +207,14 @@ export function fetchNetworkElementsInfos( export function fetchNetworkElementInfos( studyUuid: string | undefined | null, currentNodeUuid: UUID | undefined, + currentRootNetworkUuid: string | undefined | null, elementType: EquipmentType | EQUIPMENT_TYPES, infoType: string, elementId: string, inUpstreamBuiltParentNode: boolean ) { console.info( - `Fetching specific network element '${elementId}' of type '${elementType}' of study '${studyUuid}' and node '${currentNodeUuid}' ...` + `Fetching specific network element '${elementId}' of type '${elementType}' of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` ); const urlSearchParams = new URLSearchParams(); if (inUpstreamBuiltParentNode !== undefined) { @@ -211,7 +224,7 @@ export function fetchNetworkElementInfos( urlSearchParams.append('infoType', infoType); const fetchElementsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/network/elements/' + encodeURIComponent(elementId) + '?' + @@ -224,12 +237,14 @@ export function fetchNetworkElementInfos( export function fetchSubstationsMapInfos( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[] | null | undefined, inUpstreamBuiltParentNode: boolean ) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.SUBSTATION, EQUIPMENT_INFOS_TYPES.MAP.type, @@ -240,12 +255,14 @@ export function fetchSubstationsMapInfos( export function fetchLinesMapInfos( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[] | undefined | null, inUpstreamBuiltParentNode: boolean ) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.LINE, EQUIPMENT_INFOS_TYPES.MAP.type, @@ -256,12 +273,14 @@ export function fetchLinesMapInfos( export function fetchTieLinesMapInfos( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[] | undefined | null, inUpstreamBuiltParentNode: boolean ) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.TIE_LINE, EQUIPMENT_INFOS_TYPES.MAP.type, @@ -272,12 +291,14 @@ export function fetchTieLinesMapInfos( export function fetchHvdcLinesMapInfos( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, substationsIds: string[] | undefined | null, inUpstreamBuiltParentNode: boolean ) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.HVDC_LINE, EQUIPMENT_INFOS_TYPES.MAP.type, @@ -285,10 +306,16 @@ export function fetchHvdcLinesMapInfos( ); } -export function fetchSubstations(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchSubstations( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.SUBSTATION, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -296,10 +323,16 @@ export function fetchSubstations(studyUuid: UUID, currentNodeUuid: UUID, substat ); } -export function fetchLines(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchLines( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.LINE, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -307,10 +340,16 @@ export function fetchLines(studyUuid: UUID, currentNodeUuid: UUID, substationsId ); } -export function fetchVoltageLevels(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchVoltageLevels( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.VOLTAGE_LEVEL, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -318,10 +357,16 @@ export function fetchVoltageLevels(studyUuid: UUID, currentNodeUuid: UUID, subst ); } -export function fetchVoltageLevelsListInfos(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchVoltageLevelsListInfos( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.VOLTAGE_LEVEL, EQUIPMENT_INFOS_TYPES.LIST.type, @@ -329,10 +374,16 @@ export function fetchVoltageLevelsListInfos(studyUuid: UUID, currentNodeUuid: UU ); } -export function fetchVoltageLevelsMapInfos(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchVoltageLevelsMapInfos( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.VOLTAGE_LEVEL, EQUIPMENT_INFOS_TYPES.MAP.type, @@ -340,10 +391,16 @@ export function fetchVoltageLevelsMapInfos(studyUuid: UUID, currentNodeUuid: UUI ); } -export function fetchTwoWindingsTransformers(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchTwoWindingsTransformers( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -351,10 +408,16 @@ export function fetchTwoWindingsTransformers(studyUuid: UUID, currentNodeUuid: U ); } -export function fetchThreeWindingsTransformers(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchThreeWindingsTransformers( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.THREE_WINDINGS_TRANSFORMER, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -362,20 +425,32 @@ export function fetchThreeWindingsTransformers(studyUuid: UUID, currentNodeUuid: ); } -export function fetchGenerators(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchGenerators( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.GENERATOR, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchLoads(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchLoads( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.LOAD, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -383,30 +458,48 @@ export function fetchLoads(studyUuid: UUID, currentNodeUuid: UUID, substationsId ); } -export function fetchDanglingLines(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchDanglingLines( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.DANGLING_LINE, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchBatteries(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchBatteries( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.BATTERY, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchHvdcLines(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchHvdcLines( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.HVDC_LINE, EQUIPMENT_INFOS_TYPES.TAB.type, @@ -414,75 +507,125 @@ export function fetchHvdcLines(studyUuid: UUID, currentNodeUuid: UUID, substatio ); } -export function fetchLccConverterStations(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchLccConverterStations( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.LCC_CONVERTER_STATION, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchVscConverterStations(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchVscConverterStations( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.VSC_CONVERTER_STATION, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchShuntCompensators(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchShuntCompensators( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.SHUNT_COMPENSATOR, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchStaticVarCompensators(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchStaticVarCompensators( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.STATIC_VAR_COMPENSATOR, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchBuses(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchBuses( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.BUS, EQUIPMENT_INFOS_TYPES.TAB.type ); } -export function fetchBusbarSections(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { +export function fetchBusbarSections( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { return fetchNetworkElementsInfos( studyUuid, currentNodeUuid, + currentRootNetworkUuid, substationsIds, EQUIPMENT_TYPES.BUSBAR_SECTION, EQUIPMENT_INFOS_TYPES.TAB.type ); } - -export const fetchNetworkExistence = (studyUuid: UUID) => { - const fetchNetworkExistenceUrl = `${PREFIX_STUDY_QUERIES}/v1/studies/${studyUuid}/network`; +export function fetchTieLines( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + substationsIds?: string[] +) { + return fetchNetworkElementsInfos( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + substationsIds, + EQUIPMENT_TYPES.TIE_LINE, + EQUIPMENT_INFOS_TYPES.TAB.type + ); +} +export const fetchNetworkExistence = (studyUuid: UUID, rootNetworkUuid: UUID) => { + const fetchNetworkExistenceUrl = `${PREFIX_STUDY_QUERIES}/v1/studies/${studyUuid}/root-networks/${rootNetworkUuid}/network`; return backendFetch(fetchNetworkExistenceUrl, { method: 'HEAD' }); }; -export const fetchStudyIndexationStatus = (studyUuid: UUID) => { +export const fetchStudyIndexationStatus = (studyUuid: UUID, rootNetworkUuid: UUID) => { console.info(`Fetching study indexation status of study '${studyUuid}' ...`); - const fetchStudyIndexationUrl = `${PREFIX_STUDY_QUERIES}/v1/studies/${studyUuid}/indexation/status`; + const fetchStudyIndexationUrl = `${PREFIX_STUDY_QUERIES}/v1/studies/${studyUuid}/root-networks/${rootNetworkUuid}/indexation/status`; console.debug(fetchStudyIndexationUrl); @@ -490,17 +633,16 @@ export const fetchStudyIndexationStatus = (studyUuid: UUID) => { }; /* export-network */ -export function getExportUrl(studyUuid: UUID, nodeUuid: UUID, exportFormat: string) { - const url = getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/export-network/' + exportFormat; - return getUrlWithToken(url); -} -export function fetchTieLines(studyUuid: UUID, currentNodeUuid: UUID, substationsIds?: string[]) { - return fetchNetworkElementsInfos( - studyUuid, - currentNodeUuid, - substationsIds, - EQUIPMENT_TYPES.TIE_LINE, - EQUIPMENT_INFOS_TYPES.TAB.type - ); +export function getExportUrl(studyUuid: UUID, nodeUuid: UUID, rootNetworkUuid: UUID, exportFormat: string) { + // getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, currentRootNetworkUuid) + + // '/root-networks/' + + // rootNetworkUuid + + // '/export-network/' + + // exportFormat; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, nodeUuid, rootNetworkUuid) + + '/export-network/' + + exportFormat; + return getUrlWithToken(url); } diff --git a/src/services/study/non-evacuated-energy.ts b/src/services/study/non-evacuated-energy.ts index 71e84a8d90..db51075954 100644 --- a/src/services/study/non-evacuated-energy.ts +++ b/src/services/study/non-evacuated-energy.ts @@ -5,36 +5,57 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid, PREFIX_STUDY_QUERIES } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES } from './index'; import { backendFetch, backendFetchJson, backendFetchText } from '../utils'; import { UUID } from 'crypto'; -export function startNonEvacuatedEnergy(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Running non evacuated energy analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/non-evacuated-energy/run'; - +export function startNonEvacuatedEnergy(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Running non evacuated energy analysis on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/non-evacuated-energy/run'; console.debug(url); return backendFetch(url, { method: 'post' }); } -export function stopNonEvacuatedEnergy(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping non evacuated energy analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/non-evacuated-energy/stop`; +export function stopNonEvacuatedEnergy(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping non evacuated energy analysis on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/non-evacuated-energy/stop`; console.debug(url); return backendFetch(url, { method: 'put' }); } -export function fetchNonEvacuatedEnergyStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching non evacuated energy analysis status on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/non-evacuated-energy/status`; +export function fetchNonEvacuatedEnergyStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching non evacuated energy analysis status on ${studyUuid} on root network '${currentRootNetworkUuid}' on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/non-evacuated-energy/status`; console.debug(url); return backendFetchText(url); } -export function fetchNonEvacuatedEnergyResult(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching non evacuated energy analysis result on ${studyUuid} and node ${currentNodeUuid} ...`); +export function fetchNonEvacuatedEnergyResult(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching non evacuated energy analysis result on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/non-evacuated-energy/result`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/non-evacuated-energy/result`; console.debug(url); return backendFetchJson(url); } diff --git a/src/services/study/security-analysis.ts b/src/services/study/security-analysis.ts index 1a6a8f1058..929d6364d6 100644 --- a/src/services/study/security-analysis.ts +++ b/src/services/study/security-analysis.ts @@ -5,34 +5,55 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid, PREFIX_STUDY_QUERIES } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES } from './index'; import { backendFetch, backendFetchFile, backendFetchJson, backendFetchText, getRequestParamFromList } from '../utils'; import { UUID } from 'crypto'; import { RESULT_TYPE } from '../../components/results/securityanalysis/security-analysis-result-utils'; -export function startSecurityAnalysis(studyUuid: UUID, currentNodeUuid: UUID, contingencyListNames: string[]) { - console.info(`Running security analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - +export function startSecurityAnalysis( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + contingencyListNames: string[] +) { + console.info( + `Running security analysis on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); // Add params to Url const contingencyListsQueryParams = getRequestParamFromList(contingencyListNames, 'contingencyListName'); const urlSearchParams = new URLSearchParams(contingencyListsQueryParams); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/security-analysis/run?${urlSearchParams}`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/security-analysis/run?${urlSearchParams}`; console.debug(url); return backendFetch(url, { method: 'post' }); } -export function stopSecurityAnalysis(studyUuid: UUID, currentNodeUuid: UUID) { +export function stopSecurityAnalysis(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info('Stopping security analysis on ' + studyUuid + ' and node ' + currentNodeUuid + ' ...'); - const stopSecurityAnalysisUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/security-analysis/stop'; + const stopSecurityAnalysisUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/security-analysis/stop'; console.debug(stopSecurityAnalysisUrl); return backendFetch(stopSecurityAnalysisUrl, { method: 'put' }); } -export function fetchSecurityAnalysisResult(studyUuid: string, currentNodeUuid: string, queryParams: any) { +export function fetchSecurityAnalysisResult( + studyUuid: string, + currentNodeUuid: string, + currentRootNetworkUuid: string, + queryParams: any +) { console.info(`Fetching security analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/security-analysis/result`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/security-analysis/result`; const { resultType, page, size, sort, filters } = queryParams || {}; @@ -57,12 +78,19 @@ export function fetchSecurityAnalysisResult(studyUuid: string, currentNodeUuid: export function downloadSecurityAnalysisResultZippedCsv( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, queryParams: { resultType: RESULT_TYPE }, headers: string[] | undefined, enumValueTranslations: Record ) { - console.info(`Fetching security analysis zipped csv on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/security-analysis/result/csv`; + console.info( + `Fetching security analysis zipped csv on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/security-analysis/result/csv`; const { resultType } = queryParams || {}; @@ -83,10 +111,13 @@ export function downloadSecurityAnalysisResultZippedCsv( }); } -export function fetchSecurityAnalysisStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching security analysis status on ${studyUuid} and node ${currentNodeUuid} ...`); - - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/security-analysis/status'; +export function fetchSecurityAnalysisStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching security analysis status on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/security-analysis/status'; console.debug(url); return backendFetchText(url); } diff --git a/src/services/study/sensitivity-analysis.ts b/src/services/study/sensitivity-analysis.ts index 13bee0145b..cbe0ee733e 100644 --- a/src/services/study/sensitivity-analysis.ts +++ b/src/services/study/sensitivity-analysis.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { PREFIX_STUDY_QUERIES, getStudyUrl, getStudyUrlWithNodeUuid } from './index'; +import { PREFIX_STUDY_QUERIES, getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { backendFetch, backendFetchJson, backendFetchText } from '../utils'; import { UUID } from 'crypto'; import { SensitivityAnalysisParametersInfos } from './sensitivity-analysis.type'; @@ -31,40 +31,62 @@ interface CsvConfig { sensitivityFunctionType: string; } -export function startSensitivityAnalysis(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Running sensi on ${studyUuid} and node ${currentNodeUuid} ...`); - const startSensiAnalysisUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/sensitivity-analysis/run'; - +export function startSensitivityAnalysis(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Running sensi on ${studyUuid} for root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const startSensiAnalysisUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/sensitivity-analysis/run'; console.debug(startSensiAnalysisUrl); return backendFetch(startSensiAnalysisUrl, { method: 'post' }); } -export function stopSensitivityAnalysis(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping sensitivity analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - const stopSensitivityAnalysisUrl = `${getStudyUrlWithNodeUuid( +export function stopSensitivityAnalysis(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping sensitivity analysis on ${studyUuid} for root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); + const stopSensitivityAnalysisUrl = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, - currentNodeUuid + currentNodeUuid, + currentRootNetworkUuid )}/sensitivity-analysis/stop`; console.debug(stopSensitivityAnalysisUrl); return backendFetch(stopSensitivityAnalysisUrl, { method: 'put' }); } -export function fetchSensitivityAnalysisStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching sensitivity analysis status on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/sensitivity-analysis/status`; +export function fetchSensitivityAnalysisStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching sensitivity analysis status on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/sensitivity-analysis/status`; console.debug(url); return backendFetchText(url); } -export function fetchSensitivityAnalysisResult(studyUuid: UUID, currentNodeUuid: UUID, selector: any) { - console.info(`Fetching sensitivity analysis on ${studyUuid} and node ${currentNodeUuid} ...`); - +export function fetchSensitivityAnalysisResult( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + selector: any +) { + console.info( + `Fetching sensitivity analysis on ${studyUuid} for root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); // Add params to Url const urlSearchParams = new URLSearchParams(); const jsoned = JSON.stringify(selector); urlSearchParams.append('selector', jsoned); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/sensitivity-analysis/result?${urlSearchParams}`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/sensitivity-analysis/result?${urlSearchParams}`; console.debug(url); return backendFetchJson(url); } @@ -72,18 +94,21 @@ export function fetchSensitivityAnalysisResult(studyUuid: UUID, currentNodeUuid: export function fetchSensitivityAnalysisFilterOptions( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, selector: SelectorFilterOptions ) { - console.info(`Fetching sensitivity analysis filter options on ${studyUuid} and node ${currentNodeUuid} ...`); - + console.info( + `Fetching sensitivity analysis filter options on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); // Add params to Url const urlSearchParams = new URLSearchParams(); const jsoned = JSON.stringify(selector); urlSearchParams.append('selector', jsoned); - const url = `${getStudyUrlWithNodeUuid( + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, - currentNodeUuid + currentNodeUuid, + currentRootNetworkUuid )}/sensitivity-analysis/result/filter-options?${urlSearchParams}`; console.debug(url); return backendFetchJson(url); @@ -130,6 +155,7 @@ export function setSensitivityAnalysisParameters( export function getSensitivityAnalysisFactorsCount( studyUuid: UUID | null, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, isInjectionsSet: boolean, newParams: SensitivityAnalysisFactorsCountParameters ) { @@ -143,7 +169,7 @@ export function getSensitivityAnalysisFactorsCount( // @ts-ignore .forEach((key) => urlSearchParams.append(`ids[${key}]`, newParams[key])); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)} + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid)} /sensitivity-analysis/factors-count?${urlSearchParams}`; console.debug(url); return backendFetch(url, { @@ -151,10 +177,21 @@ export function getSensitivityAnalysisFactorsCount( }); } -export function exportSensitivityResultsAsCsv(studyUuid: UUID, currentNodeUuid: UUID, csvConfig: CsvConfig) { - console.info(`Exporting sensitivity analysis on ${studyUuid} and node ${currentNodeUuid} as CSV ...`); +export function exportSensitivityResultsAsCsv( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, + csvConfig: CsvConfig +) { + console.info( + `Exporting sensitivity analysis on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} as CSV ...` + ); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/sensitivity-analysis/result/csv`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/sensitivity-analysis/result/csv`; console.debug(url); return backendFetch(url, { method: 'POST', diff --git a/src/services/study/short-circuit-analysis.ts b/src/services/study/short-circuit-analysis.ts index 2f1a7b1424..a1444c9583 100644 --- a/src/services/study/short-circuit-analysis.ts +++ b/src/services/study/short-circuit-analysis.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { getShortCircuitAnalysisTypeFromEnum, ShortCircuitAnalysisType, @@ -21,6 +21,7 @@ const PREFIX_SHORT_CIRCUIT_SERVER_QUERIES = import.meta.env.VITE_API_GATEWAY + ' interface ShortCircuitAnalysisResult { studyUuid: UUID | null; currentNodeUuid?: UUID; + currentRootNetworkUuid?: UUID; type: ShortCircuitAnalysisType; } interface Selector { @@ -55,21 +56,37 @@ function getShortCircuitUrl() { return `${PREFIX_SHORT_CIRCUIT_SERVER_QUERIES}/v1/`; } -export function startShortCircuitAnalysis(studyUuid: string, currentNodeUuid: UUID | undefined, busId: string) { - console.info(`Running short circuit analysis on '${studyUuid}' and node '${currentNodeUuid}' ...`); - +export function startShortCircuitAnalysis( + studyUuid: string, + currentNodeUuid: UUID | undefined, + currentRootNetworkUuid: UUID | null, + busId: string +) { + console.info( + `Running short circuit analysis on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); const urlSearchParams = new URLSearchParams(); busId && urlSearchParams.append('busId', busId); const startShortCircuitAnalysisUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/shortcircuit/run?' + urlSearchParams.toString(); + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/shortcircuit/run?' + + urlSearchParams.toString(); console.debug(startShortCircuitAnalysisUrl); return backendFetch(startShortCircuitAnalysisUrl, { method: 'put' }); } -export function stopShortCircuitAnalysis(studyUuid: string, currentNodeUuid: UUID | undefined) { - console.info(`Stopping short circuit analysis on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const stopShortCircuitAnalysisUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/shortcircuit/stop'; +export function stopShortCircuitAnalysis( + studyUuid: string, + currentNodeUuid: UUID | undefined, + currentRootNetworkUuid: UUID | undefined +) { + console.info( + `Stopping short circuit analysis on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const stopShortCircuitAnalysisUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/shortcircuit/stop'; console.debug(stopShortCircuitAnalysisUrl); return backendFetch(stopShortCircuitAnalysisUrl, { method: 'put' }); } @@ -77,31 +94,48 @@ export function stopShortCircuitAnalysis(studyUuid: string, currentNodeUuid: UUI export function fetchShortCircuitAnalysisStatus( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, type = ShortCircuitAnalysisType.ALL_BUSES ) { const analysisType = getShortCircuitAnalysisTypeFromEnum(type); console.info( - `Fetching ${analysisType} short circuit analysis status on '${studyUuid}' and node '${currentNodeUuid}' ...` + `Fetching ${analysisType} short circuit analysis status on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` ); const urlSearchParams = new URLSearchParams(); if (analysisType !== null) { urlSearchParams.append('type', analysisType); } const url = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/shortcircuit/status?' + urlSearchParams.toString(); + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/shortcircuit/status?' + + urlSearchParams.toString(); console.debug(url); return backendFetchText(url); } -export function fetchOneBusShortCircuitAnalysisStatus(studyUuid: UUID, currentNodeUuid: UUID) { - return fetchShortCircuitAnalysisStatus(studyUuid, currentNodeUuid, ShortCircuitAnalysisType.ONE_BUS); +export function fetchOneBusShortCircuitAnalysisStatus( + studyUuid: UUID, + currentNodeUuid: UUID, + currentRootNetworkUuid: UUID +) { + return fetchShortCircuitAnalysisStatus( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + ShortCircuitAnalysisType.ONE_BUS + ); } -export function fetchShortCircuitAnalysisResult({ studyUuid, currentNodeUuid, type }: ShortCircuitAnalysisResult) { +export function fetchShortCircuitAnalysisResult({ + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + type, +}: ShortCircuitAnalysisResult) { const analysisType = getShortCircuitAnalysisTypeFromEnum(type); console.info( - `Fetching ${analysisType} short circuit analysis result on '${studyUuid}' and node '${currentNodeUuid}' ...` + `Fetching ${analysisType} short circuit analysis result on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` ); const urlSearchParams = new URLSearchParams(); if (analysisType) { @@ -109,7 +143,9 @@ export function fetchShortCircuitAnalysisResult({ studyUuid, currentNodeUuid, ty } const url = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/shortcircuit/result?' + urlSearchParams.toString(); + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/shortcircuit/result?' + + urlSearchParams.toString(); console.debug(url); return backendFetchJson(url); } @@ -117,6 +153,7 @@ export function fetchShortCircuitAnalysisResult({ studyUuid, currentNodeUuid, ty export function fetchShortCircuitAnalysisPagedResults({ studyUuid, currentNodeUuid, + currentRootNetworkUuid, selector = {}, type = ShortCircuitAnalysisType.ALL_BUSES, }: ShortCircuitAnalysisPagedResults) { @@ -149,7 +186,9 @@ export function fetchShortCircuitAnalysisPagedResults({ } const url = - getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/shortcircuit/result?' + urlSearchParams.toString(); + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/shortcircuit/result?' + + urlSearchParams.toString(); console.debug(url); return backendFetchJson(url); } @@ -194,12 +233,17 @@ export function invalidateShortCircuitStatus(studyUuid: UUID | null) { export function downloadShortCircuitResultZippedCsv( studyUuid: UUID, currentNodeUuid: UUID, + currentRootNetworkUuid: UUID, analysisType: number, headersCsv: string[] | undefined, enumValueTranslations: Record ) { console.info(`Fetching short-circuit analysis export csv on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/shortcircuit/result/csv`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/shortcircuit/result/csv`; const type = getShortCircuitAnalysisTypeFromEnum(analysisType); const param = new URLSearchParams(); if (type) { diff --git a/src/services/study/state-estimation.ts b/src/services/study/state-estimation.ts index e9ec1a9469..41c2611fc6 100644 --- a/src/services/study/state-estimation.ts +++ b/src/services/study/state-estimation.ts @@ -5,36 +5,58 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { backendFetch, backendFetchJson, backendFetchText } from '../utils'; import { UUID } from 'crypto'; -export function startStateEstimation(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Running state estimation on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/state-estimation/run'; +export function startStateEstimation(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Running state estimation on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/state-estimation/run'; console.debug(url); return backendFetch(url, { method: 'post' }); } -export function stopStateEstimation(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping state estimation on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/state-estimation/stop`; +export function stopStateEstimation(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping state estimation on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/state-estimation/stop`; console.debug(url); return backendFetch(url, { method: 'put' }); } -export function fetchStateEstimationStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching state estimation status on ${studyUuid} and node ${currentNodeUuid} ...`); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/state-estimation/status`; +export function fetchStateEstimationStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching state estimation status on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` + ); + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/state-estimation/status`; console.debug(url); return backendFetchText(url); } -export function fetchStateEstimationResult(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching state estimation result on ${studyUuid} and node ${currentNodeUuid} ...`); +export function fetchStateEstimationResult(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching state estimation result on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` + ); - const url = `${getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid)}/state-estimation/result`; + const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid + )}/state-estimation/result`; console.debug(url); return backendFetchJson(url); } diff --git a/src/services/study/study.ts b/src/services/study/study.ts index 9824a94af3..f6ca25392d 100644 --- a/src/services/study/study.ts +++ b/src/services/study/study.ts @@ -18,6 +18,7 @@ interface BasicStudyInfos { export const recreateStudyNetworkFromExistingCase = ( caseUuid: UUID, studyUuid: UUID, + currentRootNetworkUuid: UUID, importParameters: Record ): Promise => { const urlSearchParams = new URLSearchParams(); @@ -27,6 +28,8 @@ export const recreateStudyNetworkFromExistingCase = ( PREFIX_STUDY_QUERIES + '/v1/studies/' + encodeURIComponent(studyUuid) + + '/root-networks/' + + encodeURIComponent(currentRootNetworkUuid) + '/network?' + urlSearchParams.toString(); @@ -39,8 +42,14 @@ export const recreateStudyNetworkFromExistingCase = ( }); }; -export const recreateStudyNetwork = (studyUuid: UUID): Promise => { - const recreateStudyNetworkUrl = PREFIX_STUDY_QUERIES + '/v1/studies/' + encodeURIComponent(studyUuid) + '/network'; +export const recreateStudyNetwork = (studyUuid: UUID, currentRootNetworkUuid: UUID): Promise => { + const recreateStudyNetworkUrl = + PREFIX_STUDY_QUERIES + + '/v1/studies/' + + encodeURIComponent(studyUuid) + + '/root-networks/' + + encodeURIComponent(currentRootNetworkUuid) + + '/network'; console.debug(recreateStudyNetworkUrl); @@ -50,8 +59,14 @@ export const recreateStudyNetwork = (studyUuid: UUID): Promise }); }; -export const reindexAllStudy = (studyUuid: UUID): Promise => { - const reindexAllStudyUrl = PREFIX_STUDY_QUERIES + '/v1/studies/' + encodeURIComponent(studyUuid) + '/reindex-all'; +export const reindexAllStudy = (studyUuid: UUID, currentRootNetworkUuid: UUID): Promise => { + const reindexAllStudyUrl = + PREFIX_STUDY_QUERIES + + '/v1/studies/' + + encodeURIComponent(studyUuid) + + '/root-networks/' + + encodeURIComponent(currentRootNetworkUuid) + + '/reindex-all'; console.debug(reindexAllStudyUrl); diff --git a/src/services/study/tree-subtree.ts b/src/services/study/tree-subtree.ts index 222278becf..f9576f0c59 100644 --- a/src/services/study/tree-subtree.ts +++ b/src/services/study/tree-subtree.ts @@ -177,16 +177,21 @@ export function updateNodesColumnPositions(studyUuid: UUID, parentNodeId: UUID, }); } -export function fetchNetworkModificationTreeNode(studyUuid: UUID, nodeUuid: UUID) { +export function fetchNetworkModificationTreeNode(studyUuid: UUID, nodeUuid: UUID, rootNetworkUuid: UUID) { console.info('Fetching network modification tree node : ', nodeUuid); - const url = getStudyUrl(studyUuid) + '/tree/nodes/' + encodeURIComponent(nodeUuid); + const urlSearchParams = new URLSearchParams(); + urlSearchParams.set('rootNetworkUuid', rootNetworkUuid); + const url = + getStudyUrl(studyUuid) + '/tree/nodes/' + encodeURIComponent(nodeUuid) + '?' + urlSearchParams.toString(); console.debug(url); return backendFetchJson(url); } -export function fetchNetworkModificationTree(studyUuid: UUID) { +export function fetchNetworkModificationTree(studyUuid: UUID, rootNetworkUuid: UUID) { console.info('Fetching network modification tree'); - const url = getStudyUrl(studyUuid) + '/tree'; + const urlSearchParams = new URLSearchParams(); + urlSearchParams.set('rootNetworkUuid', rootNetworkUuid); + const url = getStudyUrl(studyUuid) + '/tree?' + urlSearchParams.toString(); console.debug(url); return backendFetchJson(url); } diff --git a/src/services/study/voltage-init.ts b/src/services/study/voltage-init.ts index 24ba1afc27..9ee411495b 100644 --- a/src/services/study/voltage-init.ts +++ b/src/services/study/voltage-init.ts @@ -5,36 +5,51 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { getStudyUrl, getStudyUrlWithNodeUuid } from './index'; +import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; import { backendFetch, backendFetchJson, backendFetchText } from '../utils'; import { UUID } from 'crypto'; import { VoltageInitParam } from '../../components/dialogs/parameters/voltageinit/voltage-init-utils'; -export function startVoltageInit(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Running voltage init on '${studyUuid}' and node '${currentNodeUuid}' ...`); +export function startVoltageInit(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Running voltage init on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); - const startVoltageInitUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/voltage-init/run'; + const startVoltageInitUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/voltage-init/run'; console.debug(startVoltageInitUrl); return backendFetch(startVoltageInitUrl, { method: 'put' }); } -export function stopVoltageInit(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Stopping voltage init on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const stopVoltageInitUrl = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/voltage-init/stop'; +export function stopVoltageInit(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Stopping voltage init on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const stopVoltageInitUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/voltage-init/stop'; console.debug(stopVoltageInitUrl); return backendFetch(stopVoltageInitUrl, { method: 'put' }); } -export function fetchVoltageInitStatus(studyUuid: UUID, currentNodeUuid: UUID) { - console.info(`Fetching voltage init status on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/voltage-init/status'; +export function fetchVoltageInitStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { + console.info( + `Fetching voltage init status on '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}' ...` + ); + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/voltage-init/status'; + console.debug(url); return backendFetchText(url); } -export function fetchVoltageInitResult(studyUuid: UUID, currentNodeUuid: UUID) { +export function fetchVoltageInitResult(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info(`Fetching voltage init result on '${studyUuid}' and node '${currentNodeUuid}' ...`); - const url = getStudyUrlWithNodeUuid(studyUuid, currentNodeUuid) + '/voltage-init/result'; + const url = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/voltage-init/result'; console.debug(url); return backendFetchJson(url); } @@ -66,18 +81,20 @@ export function getVoltageInitStudyParameters(studyUuid: UUID) { return backendFetchJson(getVoltageInitParams); } -export function getVoltageInitModifications(studyUuid: UUID, currentNodeId: UUID) { +export function getVoltageInitModifications(studyUuid: UUID, currentNodeId: UUID, currentRootNetworkUuid: UUID) { console.info('get voltage init modifications'); const getVoltageInitModifications = - getStudyUrlWithNodeUuid(studyUuid, currentNodeId) + '/voltage-init/modifications'; + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeId, currentRootNetworkUuid) + + '/voltage-init/modifications'; console.debug(getVoltageInitModifications); return backendFetchJson(getVoltageInitModifications); } -export function cloneVoltageInitModifications(studyUuid: UUID, currentNodeId: UUID) { +export function cloneVoltageInitModifications(studyUuid: UUID, currentNodeId: UUID, currentRootNetworkUuid: UUID) { console.info('cloning voltage init modifications'); const cloneVoltageInitModificationsUrl = - getStudyUrlWithNodeUuid(studyUuid, currentNodeId) + '/voltage-init/modifications'; + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeId, currentRootNetworkUuid) + + '/voltage-init/modifications'; return backendFetch(cloneVoltageInitModificationsUrl, { method: 'PUT', diff --git a/src/translations/en.json b/src/translations/en.json index 7eb5e121e2..ea93ddc5eb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -55,6 +55,8 @@ "SingleLineDiagram": "Single line diagram", "NetworkAreaDiagram": "Network area diagram", "studyNotFound": "Study with id \"{studyUuid}\" has not been found", + "rootNetworkNotFound": "No root network was not found for the study with ID \"{studyUuid}\"", + "svgNotFound": "Diagram has not been found: {error}; {svgUrl}", "svgLoadingFail": "The diagram couldn't be loaded", "networkLoadingFail": "Network of study with id \"{studyUuid}\" couldn't be loaded", @@ -371,6 +373,9 @@ "TwoSides.ONE": "Origin side", "TwoSides.TWO": "Extremity side", + "selectCase": "Select case", + "ChooseSituation": "Select a case", + "ContingencyListsSelection": "Contingency lists selection", "Execute": "Execute", "AddContingencyList": "Add", @@ -566,6 +571,8 @@ "HideMinimap": "Hide minimap", "DisplayTheWholeTree": "Display the whole tree", + "CreateRootNetwork": "Configure root network", + "Logs": "Logs", "logsTitle": "Logs : {title}", "showReport": "Show logs", @@ -603,6 +610,7 @@ "NodeBuildingError": "An error occurred while building node", "NodeUnbuildingError": "An error occurred while unbuilding node", "NetworkModifications": "Network modifications", + "RootNetwork": "Root Network", "CreateLoad": "Create load", "ModifyLoad": "Modify load", "NameOptional": "Name (optional)", @@ -1230,6 +1238,7 @@ "SaveModificationTo": "Save to GridExplore", "ModificationsSelection": "Modification selection", "errCreateModificationsMsg": "Network modifications creation error", + "errCreateRootNetworksMsg": "Root Networks creation error", "infoCreateModificationsMsg": "Composite modification of {nbModifications} unitary network modifications created in {studyDirectory}", "idSelector.idNeeded": "Please select an ID", "SpreadsheetFetchError": "An error occurred while fetching equipments in the spreadsheet", @@ -1446,5 +1455,14 @@ "qualityPerRegionResults": "Quality region", "StateEstimationStatus": "Status : ", "StateEstimationQuality": "Quality : ", - "Or": "or" + "Or": "or", + "rootNetworksCount":"{hide, select, false {{count, plural, =0 {aucun réseau racine} =1 {# réseau racine} other {# réseaux racine}}} other {...}}", + "updateRootNetworksList": "Updating root network list ...", + "deletingRootNetwork": "Deleting root network ...", + "creatingRootNetwork": "Creating root network ...", + "rootNetworkCreated": "Root Network has been created successfully", + "errDeleteRootNetworkMsg": "Root Networks deletion error", + "warnDeleteCurrentRootNetwork" : "Current root network can not be delete" + + } diff --git a/src/translations/fr.json b/src/translations/fr.json index fde58cc42e..c8cc0bdd92 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -56,6 +56,8 @@ "NetworkAreaDiagram": "Image nodale de zone", "studyNotFound": "L'étude avec l'identifiant \"{studyUuid}\" n'a pas été trouvée", + "RootNetworkNotFound": "Aucun réseau racine n'a été trouvé pour l'étude avec l'ID \"{studyUuid}\"", + "svgNotFound": "L'image poste n'a pas été trouvée : {error}; {svgUrl}", "svgLoadingFail": "L'image n'a pas pu être chargée", "networkLoadingFail": "Le réseau de l'étude avec l'identifiant \"{studyUuid}\" n'a pas pu être chargé", @@ -372,6 +374,9 @@ "TwoSides.ONE": "Côté 1", "TwoSides.TWO": "Côté 2", + "selectCase": "Sélectionner une situation", + "ChooseSituation": "Choisir une situation", + "ContingencyListsSelection": "Sélection des listes d'aléas", "Execute": "Exécuter", "AddContingencyList": "Ajouter", @@ -568,6 +573,8 @@ "HideMinimap": "Masquer la mini-carte", "DisplayTheWholeTree": "Visualiser l'arbre en entier", + "CreateRootNetwork": "Configurer un réseau racine", + "Logs": "Logs", "logsTitle": "Logs : {title}", "showReport": "Afficher logs", @@ -606,6 +613,8 @@ "NodeBuildingError": "Une erreur est survenue lors de la réalisation du nœud", "NodeUnbuildingError": "Une erreur est survenue lors de la déréalisation du nœud", "NetworkModifications": "Modifications de réseau", + "RootNetwork": "Réseau racine", + "CreateLoad": "Créer une consommation", "ModifyLoad": "Modifier une consommation", "NameOptional": "Nom (optionnel)", @@ -1230,6 +1239,8 @@ "SaveModificationTo": "Enregistrer dans GridExplore", "ModificationsSelection": "Sélection des modifications", "errCreateModificationsMsg": "Une erreur est survenue lors de la création des modifications", + "errCreateRootNetworksMsg": "Une erreur est survenue lors de la création du réseau racine", + "infoCreateModificationsMsg": "Création d'une modification composite de {nbModifications} modifications unitaires dans {studyDirectory}", "idSelector.idNeeded": "Veuillez sélectionner un ID", "SpreadsheetFetchError": "Une erreur est survenue lors du chargement des ouvrages dans le tableur", @@ -1446,5 +1457,14 @@ "qualityPerRegionResults": "Qualité par région", "StateEstimationStatus": "Statut : ", "StateEstimationQuality": "Qualité: ", - "Or": "ou" + "Or": "ou", + "rootNetworksCount":"{hide, select, false {{count, plural, =0 {no root network} =1 {{count} root network} other {{count} root networks}}} other {...}}", + "updateRootNetworksList": "Mise à jour de la liste des réseaux racine cours ...", + "deletingRootNetwork": "Suppression du réseau racine en cours ...", + "creatingRootNetwork": "Création du réseau racine en cours ...", + "rootNetworkCreated": "Le réseau racine a été créé avec succès", + "errDeleteRootNetworkMsg": "Une erreur est survenue lors de la suppression des réseaux racine", + "warnDeleteCurrentRootNetwork": "Le réseau racine actuel ne peut pas être supprimé" + + } \ No newline at end of file