Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/main/java/org/gridsuite/study/server/StudyException.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ public enum Type {
CREATE_DIAGRAM_GRID_LAYOUT_FAILED,
DUPLICATE_DIAGRAM_GRID_LAYOUT_FAILED,
TOO_MANY_NAD_CONFIGS,
TOO_MANY_MAP_CARDS
TOO_MANY_MAP_CARDS,
SAVE_NAD_CONFIG_FAILED,
DELETE_NAD_CONFIG_FAILED,
}

private final Type type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.gridsuite.study.server.dto.*;
import org.gridsuite.study.server.dto.computation.LoadFlowComputationInfos;
import org.gridsuite.study.server.dto.diagramgridlayout.DiagramGridLayout;
import org.gridsuite.study.server.dto.diagramgridlayout.nad.NadConfigInfos;
import org.gridsuite.study.server.dto.dynamicmapping.MappingInfos;
import org.gridsuite.study.server.dto.dynamicmapping.ModelInfos;
import org.gridsuite.study.server.dto.dynamicsecurityanalysis.DynamicSecurityAnalysisStatus;
Expand Down Expand Up @@ -2522,4 +2523,32 @@ public ResponseEntity<Void> updateSpreadsheetParameters(@PathVariable("studyUuid
@RequestBody final SpreadsheetParameters spreadsheetParameters) {
return (this.studyService.updateSpreadsheetParameters(studyUuid, spreadsheetParameters) ? ResponseEntity.noContent() : ResponseEntity.notFound()).build();
}

@PostMapping(value = "/studies/{studyUuid}/network-area-diagrams/configs", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Save NAD config")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "NAD config is saved"),
@ApiResponse(responseCode = "404", description = "Study does not exist")
})
public ResponseEntity<UUID> saveNadConfig(
@PathVariable("studyUuid") UUID studyUuid,
@RequestBody NadConfigInfos nadConfigData) {
studyService.assertIsStudyExist(studyUuid);
UUID savedUuid = studyService.saveNadConfig(studyUuid, nadConfigData);
return ResponseEntity.ok().body(savedUuid);
}

@DeleteMapping(value = "/studies/{studyUuid}/network-area-diagrams/configs/{nadConfigUuid}")
@Operation(summary = "Delete NAD config")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "NAD config is deleted"),
@ApiResponse(responseCode = "404", description = "Study does not exist")
})
public ResponseEntity<Void> deleteNadConfig(
@PathVariable("studyUuid") UUID studyUuid,
@PathVariable("nadConfigUuid") UUID nadConfigUuid) {
studyService.assertIsStudyExist(studyUuid);
studyService.deleteNadConfig(studyUuid, nadConfigUuid);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ public class StudyEntity extends AbstractManuallyAssignedIdentifierEntity<UUID>
))
private List<NodeAliasEmbeddable> nodeAliases;

@ElementCollection
@CollectionTable(name = "StudyNadConfigs", foreignKey = @ForeignKey(
name = "study_nad_configs_fk"
))
@Column(name = "nad_config_uuid")
@Builder.Default
private List<UUID> nadConfigsUuids = new ArrayList<>();

@Column(name = "mono_root", columnDefinition = "boolean default true")
private boolean monoRoot;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private List<UUID> extractNadConfigUuids(DiagramGridLayout layout) {
private void cleanupOldNadConfigs(List<UUID> oldNadConfigUuidsToDelete) {
if (!oldNadConfigUuidsToDelete.isEmpty()) {
try {
singleLineDiagramService.deleteMultipleDiagramConfigs(oldNadConfigUuidsToDelete);
singleLineDiagramService.deleteDiagramConfigs(oldNadConfigUuidsToDelete);
} catch (Exception e) {
LOGGER.error("Could not clean up old NAD configs: " + oldNadConfigUuidsToDelete, e);
}
Expand Down Expand Up @@ -213,7 +213,7 @@ private DiagramGridLayout processNADLayoutDetails(DiagramGridLayout diagramGridL

// Batch create all NAD configs if any exist
if (!nadConfigsToCreate.isEmpty()) {
singleLineDiagramService.createMultipleDiagramConfigs(nadConfigsToCreate);
singleLineDiagramService.createDiagramConfigs(nadConfigsToCreate);
}

return DiagramGridLayout.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* 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/.
*/
package org.gridsuite.study.server.service;

import lombok.RequiredArgsConstructor;
import org.gridsuite.study.server.StudyException;
import org.gridsuite.study.server.dto.diagramgridlayout.nad.NadConfigInfos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;

import static org.gridsuite.study.server.StudyException.Type.DELETE_NAD_CONFIG_FAILED;
import static org.gridsuite.study.server.StudyException.Type.SAVE_NAD_CONFIG_FAILED;

/**
* @author Ayoub Labidi <ayoub.labidi at rte-france.com>
*/
@Service
@RequiredArgsConstructor
public class NadConfigService {

private static final Logger LOGGER = LoggerFactory.getLogger(NadConfigService.class);

private final SingleLineDiagramService singleLineDiagramService;

public UUID saveNadConfig(NadConfigInfos nadConfigInfos) {

UUID configUuid = nadConfigInfos.getId();

if (configUuid == null) {
// Create new config
UUID newUuid = UUID.randomUUID();
nadConfigInfos.setId(newUuid);
try {
singleLineDiagramService.createDiagramConfigs(List.of(nadConfigInfos));
} catch (Exception e) {
throw new StudyException(SAVE_NAD_CONFIG_FAILED, "Could not create NAD config: " + newUuid);
}
return newUuid;
} else {
// Update existing config
try {
singleLineDiagramService.updateNadConfig(nadConfigInfos);
} catch (Exception e) {
throw new StudyException(SAVE_NAD_CONFIG_FAILED, "Could not update NAD config: " + configUuid);
}
return configUuid;
}
}

public void deleteNadConfig(UUID configUuid) {
if (configUuid == null) {
return;
}

try {
singleLineDiagramService.deleteDiagramConfigs(List.of(configUuid));
} catch (Exception e) {
throw new StudyException(DELETE_NAD_CONFIG_FAILED, "Could not delete NAD config: " + configUuid);
}
}

public void deleteNadConfigs(List<UUID> nadConfigUuids) {
if (nadConfigUuids == null || nadConfigUuids.isEmpty()) {
return;
}

try {
singleLineDiagramService.deleteDiagramConfigs(nadConfigUuids);
} catch (Exception e) {
LOGGER.error("Could not delete NAD configs: {}", nadConfigUuids, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public String generateNetworkAreaDiagram(UUID networkUuid, String variantId, Map
}
}

public void createMultipleDiagramConfigs(List<NadConfigInfos> nadConfigs) {
public void createDiagramConfigs(List<NadConfigInfos> nadConfigs) {
var path = UriComponentsBuilder
.fromPath(DELIMITER + SINGLE_LINE_DIAGRAM_API_VERSION + "/network-area-diagram/configs")
.buildAndExpand()
Expand All @@ -235,7 +235,7 @@ public void createMultipleDiagramConfigs(List<NadConfigInfos> nadConfigs) {
restTemplate.exchange(singleLineDiagramServerBaseUri + path, HttpMethod.POST, httpEntity, Void.class);
}

public void deleteMultipleDiagramConfigs(List<UUID> configUuids) {
public void deleteDiagramConfigs(List<UUID> configUuids) {
var path = UriComponentsBuilder
.fromPath(DELIMITER + SINGLE_LINE_DIAGRAM_API_VERSION + "/network-area-diagram/configs")
.buildAndExpand()
Expand Down Expand Up @@ -282,6 +282,23 @@ public UUID duplicateNadConfig(UUID sourceNadConfigUuid) {
}
}

public void updateNadConfig(NadConfigInfos nadConfigInfos) {
Objects.requireNonNull(nadConfigInfos);
Objects.requireNonNull(nadConfigInfos.getId());

var path = UriComponentsBuilder
.fromPath(DELIMITER + SINGLE_LINE_DIAGRAM_API_VERSION + "/network-area-diagram/config/{configUuid}")
.buildAndExpand(nadConfigInfos.getId())
.toUriString();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<NadConfigInfos> httpEntity = new HttpEntity<>(nadConfigInfos, headers);

restTemplate.exchange(singleLineDiagramServerBaseUri + path, HttpMethod.PUT, httpEntity, Void.class);
}

public void createNadPositionsConfigFromCsv(MultipartFile file) {
var path = UriComponentsBuilder.fromPath(DELIMITER + SINGLE_LINE_DIAGRAM_API_VERSION +
"/network-area-diagram/config/positions").buildAndExpand()
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/org/gridsuite/study/server/service/StudyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.gridsuite.study.server.dto.InvalidateNodeTreeParameters.InvalidationMode;
import org.gridsuite.study.server.dto.caseimport.CaseImportAction;
import org.gridsuite.study.server.dto.diagramgridlayout.DiagramGridLayout;
import org.gridsuite.study.server.dto.diagramgridlayout.nad.NadConfigInfos;
import org.gridsuite.study.server.dto.dynamicmapping.MappingInfos;
import org.gridsuite.study.server.dto.dynamicmapping.ModelInfos;
import org.gridsuite.study.server.dto.dynamicsimulation.DynamicSimulationParametersInfos;
Expand Down Expand Up @@ -129,6 +130,7 @@ public class StudyService {
private final DynamicSimulationEventService dynamicSimulationEventService;
private final StudyConfigService studyConfigService;
private final DiagramGridLayoutService diagramGridLayoutService;
private final NadConfigService nadConfigService;
private final FilterService filterService;
private final ActionsService actionsService;
private final CaseService caseService;
Expand Down Expand Up @@ -193,6 +195,7 @@ public StudyService(
DynamicSimulationEventService dynamicSimulationEventService,
StudyConfigService studyConfigService,
DiagramGridLayoutService diagramGridLayoutService,
NadConfigService nadConfigService,
FilterService filterService,
StateEstimationService stateEstimationService,
PccMinService pccMinService,
Expand Down Expand Up @@ -229,6 +232,7 @@ public StudyService(
this.dynamicSimulationEventService = dynamicSimulationEventService;
this.studyConfigService = studyConfigService;
this.diagramGridLayoutService = diagramGridLayoutService;
this.nadConfigService = nadConfigService;
this.filterService = filterService;
this.stateEstimationService = stateEstimationService;
this.pccMinService = pccMinService;
Expand Down Expand Up @@ -544,6 +548,7 @@ private Optional<DeleteStudyInfos> doDeleteStudyIfNotCreationInProgress(UUID stu
removeStateEstimationParameters(s.getStateEstimationParametersUuid());
removeSpreadsheetConfigCollection(s.getSpreadsheetConfigCollectionUuid());
removeDiagramGridLayout(s.getDiagramGridLayoutUuid());
removeNadConfigs(s.getNadConfigsUuids());
});
deleteStudyInfos = new DeleteStudyInfos(rootNetworkInfos, modificationGroupUuids);
} else {
Expand Down Expand Up @@ -1463,6 +1468,44 @@ public String generateNetworkAreaDiagram(UUID nodeUuid, UUID rootNetworkUuid, Ma
}
}

@Transactional
public UUID saveNadConfig(UUID studyUuid, NadConfigInfos nadConfig) {
StudyEntity studyEntity = studyRepository.findById(studyUuid).orElseThrow(() -> new StudyException(STUDY_NOT_FOUND));

UUID resultUuid = nadConfigService.saveNadConfig(nadConfig);

List<UUID> nadConfigs = studyEntity.getNadConfigsUuids();

if (!nadConfigs.contains(resultUuid)) {
nadConfigs.add(resultUuid);
studyRepository.save(studyEntity);
}

return resultUuid;
}

@Transactional
public void deleteNadConfig(UUID studyUuid, UUID nadConfigUuid) {
StudyEntity studyEntity = studyRepository.findById(studyUuid).orElseThrow(() -> new StudyException(STUDY_NOT_FOUND));

nadConfigService.deleteNadConfig(nadConfigUuid);

List<UUID> nadConfigs = studyEntity.getNadConfigsUuids();
if (nadConfigs.remove(nadConfigUuid)) {
studyRepository.save(studyEntity);
}
}

private void removeNadConfigs(List<UUID> nadConfigUuids) {
if (nadConfigUuids != null && !nadConfigUuids.isEmpty()) {
try {
nadConfigService.deleteNadConfigs(nadConfigUuids);
} catch (Exception e) {
LOGGER.error("Could not remove NAD configs with uuids:" + nadConfigUuids, e);
}
}
}

private void invalidateLoadFlowStatusOnAllNodes(UUID studyUuid) {
loadflowService.invalidateLoadFlowStatus(rootNetworkNodeInfoService.getComputationResultUuids(studyUuid, LOAD_FLOW));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="labidiayo (generated)" id="1763039918594-18">
<createTable tableName="study_nad_configs">
<column name="study_entity_id" type="UUID">
<constraints nullable="false"/>
</column>
<column name="nad_config_uuid" type="UUID"/>
</createTable>
</changeSet>
<changeSet author="labidiayo (generated)" id="1763039918594-20">
<addForeignKeyConstraint baseColumnNames="study_entity_id" baseTableName="study_nad_configs" constraintName="study_nad_configs_fk" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="study" validate="true"/>
</changeSet>
</databaseChangeLog>
3 changes: 3 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,6 @@ databaseChangeLog:
- include:
file: changesets/changelog_20251013T112601Z.xml
relativeToChangelogFile: true
- include:
file: changesets/changelog_20251113T131808Z.xml
relativeToChangelogFile: true
Loading