diff --git a/src/components/computing-status/use-all-computing-status.ts b/src/components/computing-status/use-all-computing-status.ts index 11e6bba8dc..594e203977 100644 --- a/src/components/computing-status/use-all-computing-status.ts +++ b/src/components/computing-status/use-all-computing-status.ts @@ -109,6 +109,7 @@ export const dynamicSimulationResultInvalidations = [NotificationType.DYNAMIC_SI export const dynamicSecurityAnalysisResultInvalidations = [NotificationType.DYNAMIC_SECURITY_ANALYSIS_RESULT]; export const voltageInitResultInvalidations = [NotificationType.VOLTAGE_INIT_RESULT]; export const stateEstimationResultInvalidations = [NotificationType.STATE_ESTIMATION_RESULT]; +export const pccMinResultInvalidations = [NotificationType.PCC_MIN_RESULT]; // 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, currentRootNetworkUuid: UUID): void => { diff --git a/src/components/graph/tree-node.type.ts b/src/components/graph/tree-node.type.ts index 5780375424..c3245fdafb 100644 --- a/src/components/graph/tree-node.type.ts +++ b/src/components/graph/tree-node.type.ts @@ -60,6 +60,7 @@ export type NetworkModificationNodeData = AbstractNode & { sensitivityAnalysisResultUuid?: UUID; dynamicSimulationResultUuid?: UUID; stateEstimationResultUuid?: UUID; + pccMinResultUuid?: UUID; nodeBuildStatus?: NodeBuildStatus; nodeType?: NetworkModificationNodeType; }; diff --git a/src/components/result-view-tab.tsx b/src/components/result-view-tab.tsx index a99117dfde..4bab96d56a 100644 --- a/src/components/result-view-tab.tsx +++ b/src/components/result-view-tab.tsx @@ -29,6 +29,7 @@ import { ComputingType, type MuiStyles, usePrevious } from '@gridsuite/commons-u import { useParameterState } from './dialogs/parameters/use-parameters-state'; import { IService } from './result-view-tab.type'; import { CurrentTreeNode } from './graph/tree-node.type'; +import { PccMinResultTab } from './results/pccmin/pcc-min-result-tab'; const styles = { table: { @@ -85,6 +86,7 @@ export const ResultViewTab: FunctionComponent = ({ const voltageInitAvailability = useOptionalServiceStatus(OptionalServicesNames.VoltageInit); const shortCircuitAvailability = useOptionalServiceStatus(OptionalServicesNames.ShortCircuit); const stateEstimationAvailability = useOptionalServiceStatus(OptionalServicesNames.StateEstimation); + const pccMinAvailability = useOptionalServiceStatus(OptionalServicesNames.PccMin); const renderLoadFlowResult = useMemo(() => { return ( @@ -186,6 +188,18 @@ export const ResultViewTab: FunctionComponent = ({ ); }, [studyUuid, currentNode, currentRootNetworkUuid]); + const renderPccMinResult = useMemo(() => { + return ( + + + + ); + }, [currentNode?.id, currentRootNetworkUuid, studyUuid]); + const services: IService[] = useMemo(() => { return [ { @@ -236,24 +250,32 @@ export const ResultViewTab: FunctionComponent = ({ displayed: enableDeveloperMode && stateEstimationAvailability === OptionalServicesStatus.Up, renderResult: renderStateEstimationResult, }, + { + id: 'PccMin', + computingType: [ComputingType.PCC_MIN], + displayed: enableDeveloperMode && pccMinAvailability === OptionalServicesStatus.Up, + renderResult: renderPccMinResult, + }, ].filter(({ displayed }: IService) => displayed); }, [ - sensitivityAnalysisUnavailability, + renderLoadFlowResult, securityAnalysisAvailability, - dynamicSimulationAvailability, - dynamicSecurityAnalysisAvailability, - voltageInitAvailability, + renderSecurityAnalysisResult, + sensitivityAnalysisUnavailability, + renderSensitivityAnalysisResult, shortCircuitAvailability, - stateEstimationAvailability, + renderShortCircuitAnalysisResult, enableDeveloperMode, + dynamicSimulationAvailability, renderDynamicSimulationResult, + dynamicSecurityAnalysisAvailability, renderDynamicSecurityAnalysisResult, - renderSecurityAnalysisResult, - renderSensitivityAnalysisResult, - renderShortCircuitAnalysisResult, + voltageInitAvailability, renderVoltageInitResult, - renderLoadFlowResult, + stateEstimationAvailability, renderStateEstimationResult, + pccMinAvailability, + renderPccMinResult, ]); const resultTabIndexRedirection = useMemo( diff --git a/src/components/results/common/utils.ts b/src/components/results/common/utils.ts index b8ccaa5857..845b71bede 100644 --- a/src/components/results/common/utils.ts +++ b/src/components/results/common/utils.ts @@ -7,6 +7,7 @@ import { NA_Value } from 'components/custom-aggrid/utils/format-values-utils'; import { IntlShape } from 'react-intl'; import type { MuiStyles } from '@gridsuite/commons-ui'; +import { FilterConfig, SortConfig } from 'types/custom-aggrid-types'; export const PERMANENT_LIMIT_NAME = 'permanent'; @@ -39,9 +40,45 @@ export enum FilterType { SUBSTATION_PROPERTY = 'substationProperty', } +export interface Selector { + page: number; + size: number; + filter: FilterConfig[] | null; + sort: SortConfig[]; +} + export const resultsStyles = { sldLink: { color: 'node.background', maxWidth: '100%', }, } as const satisfies MuiStyles; + +export type Pageable = { + offset?: number; + pageNumber?: number; + pageSize?: number; + paged?: boolean; + sort?: Sort; + unpaged?: boolean; +}; + +export type Sort = { + empty?: boolean; + sorted?: boolean; + unsorted?: boolean; +}; + +export interface Page { + content?: ResultType[]; + pageable?: Pageable; + last?: boolean; + totalPages?: number; + totalElements?: number; + first?: boolean; + size?: number; + number?: number; + sort?: Sort; + numberOfElements?: number; + empty?: boolean; +} diff --git a/src/components/results/loadflow/limit-violation-result.tsx b/src/components/results/loadflow/limit-violation-result.tsx index 3f8bbb0ac6..06dc93e1ea 100644 --- a/src/components/results/loadflow/limit-violation-result.tsx +++ b/src/components/results/loadflow/limit-violation-result.tsx @@ -65,12 +65,6 @@ export const LimitViolationResult: FunctionComponent [] ); - const onRowDataUpdated = useCallback((params: any) => { - if (params.api) { - params.api.sizeColumnsToFit(); - } - }, []); - const getRowStyle = useCallback( (params: RowClassParams) => { if (params?.data?.elementId) { @@ -82,9 +76,6 @@ export const LimitViolationResult: FunctionComponent [theme.selectedRow.background] ); - const onGridReady = useCallback(({ api }: GridReadyEvent) => { - api?.sizeColumnsToFit(); - }, []); const messages = useIntlResultStatusMessages(intl); const renderLoadFlowLimitViolations = () => { @@ -100,8 +91,6 @@ export const LimitViolationResult: FunctionComponent defaultColDef={defaultColDef} tableName={tableName} rows={rowsToShow} - onRowDataUpdated={onRowDataUpdated} - onGridReady={onGridReady} getRowStyle={getRowStyle} overlayNoRowsTemplate={message} skipColumnHeaders={false} diff --git a/src/components/results/loadflow/load-flow-result-utils.ts b/src/components/results/loadflow/load-flow-result-utils.ts index 37e1b3070c..a53f867ca4 100644 --- a/src/components/results/loadflow/load-flow-result-utils.ts +++ b/src/components/results/loadflow/load-flow-result-utils.ts @@ -26,12 +26,16 @@ import { AppState } from 'redux/reducer'; import RunningStatus from 'components/utils/running-status'; import { CustomAggridComparatorFilter } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-comparator-filter'; import CustomAggridDurationFilter from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-duration-filter'; -import { FilterConfig, FilterType as AgGridFilterType } from '../../../types/custom-aggrid-types'; +import { + FilterConfig, + FilterType as AgGridFilterType, + textFilterParams, + numericFilterParams, +} from '../../../types/custom-aggrid-types'; import { CustomAggridAutocompleteFilter } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-autocomplete-filter'; import { ColumnContext, FILTER_DATA_TYPES, - FILTER_NUMBER_COMPARATORS, FILTER_TEXT_COMPARATORS, FilterEnumsType, } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type'; @@ -74,21 +78,11 @@ export const FROM_COLUMN_TO_FIELD_LOADFLOW_RESULT: Record = { distributedActivePower: 'distributedActivePower', }; -const textFilterParams = { - dataType: FILTER_DATA_TYPES.TEXT, - comparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], -}; - const translatedFilterParams = { dataType: FILTER_DATA_TYPES.TEXT, comparators: [FILTER_TEXT_COMPARATORS.EQUALS], }; -const numericFilterParams = { - dataType: FILTER_DATA_TYPES.NUMBER, - comparators: Object.values(FILTER_NUMBER_COMPARATORS), -}; - export const mappingFields = (index: number): Record => { switch (index) { case 0: diff --git a/src/components/results/loadflow/load-flow-result.tsx b/src/components/results/loadflow/load-flow-result.tsx index 25557ab367..b718d733ab 100644 --- a/src/components/results/loadflow/load-flow-result.tsx +++ b/src/components/results/loadflow/load-flow-result.tsx @@ -54,12 +54,6 @@ export const LoadFlowResult: FunctionComponent = ({ result, [] ); - const onRowDataUpdated = useCallback((params: any) => { - if (params.api) { - params.api.sizeColumnsToFit(); - } - }, []); - const messages = useIntlResultStatusMessages(intl); const getRowStyle = useCallback( @@ -73,10 +67,6 @@ export const LoadFlowResult: FunctionComponent = ({ result, [theme.selectedRow.background] ); - const onGridReady = useCallback(({ api }: GridReadyEvent) => { - api?.sizeColumnsToFit(); - }, []); - const renderLoadFlowResult = () => { const message = getNoRowsMessage( messages, @@ -97,8 +87,6 @@ export const LoadFlowResult: FunctionComponent = ({ result, id: 'LoadFlowResultsStatus', })} rows={rowsToShow} - onRowDataUpdated={onRowDataUpdated} - onGridReady={onGridReady} getRowStyle={getRowStyle} overlayNoRowsTemplate={message} skipColumnHeaders={false} diff --git a/src/components/results/pcc-min-columns-utils.ts b/src/components/results/pcc-min-columns-utils.ts new file mode 100644 index 0000000000..e4c1e67c10 --- /dev/null +++ b/src/components/results/pcc-min-columns-utils.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +import { numericFilterParams, textFilterParams, FilterType as AgGridFilterType } from 'types/custom-aggrid-types'; +import { ColumnContext } from 'components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type'; +import { CustomAggridComparatorFilter } from 'components/custom-aggrid/custom-aggrid-filters/custom-aggrid-comparator-filter'; +import { PCCMIN_ANALYSIS_RESULT_SORT_STORE, PCCMIN_RESULT } from 'utils/store-sort-filter-fields'; +import { IntlShape } from 'react-intl'; +import { makeAgGridCustomHeaderColumn } from 'components/custom-aggrid/utils/custom-aggrid-header-utils'; +import { FROM_COLUMN_TO_FIELD_PCC_MIN } from './pccmin/pcc-min-result.type'; + +export const getPccMinColumns = (intl: IntlShape, onFilter: (filters: any) => void) => { + const sortParams: ColumnContext['sortParams'] = { + table: PCCMIN_ANALYSIS_RESULT_SORT_STORE, + tab: PCCMIN_RESULT, + }; + + const pccMinFilterParams = { + type: AgGridFilterType.PccMin, + tab: PCCMIN_RESULT, + updateFilterCallback: onFilter, + }; + + const createFilterContext = ( + filterDefinition: Pick< + Required['filterComponentParams']['filterParams'], + 'dataType' | 'comparators' + >, + numeric?: boolean, + fractionDigits?: number + ) => ({ + sortParams, + ...pccMinFilterParams, + ...(numeric ? { numeric: true, fractionDigits } : {}), + filterComponent: CustomAggridComparatorFilter, + filterComponentParams: { + filterParams: { + ...filterDefinition, + ...pccMinFilterParams, + }, + }, + }); + + let columnsMeta = [ + { colId: 'busId', headerKey: 'Bus', filterDef: textFilterParams }, + { colId: 'limitingEquipment', headerKey: 'Contingency', filterDef: textFilterParams }, + { + colId: 'pccMinTri', + headerKey: 'PccMinTri', + filterDef: numericFilterParams, + numeric: true, + fractionDigits: 2, + }, + { + colId: 'iccMinTri', + headerKey: 'IccMinTri', + filterDef: numericFilterParams, + numeric: true, + fractionDigits: 2, + }, + { colId: 'x', headerKey: 'xOhm', filterDef: numericFilterParams, numeric: true, fractionDigits: 2 }, + { colId: 'r', headerKey: 'rOhm', filterDef: numericFilterParams, numeric: true, fractionDigits: 2 }, + ]; + + return columnsMeta.map(({ colId, headerKey, filterDef, numeric, fractionDigits }) => + makeAgGridCustomHeaderColumn({ + colId, + field: FROM_COLUMN_TO_FIELD_PCC_MIN[colId], + headerName: intl.formatMessage({ id: headerKey }), + context: createFilterContext(filterDef, numeric, fractionDigits), + minWidth: numeric ? undefined : 180, + }) + ); +}; diff --git a/src/components/results/pccmin/pcc-min-result-tab.tsx b/src/components/results/pccmin/pcc-min-result-tab.tsx new file mode 100644 index 0000000000..f5a50175dc --- /dev/null +++ b/src/components/results/pccmin/pcc-min-result-tab.tsx @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Box, LinearProgress, Tab, Tabs } from '@mui/material'; +import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { ComputationReportViewer } from '../common/computation-report-viewer'; + +import { useSelector } from 'react-redux'; +import { AppState } from '../../../redux/reducer'; +import { ComputingType } from '@gridsuite/commons-ui'; +import { RunningStatus } from '../../utils/running-status'; +import { useOpenLoaderShortWait } from '../../dialogs/commons/handle-loader'; +import { RESULTS_LOADING_DELAY } from '../../network/constants'; +import GlobalFilterSelector from '../common/global-filter/global-filter-selector'; +import { EQUIPMENT_TYPES } from '../../utils/equipment-types'; +import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters'; +import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options'; +import { PccMinResultTabProps } from './pcc-min-result.type'; +import { PccMinResult } from './pcc-min-result'; + +export const PccMinResultTab: FunctionComponent = ({ + studyUuid, + nodeUuid, + currentRootNetworkUuid, +}) => { + const [resultOrLogIndex, setResultOrLogIndex] = useState(0); + + const pccMinStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.PCC_MIN]); + + const RESULTS_TAB_INDEX = 0; + const LOGS_TAB_INDEX = 1; + + const { globalFilters, handleGlobalFilterChange } = useGlobalFilters(); + const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions(); + + const handleSubTabChange = useCallback((event: React.SyntheticEvent, newIndex: number) => { + setResultOrLogIndex(newIndex); + }, []); + + const openLoader = useOpenLoaderShortWait({ + isLoading: pccMinStatus === RunningStatus.RUNNING, + delay: RESULTS_LOADING_DELAY, + }); + + const filterableEquipmentTypes: EQUIPMENT_TYPES[] = useMemo(() => { + return [EQUIPMENT_TYPES.LINE, EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, EQUIPMENT_TYPES.GENERATOR]; + }, []); + + useEffect(() => { + // Clear the globalfilter when tab changes + handleGlobalFilterChange([]); + }, [handleGlobalFilterChange]); + + const globalFilterOptions = useMemo( + () => [...voltageLevelsFilter, ...countriesFilter, ...propertiesFilter], + [voltageLevelsFilter, countriesFilter, propertiesFilter] + ); + + return ( + + + + } /> + } /> + + {resultOrLogIndex === RESULTS_TAB_INDEX && ( + + + + )} + + + + {resultOrLogIndex === RESULTS_TAB_INDEX && ( + + + + )} + + {resultOrLogIndex === LOGS_TAB_INDEX && ( + <> + {openLoader && } + {(pccMinStatus === RunningStatus.SUCCEED || pccMinStatus === RunningStatus.FAILED) && ( + + + + )} + + )} + + + ); +}; diff --git a/src/components/results/pccmin/pcc-min-result-table.tsx b/src/components/results/pccmin/pcc-min-result-table.tsx new file mode 100644 index 0000000000..a30f6f928c --- /dev/null +++ b/src/components/results/pccmin/pcc-min-result-table.tsx @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { FunctionComponent, useMemo, useRef } from 'react'; +import { useIntl } from 'react-intl'; +import { Box } from '@mui/material'; +import { getNoRowsMessage, getRows, useIntlResultStatusMessages } from '../../utils/aggrid-rows-handler'; +import { useSelector } from 'react-redux'; +import { AppState } from '../../../redux/reducer'; +import { DefaultCellRenderer } from '../../custom-aggrid/cell-renderers'; +import { ComputingType } from '@gridsuite/commons-ui'; + +import { PccMinResultTableProps } from './pcc-min-result.type'; +import { AgGridReact } from 'ag-grid-react'; +import { RenderTableAndExportCsv } from 'components/utils/renderTable-ExportCsv'; +import { RESULTS_LOADING_DELAY } from 'components/network/constants'; +import RunningStatus from 'components/utils/running-status'; +import { useOpenLoaderShortWait } from 'components/dialogs/commons/handle-loader'; +import { getPccMinColumns } from '../pcc-min-columns-utils'; + +const PccMinResultTable: FunctionComponent = ({ result, isFetching, onFilter, filters }) => { + const intl = useIntl(); + const pccMinStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.PCC_MIN]); + const gridRef = useRef(null); + + const columns = useMemo(() => getPccMinColumns(intl, onFilter), [intl, onFilter]); + + const statusMessage = useIntlResultStatusMessages(intl, true, filters.length > 0); + + const pccMinDefaultColDef = useMemo( + () => ({ + suppressMovable: true, + resizable: true, + flex: 1, + cellRenderer: DefaultCellRenderer, + }), + [] + ); + + const rowsToShow = getRows(result, pccMinStatus); + const noRowMessage = getNoRowsMessage(statusMessage, result, pccMinStatus, !isFetching); + + const openPccMinLoader = useOpenLoaderShortWait({ + isLoading: pccMinStatus === RunningStatus.RUNNING || isFetching, + delay: RESULTS_LOADING_DELAY, + }); + return ( + + + + + + ); +}; + +export default PccMinResultTable; diff --git a/src/components/results/pccmin/pcc-min-result.tsx b/src/components/results/pccmin/pcc-min-result.tsx new file mode 100644 index 0000000000..7006956bdf --- /dev/null +++ b/src/components/results/pccmin/pcc-min-result.tsx @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { useSelector } from 'react-redux'; +import { AppState } from 'redux/reducer'; +import { FunctionComponent, useCallback, useEffect, useState } from 'react'; +import { ComputingType, useSnackMessage } from '@gridsuite/commons-ui'; +import { GlobalFilters } from '../common/global-filter/global-filter-types'; +import { FROM_COLUMN_TO_FIELD_PCC_MIN, PagedPccMinResults, SinglePccMinResultInfos } from './pcc-min-result.type'; +import { useIntl } from 'react-intl'; +import { useFilterSelector } from 'hooks/use-filter-selector'; +import { usePaginationSelector } from 'hooks/use-pagination-selector'; +import RunningStatus from 'components/utils/running-status'; +import { mapFieldsToColumnsFilter } from 'utils/aggrid-headers-utils'; +import { Box } from '@mui/material'; +import { PAGE_OPTIONS } from '../securityanalysis/security-analysis-result-utils'; +import CustomTablePagination from 'components/utils/custom-table-pagination'; +import PccMinResultTable from './pcc-min-result-table'; +import { FilterType, PaginationType, PccminTab } from 'types/custom-aggrid-types'; +import { PCCMIN_ANALYSIS_RESULT_SORT_STORE, PCCMIN_RESULT } from 'utils/store-sort-filter-fields'; +import { fetchPccMinPagedResults } from 'services/study/pcc-min'; +import { UUID } from 'node:crypto'; + +interface PccMinResultProps { + studyUuid: UUID; + nodeUuid: UUID; + currentRootNetworkUuid: UUID; + globalFilters?: GlobalFilters; + customTablePaginationProps: any; +} + +export const PccMinResult: FunctionComponent = ({ + studyUuid, + nodeUuid, + currentRootNetworkUuid, + customTablePaginationProps, + globalFilters, +}) => { + const pccMinStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.PCC_MIN]); + const { snackError } = useSnackMessage(); + const intl = useIntl(); + + const [result, setResult] = useState([]); + + const updateResult = useCallback((results: SinglePccMinResultInfos[] | null) => { + setResult(results ?? []); + }, []); + + const [count, setCount] = useState(0); + const [isFetching, setIsFetching] = useState(false); + + const sortConfig = useSelector( + (state: AppState) => state.tableSort[PCCMIN_ANALYSIS_RESULT_SORT_STORE][PCCMIN_RESULT] + ); + + const { filters } = useFilterSelector(FilterType.PccMin, PCCMIN_RESULT); + const { pagination, dispatchPagination } = usePaginationSelector(PaginationType.PccMin, PCCMIN_RESULT as PccminTab); + const { page, rowsPerPage } = pagination; + + const handleChangePage = useCallback( + (_: any, newPage: number) => { + dispatchPagination({ ...pagination, page: newPage }); + }, + [pagination, dispatchPagination] + ); + + const handleChangeRowsPerPage = useCallback( + (event: any) => { + const newRowsPerPage = Number.parseInt(event.target.value, 10); + dispatchPagination({ page: 0, rowsPerPage: newRowsPerPage }); + }, + [dispatchPagination] + ); + + const memoizedSetPageCallback = useCallback(() => { + dispatchPagination({ ...pagination, page: 0 }); + }, [pagination, dispatchPagination]); + + useEffect(() => { + if (pccMinStatus !== RunningStatus.SUCCEED) { + return; + } + if (!currentRootNetworkUuid) { + return; + } + let isMounted = true; // prevents state updates if the component has unmounted + setIsFetching(true); + updateResult(null); + + const selector = { + page, + size: rowsPerPage as number, + filter: filters ? mapFieldsToColumnsFilter(filters, FROM_COLUMN_TO_FIELD_PCC_MIN) : null, + sort: sortConfig, + }; + + fetchPccMinPagedResults({ + studyUuid, + currentNodeUuid: nodeUuid, + currentRootNetworkUuid, + selector, + globalFilters, + }) + .then((result: PagedPccMinResults | null) => { + if (isMounted) { + const { content = [], totalElements = 0 } = result || {}; + updateResult(content); + setCount(totalElements); + } + }) + .catch((error) => + snackError({ + messageTxt: error.message, + headerId: 'PccMinResultsError', + }) + ) + .finally(() => { + if (isMounted) { + setIsFetching(false); + } + }); + + return () => { + isMounted = false; + }; + }, [ + page, + rowsPerPage, + snackError, + pccMinStatus, + updateResult, + studyUuid, + nodeUuid, + currentRootNetworkUuid, + intl, + filters, + sortConfig, + globalFilters, + ]); + + return ( + + + + + ); +}; diff --git a/src/components/results/pccmin/pcc-min-result.type.ts b/src/components/results/pccmin/pcc-min-result.type.ts new file mode 100644 index 0000000000..8391606c00 --- /dev/null +++ b/src/components/results/pccmin/pcc-min-result.type.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import type { UUID } from 'node:crypto'; +import { FilterConfig } from 'types/custom-aggrid-types'; +import { GlobalFilters } from '../common/global-filter/global-filter-types'; +import { Page, Selector } from '../common/utils'; + +export interface SinglePccMinResultInfos { + singlePccMinResultUuid: string; + busId: string; + pccMinTri: number; + iccMinTri: number; + limitingEquipment: string; + x: number; + r: number; +} + +export interface PccMinResultTabProps { + studyUuid: UUID; + nodeUuid: UUID; + currentRootNetworkUuid: UUID; +} +export type PagedPccMinResults = Page; + +export interface PccMinResultTableProps { + result: SinglePccMinResultInfos[]; + isFetching: boolean; + onFilter: () => void; + filters: FilterConfig[]; +} + +interface PccMinResults { + studyUuid: UUID | null; + currentNodeUuid?: UUID; + currentRootNetworkUuid?: UUID; + globalFilters?: GlobalFilters; +} + +export interface PccMinPagedResults extends PccMinResults { + selector: Partial; +} + +export const FROM_COLUMN_TO_FIELD_PCC_MIN: Record = { + busId: 'busId', + pccMinTri: 'pccMinTri', + iccMinTri: 'iccMinTri', + limitingEquipment: 'limitingEquipment', + x: 'x', + r: 'r', +}; diff --git a/src/components/results/securityanalysis/security-analysis.type.ts b/src/components/results/securityanalysis/security-analysis.type.ts index d3f4ae8db3..57a3b38564 100644 --- a/src/components/results/securityanalysis/security-analysis.type.ts +++ b/src/components/results/securityanalysis/security-analysis.type.ts @@ -12,6 +12,7 @@ import type { UUID } from 'node:crypto'; import { FilterConfig, SortConfig } from '../../../types/custom-aggrid-types'; import { TablePaginationProps } from '@mui/material'; import { GlobalFilters } from '../common/global-filter/global-filter-types'; +import { Page } from '../common/utils'; export interface LimitViolation { subjectId?: string; @@ -90,36 +91,11 @@ export interface PreContingencyResult { export type QueryParamsType = Record; -type Sort = { - empty?: boolean; - sorted?: boolean; - unsorted?: boolean; -}; - -type Pageable = { - offset?: number; - pageNumber?: number; - pageSize?: number; - paged?: boolean; - sort?: Sort; - unpaged?: boolean; -}; - export type SubjectIdRendererType = (cellData: ICellRendererParams) => React.JSX.Element | undefined; -export interface SecurityAnalysisNmkResult { - content?: ContingenciesFromConstraintItem[] | ConstraintsFromContingencyItem[] | null; - empty?: boolean; - first?: boolean; - last?: boolean; - number?: number; - numberOfElements?: number; - pageable?: Pageable; - size?: number; - sort?: Sort; - totalElements?: number; - totalPages?: number; -} +export type SecurityAnalysisNmkResult = Page< + ContingenciesFromConstraintItem[] | ConstraintsFromContingencyItem[] | null +>; // Components props interfaces export interface SecurityAnalysisTabProps { diff --git a/src/components/results/shortcircuit/shortcircuit-analysis-result-table.tsx b/src/components/results/shortcircuit/shortcircuit-analysis-result-table.tsx index a09f1c43d2..764621fc79 100644 --- a/src/components/results/shortcircuit/shortcircuit-analysis-result-table.tsx +++ b/src/components/results/shortcircuit/shortcircuit-analysis-result-table.tsx @@ -26,14 +26,17 @@ import { convertSide } from '../loadflow/load-flow-result-utils'; import { CustomAggridComparatorFilter } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-comparator-filter'; import { CustomAggridAutocompleteFilter } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-autocomplete-filter'; import { SHORTCIRCUIT_ANALYSIS_RESULT_SORT_STORE } from '../../../utils/store-sort-filter-fields'; -import { FilterType as AgGridFilterType, FilterConfig } from '../../../types/custom-aggrid-types'; +import { + FilterType as AgGridFilterType, + FilterConfig, + numericFilterParams, + textFilterParams, +} from '../../../types/custom-aggrid-types'; import { mappingTabs } from './shortcircuit-analysis-result-content'; import { resultsStyles } from '../common/utils'; import { ColumnContext, FILTER_DATA_TYPES, - FILTER_NUMBER_COMPARATORS, - FILTER_TEXT_COMPARATORS, FilterEnumsType, } from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type'; import { AGGRID_LOCALES } from '../../../translations/not-intl/aggrid-locales'; @@ -143,16 +146,6 @@ const ShortCircuitAnalysisResultTable: FunctionComponent['filterComponentParams']['filterParams'], diff --git a/src/components/results/shortcircuit/shortcircuit-analysis-result.type.ts b/src/components/results/shortcircuit/shortcircuit-analysis-result.type.ts index 87559418f4..9861dfabf4 100644 --- a/src/components/results/shortcircuit/shortcircuit-analysis-result.type.ts +++ b/src/components/results/shortcircuit/shortcircuit-analysis-result.type.ts @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { Page } from '../common/utils'; + export interface SCAFault { id: string; elementId: string; @@ -44,19 +46,6 @@ export interface SCAFaultResult { feederResults?: SCAFeederResult[]; } -type Pageable = { - sort: { - sorted: boolean; - empty: boolean; - unsorted: boolean; - }; - pageNumber: number; - pageSize: number; - offset: number; - paged: boolean; - unpaged: boolean; -}; - export type SCAResult = { resultUuid: string; writeTimeStamp: any; @@ -65,24 +54,6 @@ export type SCAResult = { export type SCAPagedResults = Page | Page; -interface Page { - content: ResultType[]; - pageable: Pageable; - last: boolean; - totalPages: number; - totalElements: number; - first: boolean; - size: number; - number: number; - sort: { - sorted: boolean; - empty: boolean; - unsorted: boolean; - }; - numberOfElements: number; - empty: boolean; -} - export enum ShortCircuitAnalysisResultTabs { ALL_BUSES = 0, ONE_BUS = 1, diff --git a/src/components/results/stateestimation/state-estimation-quality-result.tsx b/src/components/results/stateestimation/state-estimation-quality-result.tsx index 1af445d926..44b5877059 100644 --- a/src/components/results/stateestimation/state-estimation-quality-result.tsx +++ b/src/components/results/stateestimation/state-estimation-quality-result.tsx @@ -111,8 +111,6 @@ export const StateEstimationQualityResult: FunctionComponent void; - onGridReady: ((event: GridReadyEvent) => void) | undefined; - getRowStyle: (params: RowClassParams) => RowStyle | undefined; + showLinearProgress?: boolean; + getRowStyle?: (params: RowClassParams) => RowStyle | undefined; overlayNoRowsTemplate: string | undefined; skipColumnHeaders: boolean; } @@ -49,15 +48,21 @@ export const RenderTableAndExportCsv: FunctionComponent { const isRowsEmpty = !rows || rows.length === 0; const language = useSelector((state: AppState) => state.computedLanguage); - + const onRowDataUpdated = useCallback((params: any) => { + if (params.api) { + params.api.sizeColumnsToFit(); + } + }, []); + const onGridReady = useCallback(({ api }: GridReadyEvent) => { + api?.sizeColumnsToFit(); + }, []); return ( @@ -71,6 +76,9 @@ export const RenderTableAndExportCsv: FunctionComponent gridRef.current?.api?.exportDataAsCsv(params)} /> + + {showLinearProgress && } + {rows && ( = ({ }), [] ); - const onRowDataUpdated = useCallback((params: any) => { - if (params.api) { - params.api.sizeColumnsToFit(); - } - }, []); - - const onGridReady = useCallback(({ api }: GridReadyEvent) => { - api?.sizeColumnsToFit(); - }, []); const applyModifications = () => { setApplyingModifications(true); @@ -289,8 +280,6 @@ export const VoltageInitResult: FunctionComponent = ({ defaultColDef={defaultColDef} tableName={intl.formatMessage({ id: 'Indicators' })} rows={rows as any[]} - onRowDataUpdated={onRowDataUpdated} - onGridReady={onGridReady} skipColumnHeaders={false} getRowStyle={function (_params: RowClassParams): RowStyle | undefined { return undefined; @@ -330,8 +319,6 @@ export const VoltageInitResult: FunctionComponent = ({ defaultColDef={defaultColDef} tableName={intl.formatMessage({ id: 'ReactiveSlacks' })} rows={result.reactiveSlacks} - onRowDataUpdated={onRowDataUpdated} - onGridReady={onGridReady} skipColumnHeaders={false} getRowStyle={function (_params: RowClassParams): RowStyle | undefined { return undefined; @@ -381,8 +368,6 @@ export const VoltageInitResult: FunctionComponent = ({ defaultColDef={defaultColDef} tableName={intl.formatMessage({ id: 'BusVoltages' })} rows={busVoltages} - onRowDataUpdated={onRowDataUpdated} - onGridReady={onGridReady} skipColumnHeaders={false} getRowStyle={function (_params: RowClassParams): RowStyle | undefined { return undefined; diff --git a/src/hooks/use-computation-results-count.ts b/src/hooks/use-computation-results-count.ts index 3a36489787..6f6499f66a 100644 --- a/src/hooks/use-computation-results-count.ts +++ b/src/hooks/use-computation-results-count.ts @@ -50,6 +50,7 @@ export const useComputationResultsCount = () => { const stateEstimationStatus = useSelector( (state: AppState) => state.computingStatus[ComputingType.STATE_ESTIMATION] ); + const pccMinStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.PCC_MIN]); const [enableDeveloperMode] = useParameterState(PARAM_DEVELOPER_MODE); @@ -79,6 +80,9 @@ export const useComputationResultsCount = () => { enableDeveloperMode && (stateEstimationStatus === RunningStatus.SUCCEED || stateEstimationStatus === RunningStatus.FAILED); // Can be failed for technical reasons (e.g., server not responding or computation divergence) + const pccMinResultPresent = + enableDeveloperMode && (pccMinStatus === RunningStatus.SUCCEED || pccMinStatus === RunningStatus.FAILED); // Can be failed for technical reasons (e.g., server not responding or computation divergence) + return [ loadflowResultPresent, saResutPresent, @@ -89,5 +93,6 @@ export const useComputationResultsCount = () => { dynamicSimulationResultPresent, dynamicSecurityAnalysisResultPresent, stateEstimationResultPresent, + pccMinResultPresent, ].filter(Boolean).length; }; diff --git a/src/hooks/use-filter-selector.ts b/src/hooks/use-filter-selector.ts index a643275292..57af2891aa 100644 --- a/src/hooks/use-filter-selector.ts +++ b/src/hooks/use-filter-selector.ts @@ -9,6 +9,7 @@ import { DYNAMIC_SIMULATION_RESULT_STORE_FIELD, LOADFLOW_RESULT_STORE_FIELD, LOGS_STORE_FIELD, + PCCMIN_ANALYSIS_RESULT_STORE_FIELD, SECURITY_ANALYSIS_RESULT_STORE_FIELD, SENSITIVITY_ANALYSIS_RESULT_STORE_FIELD, SHORTCIRCUIT_ANALYSIS_RESULT_STORE_FIELD, @@ -19,6 +20,7 @@ import { setDynamicSimulationResultFilter, setLoadflowResultFilter, setLogsFilter, + setPccminAnalysisResultFilter, setSecurityAnalysisResultFilter, setSensitivityAnalysisResultFilter, setShortcircuitAnalysisResultFilter, @@ -65,6 +67,10 @@ const FILTER_PARAMS: Record< filterType: STATEESTIMATION_RESULT_STORE_FIELD, filterStoreAction: setStateEstimationResultFilter, }, + [FilterType.PccMin]: { + filterType: PCCMIN_ANALYSIS_RESULT_STORE_FIELD, + filterStoreAction: setPccminAnalysisResultFilter, + }, }; export const useFilterSelector = (filterType: FilterType, filterTab: string) => { diff --git a/src/hooks/use-pagination-selector.ts b/src/hooks/use-pagination-selector.ts index ad588e1a07..8cdf13769b 100644 --- a/src/hooks/use-pagination-selector.ts +++ b/src/hooks/use-pagination-selector.ts @@ -10,19 +10,23 @@ import { PaginationConfig, PaginationTab, PaginationType, + PccminTab, SecurityAnalysisTab, SensitivityAnalysisTab, ShortcircuitAnalysisTab, } from '../types/custom-aggrid-types'; import { + resetPccminAnalysisPagination, resetSecurityAnalysisPagination, resetSensitivityAnalysisPagination, resetShortcircuitAnalysisPagination, + setPccminAnalysisResultPagination, setSecurityAnalysisResultPagination, setSensitivityAnalysisResultPagination, setShortcircuitAnalysisResultPagination, } from '../redux/actions'; import { + PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD, SECURITY_ANALYSIS_PAGINATION_STORE_FIELD, SENSITIVITY_ANALYSIS_PAGINATION_STORE_FIELD, SHORTCIRCUIT_ANALYSIS_PAGINATION_STORE_FIELD, @@ -44,6 +48,10 @@ function createPaginationSelector(paginationType: PaginationType, paginationTab: const paginationState = state[SHORTCIRCUIT_ANALYSIS_PAGINATION_STORE_FIELD]; return paginationState[paginationTab as ShortcircuitAnalysisTab] || DEFAULT_PAGINATION; } + case PaginationType.PccMin: { + const paginationState = state[PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]; + return paginationState[paginationTab as PccminTab] || DEFAULT_PAGINATION; + } default: return DEFAULT_PAGINATION; } @@ -61,6 +69,9 @@ function createPaginationDispatcher(paginationType: PaginationType, paginationTa case PaginationType.ShortcircuitAnalysis: return (pagination: PaginationConfig) => setShortcircuitAnalysisResultPagination(paginationTab as ShortcircuitAnalysisTab, pagination); + case PaginationType.PccMin: + return (pagination: PaginationConfig) => + setPccminAnalysisResultPagination(paginationTab as PccminTab, pagination); default: throw new Error(`Unknown pagination type: ${paginationType}`); } @@ -87,6 +98,7 @@ const PAGINATION_RESET_DISPATCHERS = { [PaginationType.SecurityAnalysis]: resetSecurityAnalysisPagination, [PaginationType.SensitivityAnalysis]: resetSensitivityAnalysisPagination, [PaginationType.ShortcircuitAnalysis]: resetShortcircuitAnalysisPagination, + [PaginationType.PccMin]: resetPccminAnalysisPagination, } as const; export const usePaginationReset = (paginationType: PaginationType) => { diff --git a/src/redux/actions.ts b/src/redux/actions.ts index e3a00d3970..935d3ccd69 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -44,6 +44,8 @@ import { LOADFLOW_RESULT_STORE_FIELD, LOGS_PAGINATION_STORE_FIELD, LOGS_STORE_FIELD, + PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD, + PCCMIN_ANALYSIS_RESULT_STORE_FIELD, SECURITY_ANALYSIS_PAGINATION_STORE_FIELD, SECURITY_ANALYSIS_RESULT_STORE_FIELD, SENSITIVITY_ANALYSIS_PAGINATION_STORE_FIELD, @@ -67,6 +69,7 @@ import { FilterConfig, LogsPaginationConfig, PaginationConfig, + PccminTab, SecurityAnalysisTab, SensitivityAnalysisTab, ShortcircuitAnalysisTab, @@ -1092,6 +1095,23 @@ export function setShortcircuitAnalysisResultFilter( }; } +export const PCCMIN_ANALYSIS_RESULT_FILTER = 'PCCMIN_ANALYSIS_RESULT_FILTER'; +export type PccminAnalysisResultFilterAction = Readonly> & { + filterTab: keyof AppState[typeof PCCMIN_ANALYSIS_RESULT_STORE_FIELD]; + [PCCMIN_ANALYSIS_RESULT_STORE_FIELD]: FilterConfig[]; +}; + +export function setPccminAnalysisResultFilter( + filterTab: keyof AppState[typeof PCCMIN_ANALYSIS_RESULT_STORE_FIELD], + pccminAnalysisResultFilter: FilterConfig[] +): PccminAnalysisResultFilterAction { + return { + type: PCCMIN_ANALYSIS_RESULT_FILTER, + filterTab: filterTab, + [PCCMIN_ANALYSIS_RESULT_STORE_FIELD]: pccminAnalysisResultFilter, + }; +} + export const DYNAMIC_SIMULATION_RESULT_FILTER = 'DYNAMIC_SIMULATION_RESULT_FILTER'; export type DynamicSimulationResultFilterAction = Readonly> & { filterTab: keyof AppState[typeof DYNAMIC_SIMULATION_RESULT_STORE_FIELD]; @@ -1191,6 +1211,32 @@ export function resetShortcircuitAnalysisPagination(): ResetShortcircuitAnalysis }; } +export const RESET_PCCMIN_ANALYSIS_PAGINATION = 'RESET_PCCMIN_ANALYSIS_PAGINATION'; +export type ResetPccminAnalysisPaginationAction = Readonly>; + +export function resetPccminAnalysisPagination(): ResetPccminAnalysisPaginationAction { + return { + type: RESET_PCCMIN_ANALYSIS_PAGINATION, + }; +} + +export const PCCMIN_ANALYSIS_RESULT_PAGINATION = 'PCCMIN_ANALYSIS_RESULT_PAGINATION'; +export type PccminAnalysisResultPaginationAction = Readonly> & { + paginationTab: PccminTab; + [PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]: PaginationConfig; +}; + +export function setPccminAnalysisResultPagination( + paginationTab: PccminTab, + pccminAnalysisPagination: PaginationConfig +): PccminAnalysisResultPaginationAction { + return { + type: PCCMIN_ANALYSIS_RESULT_PAGINATION, + paginationTab: paginationTab, + [PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]: pccminAnalysisPagination, + }; +} + export const SPREADSHEET_FILTER = 'SPREADSHEET_FILTER'; export type SpreadsheetFilterAction = Readonly> & { filterTab: keyof AppState[typeof SPREADSHEET_STORE_FIELD]; diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 41183592aa..c0925412fb 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -134,6 +134,7 @@ import { RESET_SECURITY_ANALYSIS_PAGINATION, RESET_SENSITIVITY_ANALYSIS_PAGINATION, RESET_SHORTCIRCUIT_ANALYSIS_PAGINATION, + RESET_PCCMIN_ANALYSIS_PAGINATION, type ResetAllSpreadsheetGlobalFiltersAction, type ResetDiagramEventAction, type ResetEquipmentsAction, @@ -145,6 +146,7 @@ import { ResetSecurityAnalysisPaginationAction, ResetSensitivityAnalysisPaginationAction, ResetShortcircuitAnalysisPaginationAction, + ResetPccminAnalysisPaginationAction, SAVE_SPREADSHEET_GS_FILTER, type SaveSpreadSheetGlobalFilterAction, SECURITY_ANALYSIS_RESULT_FILTER, @@ -213,6 +215,10 @@ import { SHORTCIRCUIT_ANALYSIS_RESULT_PAGINATION, type ShortcircuitAnalysisResultFilterAction, ShortcircuitAnalysisResultPaginationAction, + PCCMIN_ANALYSIS_RESULT_FILTER, + PCCMIN_ANALYSIS_RESULT_PAGINATION, + type PccminAnalysisResultFilterAction, + PccminAnalysisResultPaginationAction, SPREADSHEET_FILTER, type SpreadsheetFilterAction, STATEESTIMATION_RESULT_FILTER, @@ -278,6 +284,10 @@ import { LOGS_PAGINATION_STORE_FIELD, LOGS_STORE_FIELD, ONE_BUS, + PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD, + PCCMIN_ANALYSIS_RESULT_SORT_STORE, + PCCMIN_ANALYSIS_RESULT_STORE_FIELD, + PCCMIN_RESULT, SECURITY_ANALYSIS_PAGINATION_STORE_FIELD, SECURITY_ANALYSIS_RESULT_N, SECURITY_ANALYSIS_RESULT_N_K, @@ -328,6 +338,8 @@ import { FilterConfig, LogsPaginationConfig, PaginationConfig, + PCCMIN_ANALYSIS_TABS, + PccminTab, SECURITY_ANALYSIS_TABS, SecurityAnalysisTab, SENSITIVITY_ANALYSIS_TABS, @@ -466,6 +478,7 @@ export type TableSort = { [DYNAMIC_SIMULATION_RESULT_SORT_STORE]: TableSortConfig; [SHORTCIRCUIT_ANALYSIS_RESULT_SORT_STORE]: TableSortConfig; [STATEESTIMATION_RESULT_SORT_STORE]: TableSortConfig; + [PCCMIN_ANALYSIS_RESULT_SORT_STORE]: TableSortConfig; }; export type TableSortKeysType = keyof TableSort; @@ -636,6 +649,9 @@ export interface AppState extends CommonStoreState, AppConfigState { [ONE_BUS]: FilterConfig[]; [ALL_BUSES]: FilterConfig[]; }; + [PCCMIN_ANALYSIS_RESULT_STORE_FIELD]: { + [PCCMIN_RESULT]: FilterConfig[]; + }; [DYNAMIC_SIMULATION_RESULT_STORE_FIELD]: { [TIMELINE]: FilterConfig[]; }; @@ -646,6 +662,8 @@ export interface AppState extends CommonStoreState, AppConfigState { [SECURITY_ANALYSIS_PAGINATION_STORE_FIELD]: Record; [SENSITIVITY_ANALYSIS_PAGINATION_STORE_FIELD]: Record; [SHORTCIRCUIT_ANALYSIS_PAGINATION_STORE_FIELD]: Record; + [PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]: Record; + [SPREADSHEET_STORE_FIELD]: SpreadsheetFilterState; [LOGS_STORE_FIELD]: LogsFilterState; @@ -865,6 +883,10 @@ const initialState: AppState = { [ONE_BUS]: [], [ALL_BUSES]: [], }, + + [PCCMIN_ANALYSIS_RESULT_STORE_FIELD]: { + [PCCMIN_RESULT]: [], + }, [DYNAMIC_SIMULATION_RESULT_STORE_FIELD]: { [TIMELINE]: [], }, @@ -884,6 +906,9 @@ const initialState: AppState = { [ONE_BUS]: { ...DEFAULT_PAGINATION }, [ALL_BUSES]: { ...DEFAULT_PAGINATION }, }, + [PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]: { + [PCCMIN_RESULT]: { ...DEFAULT_PAGINATION }, + }, [STATEESTIMATION_RESULT_STORE_FIELD]: { [STATEESTIMATION_QUALITY_CRITERION]: [], [STATEESTIMATION_QUALITY_PER_REGION]: [], @@ -946,6 +971,9 @@ const initialState: AppState = { [ONE_BUS]: [{ colId: 'current', sort: SortWay.DESC }], [ALL_BUSES]: [{ colId: 'elementId', sort: SortWay.ASC }], }, + [PCCMIN_ANALYSIS_RESULT_SORT_STORE]: { + [PCCMIN_RESULT]: [{ colId: 'busId', sort: SortWay.ASC }], + }, [STATEESTIMATION_RESULT_SORT_STORE]: { [STATEESTIMATION_QUALITY_CRITERION]: [ { @@ -1802,6 +1830,9 @@ export const reducer = createReducer(initialState, (builder) => { action[SHORTCIRCUIT_ANALYSIS_RESULT_STORE_FIELD]; }); + builder.addCase(PCCMIN_ANALYSIS_RESULT_FILTER, (state, action: PccminAnalysisResultFilterAction) => { + state[PCCMIN_ANALYSIS_RESULT_STORE_FIELD][action.filterTab] = action[PCCMIN_ANALYSIS_RESULT_STORE_FIELD]; + }); builder.addCase(DYNAMIC_SIMULATION_RESULT_FILTER, (state, action: DynamicSimulationResultFilterAction) => { state[DYNAMIC_SIMULATION_RESULT_STORE_FIELD][action.filterTab] = action[DYNAMIC_SIMULATION_RESULT_STORE_FIELD]; }); @@ -1869,6 +1900,21 @@ export const reducer = createReducer(initialState, (builder) => { }); } ); + builder.addCase(PCCMIN_ANALYSIS_RESULT_PAGINATION, (state, action: PccminAnalysisResultPaginationAction) => { + state[PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD][action.paginationTab] = + action[PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD]; + }); + + builder.addCase(RESET_PCCMIN_ANALYSIS_PAGINATION, (state, _action: ResetPccminAnalysisPaginationAction) => { + // Reset all shortcircuit analysis tabs to page 0 but keep their rowsPerPage + PCCMIN_ANALYSIS_TABS.forEach((tab) => { + const currentPagination = state[PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD][tab]; + state[PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD][tab] = { + page: 0, + rowsPerPage: currentPagination.rowsPerPage, + }; + }); + }); builder.addCase(SPREADSHEET_FILTER, (state, action: SpreadsheetFilterAction) => { state[SPREADSHEET_STORE_FIELD][action.filterTab] = action[SPREADSHEET_STORE_FIELD]; diff --git a/src/services/study/pcc-min.ts b/src/services/study/pcc-min.ts index 5121579dc9..a057822ea6 100644 --- a/src/services/study/pcc-min.ts +++ b/src/services/study/pcc-min.ts @@ -6,42 +6,81 @@ */ import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index'; -import { backendFetch, backendFetchText } from '@gridsuite/commons-ui'; -import type { UUID } from 'node:crypto'; +import { backendFetch, backendFetchJson, backendFetchText } from '@gridsuite/commons-ui'; +import { PccMinPagedResults } from 'components/results/pccmin/pcc-min-result.type'; +import { UUID } from 'node:crypto'; export function startPccMin(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID): Promise { console.info( `Running pcc min on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` ); - const url = + const startPccminUrl = getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + '/pcc-min/run'; - console.debug(url); - return backendFetch(url, { method: 'post' }); + console.debug(startPccminUrl); + return backendFetch(startPccminUrl, { method: 'post' }); } export function stopPccMin(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info( `Stopping pcc min on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` ); - const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + const stopPccminUrl = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, currentNodeUuid, currentRootNetworkUuid )}/pcc-min/stop`; - console.debug(url); - return backendFetch(url, { method: 'put' }); + console.debug(stopPccminUrl); + return backendFetch(stopPccminUrl, { method: 'put' }); } export function fetchPccMinStatus(studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID) { console.info( `Fetching pcc min status on ${studyUuid} on root network '${currentRootNetworkUuid}' and node ${currentNodeUuid} ...` ); - const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( + const statusUrl = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, currentNodeUuid, currentRootNetworkUuid )}/pcc-min/status`; - console.debug(url); - return backendFetchText(url); + console.debug(statusUrl); + return backendFetchText(statusUrl); +} + +export function fetchPccMinPagedResults({ + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + selector = {}, + globalFilters, +}: PccMinPagedResults) { + console.info( + `Fetching pcc min result on '${studyUuid}' , node '${currentNodeUuid}' and root network '${currentRootNetworkUuid}'...` + ); + + const urlSearchParams = new URLSearchParams(); + + const { page = 0, sort, size, filter } = selector; + + urlSearchParams.append('page', String(page)); + + sort?.map((value) => urlSearchParams.append('sort', `${value.colId},${value.sort}`)); + + if (size) { + urlSearchParams.append('size', String(size)); + } + if (globalFilters && Object.keys(globalFilters).length > 0) { + urlSearchParams.append('globalFilters', JSON.stringify(globalFilters)); + } + + if (filter?.length) { + urlSearchParams.append('filters', JSON.stringify(filter)); + } + + const resultsUrl = + getStudyUrlWithNodeUuidAndRootNetworkUuid(studyUuid, currentNodeUuid, currentRootNetworkUuid) + + '/pcc-min/result?' + + urlSearchParams.toString(); + console.debug(resultsUrl); + return backendFetchJson(resultsUrl); } diff --git a/src/services/study/short-circuit-analysis.ts b/src/services/study/short-circuit-analysis.ts index b97091ff68..b293dcba63 100644 --- a/src/services/study/short-circuit-analysis.ts +++ b/src/services/study/short-circuit-analysis.ts @@ -12,8 +12,8 @@ import { } from '../../components/results/shortcircuit/shortcircuit-analysis-result.type'; import { GsLang, backendFetch, backendFetchJson, backendFetchText } from '@gridsuite/commons-ui'; import type { UUID } from 'node:crypto'; -import { FilterConfig, SortConfig } from '../../types/custom-aggrid-types'; import { GlobalFilters } from '../../components/results/common/global-filter/global-filter-types'; +import { Selector } from 'components/results/common/utils'; interface ShortCircuitAnalysisResult { studyUuid: UUID | null; @@ -22,12 +22,6 @@ interface ShortCircuitAnalysisResult { type: ShortCircuitAnalysisType; globalFilters?: GlobalFilters; } -interface Selector { - page: number; - size: number; - filter: FilterConfig[] | null; - sort: SortConfig[]; -} interface ShortCircuitAnalysisPagedResults extends ShortCircuitAnalysisResult { selector: Partial; } diff --git a/src/translations/en.json b/src/translations/en.json index ec999c60eb..eb88fa7939 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1398,6 +1398,7 @@ "PccMin": "Pcc min N-1", "startPccMinError": "An error occurred while launching the pcc min", "pccMinError": "An error occurred when executing the pcc min", + "PccMinResultsError": "An error occurred when exporting pcc min N-1 results", "NoFilter": "No filter", "searchPlaceholder": "Search", "searchPlaceholderLog": "Search in Logs", @@ -1645,5 +1646,9 @@ "equipmentID": "Equipment ID", "voltageLevelNominalVoltageMinValueError": "Nominal voltage must be greater than low voltage limit and less than high voltage limit", "voltageLevelNominalVoltageMaxValueError": "Low voltage limit must be inferior to high voltage limit", - "generatorMinimumActivePowerMaxValueError" : "Minimum active power must be inferior to maximum active power" -} + "generatorMinimumActivePowerMaxValueError" : "Minimum active power must be inferior to maximum active power", + "PccMinTri": "Pcc min (MVA) ", + "IccMinTri": "Icc min (kA)", + "rOhm": "r (Ω)", + "xOhm":"x (Ω)" + } diff --git a/src/translations/fr.json b/src/translations/fr.json index 896db37b83..b4392ee530 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -1396,8 +1396,9 @@ "startStateEstimationError": "Une erreur est survenue lors du lancement de l'estimation d'état", "stateEstimationError": "Une erreur est survenue lors de l'exécution de l'estimation d'état", "PccMin": "Pcc min N-1", - "startPccMinError": "Une erreur est survenue lors du lancement de pccMin", - "pccMinError": "Une erreur est survenue lors de l'exécution de pccMin", + "startPccMinError": "Une erreur est survenue lors du lancement de pcc min N-1", + "pccMinError": "Une erreur est survenue lors de l'exécution de pcc min N-1", + "PccMinResultsError": "Une erreur s'est produite lors de la récupération du résultat de pcc min N-1", "NoFilter": "Aucun filtre", "searchPlaceholder": "Recherche", "searchPlaceholderLog": "Rechercher dans les Logs", @@ -1641,5 +1642,9 @@ "equipmentID": "ID de l'ouvrage", "voltageLevelNominalVoltageMinValueError": "La tension nominale doit être comprise entre la limite de tension basse et la limite de tension haute", "voltageLevelNominalVoltageMaxValueError": "La limite de tension basse doit être inférieure à celle de tension haute", - "generatorMinimumActivePowerMaxValueError" : "La puissance active min doit être inférieure à la puissance active max" + "generatorMinimumActivePowerMaxValueError" : "La puissance active min doit être inférieure à la puissance active max", + "PccMinTri": "Pcc min (MVA) ", + "IccMinTri": "Icc min (kA)", + "rOhm": "r (Ω)", + "xOhm":"x (Ω)" } diff --git a/src/types/custom-aggrid-types.ts b/src/types/custom-aggrid-types.ts index 6ae4a896ea..af5890e19a 100644 --- a/src/types/custom-aggrid-types.ts +++ b/src/types/custom-aggrid-types.ts @@ -5,9 +5,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { GridApi } from 'ag-grid-community'; +import { + FILTER_DATA_TYPES, + FILTER_NUMBER_COMPARATORS, + FILTER_TEXT_COMPARATORS, +} from 'components/custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type'; import { ALL_BUSES, ONE_BUS, + PCCMIN_RESULT, SECURITY_ANALYSIS_RESULT_N, SECURITY_ANALYSIS_RESULT_N_K, SENSITIVITY_AT_NODE_N, @@ -38,6 +44,7 @@ export enum FilterType { Spreadsheet = 'Spreadsheet', Logs = 'Logs', StateEstimation = 'StateEstimation', + PccMin = 'PccMin', } export type FilterData = { @@ -71,10 +78,21 @@ export type LogsPaginationConfig = { rowsPerPage: number; }; +export const textFilterParams = { + dataType: FILTER_DATA_TYPES.TEXT, + comparators: [FILTER_TEXT_COMPARATORS.STARTS_WITH, FILTER_TEXT_COMPARATORS.CONTAINS], +}; + +export const numericFilterParams = { + dataType: FILTER_DATA_TYPES.NUMBER, + comparators: Object.values(FILTER_NUMBER_COMPARATORS), +}; + export enum PaginationType { SecurityAnalysis = 'SecurityAnalysis', SensitivityAnalysis = 'SensitivityAnalysis', ShortcircuitAnalysis = 'ShortcircuitAnalysis', + PccMin = 'PccMin', } export const SECURITY_ANALYSIS_TABS = [SECURITY_ANALYSIS_RESULT_N, SECURITY_ANALYSIS_RESULT_N_K] as const; @@ -89,9 +107,11 @@ export const SENSITIVITY_ANALYSIS_TABS = [ ] as const; export const SHORTCIRCUIT_ANALYSIS_TABS = [ONE_BUS, ALL_BUSES] as const; +export const PCCMIN_ANALYSIS_TABS = [PCCMIN_RESULT] as const; export type SecurityAnalysisTab = (typeof SECURITY_ANALYSIS_TABS)[number]; export type SensitivityAnalysisTab = (typeof SENSITIVITY_ANALYSIS_TABS)[number]; export type ShortcircuitAnalysisTab = (typeof SHORTCIRCUIT_ANALYSIS_TABS)[number]; +export type PccminTab = (typeof PCCMIN_ANALYSIS_TABS)[number]; -export type PaginationTab = SecurityAnalysisTab | SensitivityAnalysisTab | ShortcircuitAnalysisTab; +export type PaginationTab = SecurityAnalysisTab | SensitivityAnalysisTab | ShortcircuitAnalysisTab | PccminTab; diff --git a/src/utils/store-sort-filter-fields.ts b/src/utils/store-sort-filter-fields.ts index df7fc8f2d9..14d05df798 100644 --- a/src/utils/store-sort-filter-fields.ts +++ b/src/utils/store-sort-filter-fields.ts @@ -59,3 +59,9 @@ export const STATEESTIMATION_RESULT_SORT_STORE = 'stateEstimationResult'; export const STATEESTIMATION_QUALITY_CRITERION = 'stateEstimationQualityCriterion'; export const STATEESTIMATION_QUALITY_PER_REGION = 'stateEstimationQualityPerRegion'; export const STATEESTIMATION_RESULT = 'stateEstimationResult'; + +//pccMin result store fields +export const PCCMIN_ANALYSIS_RESULT_STORE_FIELD = 'pccminAnalysisResultFilter'; +export const PCCMIN_ANALYSIS_RESULT_SORT_STORE = 'pccminAnalysisResult'; +export const PCCMIN_ANALYSIS_PAGINATION_STORE_FIELD = 'pccminAnalysisPagination'; +export const PCCMIN_RESULT = 'pccMinResults';