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
15 changes: 10 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<sonar.organization>gridsuite</sonar.organization>
<sonar.projectKey>org.gridsuite:dynamic-margin-calculation-server</sonar.projectKey>
<!-- To remove after when using gridsuite dependencies release and computation version containing merged PR: https://github.com/gridsuite/computation/pull/19 -->
<gridsuite-computation.version>1.7.0</gridsuite-computation.version>
<gridsuite-computation.version>1.9.0</gridsuite-computation.version>
<powsybl-ws-commons.version>1.34.0</powsybl-ws-commons.version>
</properties>

Expand Down Expand Up @@ -116,10 +116,6 @@

<dependencies>
<!-- Compilation dependencies -->
<dependency>
<groupId>org.gridsuite</groupId>
<artifactId>gridsuite-computation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand Down Expand Up @@ -162,6 +158,15 @@
<artifactId>powsybl-dynawo-margin-calculation</artifactId>
</dependency>

<dependency>
<groupId>org.gridsuite</groupId>
<artifactId>gridsuite-computation</artifactId>
</dependency>
<dependency>
<groupId>org.gridsuite</groupId>
<artifactId>gridsuite-filter</artifactId>
</dependency>

<!-- runtime dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2026, 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.dynamicmargincalculation.server.config;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.powsybl.commons.report.ReportNodeJsonModule;
import com.powsybl.dynawo.margincalculation.json.MarginCalculationParametersJsonModule;
import org.gridsuite.computation.ComputationConfig;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

/**
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
*/
@Configuration
@Import(ComputationConfig.class)
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper) {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(objectMapper);

return restTemplateBuilder
.additionalMessageConverters(messageConverter)
.build();
}

@Bean
public Jackson2ObjectMapperBuilderCustomizer appJsonCustomizer() {
return builder -> builder
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.modulesToInstall(new MarginCalculationParametersJsonModule(), new ReportNodeJsonModule());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* 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.dynamicmargincalculation.server.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.gridsuite.computation.dto.ReportInfos;
import org.gridsuite.dynamicmargincalculation.server.dto.DynamicMarginCalculationStatus;
import org.gridsuite.dynamicmargincalculation.server.service.DynamicMarginCalculationResultService;
import org.gridsuite.dynamicmargincalculation.server.service.DynamicMarginCalculationService;
import org.gridsuite.dynamicmargincalculation.server.service.ParametersService;
import org.gridsuite.dynamicmargincalculation.server.service.contexts.DynamicMarginCalculationRunContext;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

import static org.gridsuite.computation.service.AbstractResultContext.*;
import static org.gridsuite.computation.service.NotificationService.*;
import static org.gridsuite.dynamicmargincalculation.server.DynamicMarginCalculationApi.API_VERSION;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;

/**
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
*/
@RestController
@RequestMapping(value = "/" + API_VERSION)
@Tag(name = "Dynamic margin calculation server")
public class DynamicMarginCalculationController {

private final DynamicMarginCalculationService dynamicMarginCalculationService;
private final DynamicMarginCalculationResultService dynamicMarginCalculationResultService;
private final ParametersService parametersService;

public DynamicMarginCalculationController(DynamicMarginCalculationService dynamicMarginCalculationService,
DynamicMarginCalculationResultService dynamicMarginCalculationResultService,
ParametersService parametersService) {
this.dynamicMarginCalculationService = dynamicMarginCalculationService;
this.dynamicMarginCalculationResultService = dynamicMarginCalculationResultService;
this.parametersService = parametersService;
}

@PostMapping(value = "/networks/{networkUuid}/run", produces = "application/json")
@Operation(summary = "run the dynamic margin calculation")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Run dynamic margin calculation")})
public ResponseEntity<UUID> run(@PathVariable("networkUuid") UUID networkUuid,
@RequestParam(name = VARIANT_ID_HEADER, required = false) String variantId,
@RequestParam(name = HEADER_RECEIVER, required = false) String receiver,
@RequestParam(name = "reportUuid", required = false) UUID reportId,
@RequestParam(name = REPORTER_ID_HEADER, required = false) String reportName,
@RequestParam(name = REPORT_TYPE_HEADER, required = false, defaultValue = "DynamicMarginCalculation") String reportType,
@RequestParam(name = HEADER_PROVIDER, required = false) String provider,
@RequestParam(name = HEADER_DEBUG, required = false, defaultValue = "false") boolean debug,
@RequestParam(name = "dynamicSecurityAnalysisParametersUuid") UUID dynamicSecurityAnalysisParametersUuid,
@RequestParam(name = "parametersUuid") UUID parametersUuid,
@RequestBody String dynamicSimulationParametersJson,
@RequestHeader(HEADER_USER_ID) String userId) {

DynamicMarginCalculationRunContext dynamicMarginCalculationRunContext = parametersService.createRunContext(
networkUuid,
variantId,
receiver,
provider,
ReportInfos.builder().reportUuid(reportId).reporterId(reportName).computationType(reportType).build(),
userId,
dynamicSimulationParametersJson,
dynamicSecurityAnalysisParametersUuid,
parametersUuid,
debug);

UUID resultUuid = dynamicMarginCalculationService.runAndSaveResult(dynamicMarginCalculationRunContext);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(resultUuid);
}

@GetMapping(value = "/results/{resultUuid}/status", produces = "application/json")
@Operation(summary = "Get the dynamic margin calculation status from the database")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic margin calculation status"),
@ApiResponse(responseCode = "204", description = "Dynamic margin calculation status is empty"),
@ApiResponse(responseCode = "404", description = "Dynamic security analysis result uuid has not been found")})
public ResponseEntity<DynamicMarginCalculationStatus> getStatus(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) {
DynamicMarginCalculationStatus result = dynamicMarginCalculationService.getStatus(resultUuid);
return ResponseEntity.ok().body(result);
}

@PutMapping(value = "/results/invalidate-status", produces = "application/json")
@Operation(summary = "Invalidate the dynamic margin calculation status from the database")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic margin calculation result uuids have been invalidated"),
@ApiResponse(responseCode = "404", description = "Dynamic margin calculation result has not been found")})
public ResponseEntity<List<UUID>> invalidateStatus(@Parameter(description = "Result UUIDs") @RequestParam("resultUuid") List<UUID> resultUuids) {
List<UUID> result = dynamicMarginCalculationResultService.updateStatus(resultUuids, DynamicMarginCalculationStatus.NOT_DONE);
return CollectionUtils.isEmpty(result) ? ResponseEntity.notFound().build() :
ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result);
}

@DeleteMapping(value = "/results/{resultUuid}")
@Operation(summary = "Delete a dynamic margin calculation result from the database")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic margin calculation result has been deleted")})
public ResponseEntity<Void> deleteResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) {
dynamicMarginCalculationResultService.delete(resultUuid);
return ResponseEntity.ok().build();
}

@DeleteMapping(value = "/results", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Delete dynamic margin calculation results from the database")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Dynamic margin calculation results have been deleted")})
public ResponseEntity<Void> deleteResults(@Parameter(description = "Results UUID") @RequestParam(value = "resultsUuids", required = false) List<UUID> resultsUuids) {
dynamicMarginCalculationService.deleteResults(resultsUuids);
return ResponseEntity.ok().build();
}

@PutMapping(value = "/results/{resultUuid}/stop", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Stop a dynamic margin calculation computation")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic margin calculation has been stopped")})
public ResponseEntity<Void> stop(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
@Parameter(description = "Result receiver") @RequestParam(name = "receiver", required = false, defaultValue = "") String receiver) {
dynamicMarginCalculationService.stop(resultUuid, receiver);
return ResponseEntity.ok().build();
}

@GetMapping(value = "/providers", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Get all margin calculation providers")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Dynamic margin calculation providers have been found")})
public ResponseEntity<List<String>> getProviders() {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
.body(dynamicMarginCalculationService.getProviders());
}

@GetMapping(value = "/default-provider", produces = TEXT_PLAIN_VALUE)
@Operation(summary = "Get dynamic margin calculation default provider")
@ApiResponses(@ApiResponse(responseCode = "200", description = "The dynamic margin calculation default provider has been found"))
public ResponseEntity<String> getDefaultProvider() {
return ResponseEntity.ok().body(dynamicMarginCalculationService.getDefaultProvider());
}

@GetMapping(value = "/results/{resultUuid}/download-debug-file", produces = "application/json")
@Operation(summary = "Download a dynamic margin calculation debug file")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Dynamic margin calculation debug file"),
@ApiResponse(responseCode = "404", description = "Dynamic margin calculation debug file has not been found")})
public ResponseEntity<Resource> downloadDebugFile(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) {
return dynamicMarginCalculationService.downloadDebugFile(resultUuid);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* 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.dynamicmargincalculation.server.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.gridsuite.dynamicmargincalculation.server.DynamicMarginCalculationApi;
import org.gridsuite.dynamicmargincalculation.server.dto.parameters.DynamicMarginCalculationParametersInfos;
import org.gridsuite.dynamicmargincalculation.server.service.ParametersService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

/**
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
*/
@RestController
@RequestMapping(value = "/" + DynamicMarginCalculationApi.API_VERSION + "/parameters")
@Tag(name = "Dynamic security analysis server - Parameters")
public class DynamicMarginCalculationParametersController {

private final ParametersService parametersService;

public DynamicMarginCalculationParametersController(ParametersService parametersService) {
this.parametersService = parametersService;
}

@PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create parameters")
@ApiResponse(responseCode = "200", description = "parameters were created")
public ResponseEntity<UUID> createParameters(
@RequestBody DynamicMarginCalculationParametersInfos parametersInfos) {
return ResponseEntity.ok(parametersService.createParameters(parametersInfos));
}

@PostMapping(value = "/default")
@Operation(summary = "Create default parameters")
@ApiResponse(responseCode = "200", description = "Default parameters were created")
public ResponseEntity<UUID> createDefaultParameters() {
return ResponseEntity.ok(parametersService.createDefaultParameters());
}

@PostMapping(value = "", params = "duplicateFrom", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Duplicate parameters")
@ApiResponse(responseCode = "200", description = "parameters were duplicated")
public ResponseEntity<UUID> duplicateParameters(
@Parameter(description = "source parameters UUID") @RequestParam("duplicateFrom") UUID sourceParametersUuid) {
return ResponseEntity.of(Optional.of(parametersService.duplicateParameters(sourceParametersUuid)));
}

@GetMapping(value = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get parameters")
@ApiResponse(responseCode = "200", description = "parameters were returned")
@ApiResponse(responseCode = "404", description = "parameters were not found")
public ResponseEntity<DynamicMarginCalculationParametersInfos> getParameters(
@Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid) {
return ResponseEntity.of(Optional.of(parametersService.getParameters(parametersUuid)));
}

@GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get all parameters")
@ApiResponse(responseCode = "200", description = "The list of all parameters was returned")
public ResponseEntity<List<DynamicMarginCalculationParametersInfos>> getAllParameters() {
return ResponseEntity.ok().body(parametersService.getAllParameters());
}

@PutMapping(value = "/{uuid}", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update parameters")
@ApiResponse(responseCode = "200", description = "parameters were updated")
public ResponseEntity<Void> updateParameters(
@Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid,
@RequestBody(required = false) DynamicMarginCalculationParametersInfos parametersInfos) {
parametersService.updateParameters(parametersUuid, parametersInfos);
return ResponseEntity.ok().build();
}

@DeleteMapping(value = "/{uuid}")
@Operation(summary = "Delete parameters")
@ApiResponse(responseCode = "200", description = "parameters were deleted")
public ResponseEntity<Void> deleteParameters(
@Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid) {
parametersService.deleteParameters(parametersUuid);
return ResponseEntity.ok().build();
}

@GetMapping(value = "/{uuid}/provider", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get provider")
@ApiResponse(responseCode = "200", description = "provider were returned")
@ApiResponse(responseCode = "404", description = "provider were not found")
public ResponseEntity<String> getProvider(
@Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid) {
return ResponseEntity.of(Optional.of(parametersService.getParameters(parametersUuid).getProvider()));
}

@PutMapping(value = "/{uuid}/provider")
@Operation(summary = "Update provider")
@ApiResponse(responseCode = "200", description = "provider was updated")
public ResponseEntity<Void> updateProvider(
@Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid,
@RequestBody(required = false) String provider) {
parametersService.updateProvider(parametersUuid, provider);
return ResponseEntity.ok().build();
}

}
Loading