diff --git a/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigJsonUtils.java b/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigJsonUtils.java new file mode 100644 index 0000000..f157391 --- /dev/null +++ b/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigJsonUtils.java @@ -0,0 +1,35 @@ +/** + * 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 com.powsybl.dynawo.suppliers.dynamicmodels; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.powsybl.commons.json.JsonUtil; + +import java.util.List; + +/** + * @author Thang PHAM + */ +public final class DynamicModelConfigJsonUtils { + + private DynamicModelConfigJsonUtils() { + throw new AssertionError("Utility class should not be instantiated"); + } + + public static ObjectMapper createObjectMapper() { + ObjectMapper mapper = JsonUtil.createObjectMapper(); + + SimpleModule module = new SimpleModule("dynamic-model-configs"); + module.addSerializer(new DynamicModelConfigsJsonSerializer()); + module.addDeserializer(List.class, new DynamicModelConfigsJsonDeserializer()); + + mapper.registerModule(module); + return mapper; + } +} diff --git a/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializer.java b/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializer.java new file mode 100644 index 0000000..8bdc326 --- /dev/null +++ b/src/main/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializer.java @@ -0,0 +1,170 @@ +/** + * 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 com.powsybl.dynawo.suppliers.dynamicmodels; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.powsybl.dynawo.suppliers.Property; +import com.powsybl.dynawo.suppliers.PropertyType; +import com.powsybl.iidm.network.TwoSides; + +import java.io.IOException; +import java.util.List; + +/** + * Serialize a List with "models" as root: + * + * { + * "models": [ + * { + * "model": "...", + * "group": "...", + * "groupType": "FIXED", + * "properties": [ ... ] + * } + * ] + * } + * + * This matches {@link DynamicModelConfigsJsonDeserializer}. + * + * TODO : to remove when available at powsybl-dynawo + * + * @author Thang PHAM + */ +public class DynamicModelConfigsJsonSerializer extends StdSerializer> { + + public DynamicModelConfigsJsonSerializer() { + super((Class>) (Class) List.class); + } + + @Override + public void serialize(List configs, + JsonGenerator gen, + SerializerProvider provider) throws IOException { + + gen.writeStartObject(); + + gen.writeFieldName("models"); + writeDynamicModelConfigs(configs, gen); + + gen.writeEndObject(); + } + + private static void writeDynamicModelConfigs(List configs, JsonGenerator gen) throws IOException { + gen.writeStartArray(); + if (configs != null) { + for (DynamicModelConfig cfg : configs) { + writeDynamicModelConfig(cfg, gen); + } + } + gen.writeEndArray(); + } + + private static void writeDynamicModelConfig(DynamicModelConfig cfg, + JsonGenerator gen) throws IOException { + + gen.writeStartObject(); + + gen.writeStringField("model", cfg.model()); + gen.writeStringField("group", cfg.group()); + + if (cfg.groupType() != null) { + gen.writeStringField("groupType", cfg.groupType().name()); + } + + gen.writeFieldName("properties"); + writeProperties(cfg.properties(), gen); + + gen.writeEndObject(); + } + + private static void writeProperties(List properties, JsonGenerator gen) throws IOException { + gen.writeStartArray(); + if (properties != null) { + for (Property p : properties) { + writeProperty(p, gen); + } + } + gen.writeEndArray(); + } + + /** + * See {@code PropertyParserUtils.parseProperty}: + * - always writes "name" + * - writes exactly one of: "value" (string value), "values" (string array), "arrays" (array of string arrays) + * - writes "type" when it can be inferred from propertyClass + */ + private static void writeProperty(Property property, JsonGenerator gen) throws IOException { + gen.writeStartObject(); + + gen.writeStringField("name", property.name()); + + Object value = property.value(); + if (value instanceof List list) { + if (!list.isEmpty()) { + // lists: List + gen.writeFieldName("values"); + gen.writeStartArray(); + for (Object v : list) { + gen.writeString(String.valueOf(v)); + } + gen.writeEndArray(); + writeOptionalType(gen, property); + } + } else if (value != null && value.getClass().isArray()) { + // arrays: List> + gen.writeFieldName("arrays"); + gen.writeStartArray(); + for (List row : (List[]) value) { + gen.writeStartArray(); + for (Object v : row) { + gen.writeString(String.valueOf(v)); + } + gen.writeEndArray(); + } + gen.writeEndArray(); + writeOptionalType(gen, property); + } else { + if (value instanceof TwoSides ts) { + gen.writeStringField("value", ts.name()); + writeOptionalType(gen, property); + } else if (value != null) { + gen.writeStringField("value", String.valueOf(value)); + writeOptionalType(gen, property); + } + } + + gen.writeEndObject(); + } + + /** + * Writes the "type" field if we can (or if Property already carries an explicit type via propertyClass()). + * + * This keeps compatibility with PropertyParserUtils: + * case "type" -> builder.type(PropertyType.valueOf(parser.nextTextValue())); + */ + private static void writeOptionalType(JsonGenerator gen, Property property) throws IOException { + PropertyType inferredType = PropertyType.STRING; + + Class propertyClass = property.propertyClass(); + if (propertyClass != null) { + if (propertyClass == double.class) { + inferredType = PropertyType.DOUBLE; + } else if (propertyClass == int.class) { + inferredType = PropertyType.INTEGER; + } else if (propertyClass == boolean.class) { + inferredType = PropertyType.BOOLEAN; + } else if (propertyClass == TwoSides.class) { + inferredType = PropertyType.TWO_SIDES; + } + } + + gen.writeStringField("type", inferredType.name()); + } +} diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisController.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisController.java index 51cce65..d17f06f 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisController.java @@ -37,7 +37,7 @@ */ @RestController @RequestMapping(value = "/" + API_VERSION) -@Tag(name = "Dynamic simulation server") +@Tag(name = "Dynamic security analysis server") public class DynamicSecurityAnalysisController { private final DynamicSecurityAnalysisService dynamicSecurityAnalysisService; @@ -84,7 +84,7 @@ public ResponseEntity run(@PathVariable("networkUuid") UUID networkUuid, @GetMapping(value = "/results/{resultUuid}/status", produces = "application/json") @Operation(summary = "Get the dynamic security analysis status from the database") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation status"), + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic security analysis status"), @ApiResponse(responseCode = "204", description = "Dynamic security analysis status is empty"), @ApiResponse(responseCode = "404", description = "Dynamic security analysis result uuid has not been found")}) public ResponseEntity getStatus(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { @@ -104,7 +104,7 @@ public ResponseEntity> invalidateStatus(@Parameter(description = "Res @DeleteMapping(value = "/results/{resultUuid}") @Operation(summary = "Delete a dynamic security analysis result from the database") - @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation result has been deleted")}) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic security analysis result has been deleted")}) public ResponseEntity deleteResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { dynamicSecurityAnalysisResultService.delete(resultUuid); return ResponseEntity.ok().build(); @@ -128,7 +128,7 @@ public ResponseEntity stop(@Parameter(description = "Result UUID") @PathVa } @GetMapping(value = "/providers", produces = APPLICATION_JSON_VALUE) - @Operation(summary = "Get all security analysis simulation providers") + @Operation(summary = "Get all dynamic security analysis providers") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Dynamic security analysis providers have been found")}) public ResponseEntity> getProviders() { return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersController.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersController.java index 937c5c9..e0f60fd 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersController.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersController.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.gridsuite.dynamicsecurityanalysis.server.DynamicSecurityAnalysisApi; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; +import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersValues; import org.gridsuite.dynamicsecurityanalysis.server.service.ParametersService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -21,12 +22,14 @@ import java.util.Optional; import java.util.UUID; +import static org.gridsuite.computation.service.AbstractResultContext.VARIANT_ID_HEADER; + /** * @author Thang PHAM */ @RestController @RequestMapping(value = "/" + DynamicSecurityAnalysisApi.API_VERSION + "/parameters") -@Tag(name = "Dynamic security analysis parameters") +@Tag(name = "Dynamic security analysis server - Parameters") public class DynamicSecurityAnalysisParametersController { private final ParametersService parametersService; @@ -112,4 +115,15 @@ public ResponseEntity updateProvider( return ResponseEntity.ok().build(); } + @GetMapping(value = "/{uuid}/values", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get parameters values") + @ApiResponse(responseCode = "200", description = "parameters were returned") + @ApiResponse(responseCode = "404", description = "parameters were not found") + public ResponseEntity getParametersValues( + @Parameter(description = "parameters UUID") @PathVariable("uuid") UUID parametersUuid, + @RequestParam(name = "networkUuid") UUID networkUuid, + @RequestParam(name = VARIANT_ID_HEADER, required = false) String variantId) { + return ResponseEntity.of(Optional.of(parametersService.getParametersValues(parametersUuid, networkUuid, variantId))); + } + } diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/dto/parameters/DynamicSecurityAnalysisParametersValues.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/dto/parameters/DynamicSecurityAnalysisParametersValues.java new file mode 100644 index 0000000..1e69a54 --- /dev/null +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/dto/parameters/DynamicSecurityAnalysisParametersValues.java @@ -0,0 +1,31 @@ +/* + * 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.dynamicsecurityanalysis.server.dto.parameters; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.powsybl.contingency.Contingency; +import lombok.*; + +import java.util.List; + +/** + * @author Thang PHAM + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class DynamicSecurityAnalysisParametersValues { + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Double contingenciesStartTime; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + List contingencies; +} diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/DynamicSecurityAnalysisWorkerService.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/DynamicSecurityAnalysisWorkerService.java index 3631010..9427ce4 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/DynamicSecurityAnalysisWorkerService.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/DynamicSecurityAnalysisWorkerService.java @@ -9,34 +9,28 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.commons.io.FileUtil; import com.powsybl.commons.report.ReportNode; -import com.powsybl.commons.report.TypedValue; import com.powsybl.computation.ComputationManager; import com.powsybl.contingency.ContingenciesProvider; import com.powsybl.contingency.Contingency; import com.powsybl.dynamicsimulation.DynamicModelsSupplier; import com.powsybl.dynamicsimulation.DynamicSimulationParameters; import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig; +import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfigJsonUtils; import com.powsybl.dynawo.suppliers.dynamicmodels.DynawoModelsSupplier; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VariantManagerConstants; import com.powsybl.network.store.client.NetworkStoreService; -import com.powsybl.security.LimitViolation; -import com.powsybl.security.LimitViolationsResult; import com.powsybl.security.PostContingencyComputationStatus; import com.powsybl.security.SecurityAnalysisReport; import com.powsybl.security.dynamic.DynamicSecurityAnalysis; import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; import com.powsybl.security.dynamic.DynamicSecurityAnalysisRunParameters; -import com.powsybl.security.results.PostContingencyResult; import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.computation.s3.ComputationS3Service; import org.gridsuite.computation.service.*; import org.gridsuite.dynamicsecurityanalysis.server.PropertyServerNameProvider; import org.gridsuite.dynamicsecurityanalysis.server.dto.DynamicSecurityAnalysisStatus; -import org.gridsuite.dynamicsecurityanalysis.server.dto.contingency.ContingencyInfos; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; -import org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisException; -import org.gridsuite.dynamicsecurityanalysis.server.service.client.ActionsClient; import org.gridsuite.dynamicsecurityanalysis.server.service.client.DynamicSimulationClient; import org.gridsuite.dynamicsecurityanalysis.server.service.contexts.DynamicSecurityAnalysisResultContext; import org.gridsuite.dynamicsecurityanalysis.server.service.contexts.DynamicSecurityAnalysisRunContext; @@ -59,10 +53,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; -import static org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisBusinessErrorCode.CONTINGENCIES_NOT_FOUND; import static org.gridsuite.dynamicsecurityanalysis.server.service.DynamicSecurityAnalysisService.COMPUTATION_TYPE; -import static org.gridsuite.dynamicsecurityanalysis.server.utils.Utils.getReportNode; /** * @author Thang PHAM @@ -74,7 +65,6 @@ public class DynamicSecurityAnalysisWorkerService extends AbstractWorkerService< private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSecurityAnalysisWorkerService.class); private final DynamicSimulationClient dynamicSimulationClient; - private final ActionsClient actionsClient; private final ParametersService parametersService; public DynamicSecurityAnalysisWorkerService(NetworkStoreService networkStoreService, @@ -86,12 +76,10 @@ public DynamicSecurityAnalysisWorkerService(NetworkStoreService networkStoreServ DynamicSecurityAnalysisResultService dynamicSecurityAnalysisResultService, ComputationS3Service computationS3Service, DynamicSimulationClient dynamicSimulationClient, - ActionsClient actionsClient, ParametersService parametersService, PropertyServerNameProvider propertyServerNameProvider) { super(networkStoreService, notificationService, reportService, dynamicSecurityAnalysisResultService, computationS3Service, executionService, observer, objectMapper, propertyServerNameProvider); this.dynamicSimulationClient = Objects.requireNonNull(dynamicSimulationClient); - this.actionsClient = Objects.requireNonNull(actionsClient); this.parametersService = Objects.requireNonNull(parametersService); } @@ -128,40 +116,21 @@ protected String getComputationType() { return COMPUTATION_TYPE; } - /** - * TODO : open to public for mocking test with enrich report, to revert to protected when powsybl-dynawo implements - */ - @Override - public void postRun(DynamicSecurityAnalysisRunContext runContext, AtomicReference rootReportNode, SecurityAnalysisReport securityAnalysisReport) { - // TODO remove these reports when powsybl-dynawo implements - // enrich infos for contingencies timeline report - if (runContext.getReportInfos().reportUuid() != null) { - ReportNode dsaReportNode = getReportNode(runContext.getReportNode(), "dsa", null); - if (dsaReportNode != null) { - enrichContingenciesTimelineReport(securityAnalysisReport, dsaReportNode); - } - } - - super.postRun(runContext, rootReportNode, securityAnalysisReport); - } - // open the visibility from protected to public to mock in a test where the stop arrives early @Override public void preRun(DynamicSecurityAnalysisRunContext runContext) { super.preRun(runContext); - // get contingencies from actions server - List contingencyList = actionsClient.getContingencyList(runContext.getParameters().getContingencyListIds(), runContext.getNetworkUuid(), runContext.getVariantId()); - if (CollectionUtils.isEmpty(contingencyList)) { - throw new DynamicSecurityAnalysisException(CONTINGENCIES_NOT_FOUND, "No contingencies"); - } + // get contingencies + List contingencyList = parametersService.getContingencies(runContext.getParameters().getContingencyListIds(), + runContext.getNetworkUuid(), runContext.getVariantId()); // get dump file from dynamic simulation server byte[] dynamicSimulationZippedOutputState = dynamicSimulationClient.getOutputState(runContext.getDynamicSimulationResultUuid()); // get dynamic model list from dynamic simulation server byte[] dynamicSimulationZippedDynamicModel = dynamicSimulationClient.getDynamicModel(runContext.getDynamicSimulationResultUuid()); - List dynamicModel = parametersService.unZipDynamicModel(dynamicSimulationZippedDynamicModel, objectMapper); + List dynamicModel = parametersService.unZipDynamicModel(dynamicSimulationZippedDynamicModel, DynamicModelConfigJsonUtils.createObjectMapper()); // get dynamic simulation parameters from dynamic simulation server byte[] dynamicSimulationZippedParameters = dynamicSimulationClient.getDynamicSimulationParameters(runContext.getDynamicSimulationResultUuid()); @@ -202,9 +171,7 @@ public CompletableFuture getCompletableFuture(DynamicSec DynamicModelsSupplier dynamicModelsSupplier = new DynawoModelsSupplier(runContext.getDynamicModelContent()); - List contingencies = runContext.getContingencies() - .stream().map(ContingencyInfos::getContingency) - .filter(Objects::nonNull).toList(); + List contingencies = runContext.getContingencies(); ContingenciesProvider contingenciesProvider = network -> contingencies; DynamicSecurityAnalysisParameters parameters = runContext.getDynamicSecurityAnalysisParameters(); @@ -288,34 +255,4 @@ private Path createWorkingDirectory() { return workDir; } - // --- TODO remove these reports when powsybl-dynawo implements --- // - private static void enrichContingenciesTimelineReport(SecurityAnalysisReport securityAnalysisReport, ReportNode reportNode) { - for (PostContingencyResult postContingencyResult : securityAnalysisReport.getResult().getPostContingencyResults()) { - String contingencyId = postContingencyResult.getContingency().getId(); - List limitViolations = Optional.ofNullable(postContingencyResult.getLimitViolationsResult()) - .map(LimitViolationsResult::getLimitViolations).orElse(null); - - ReportNode contingencyReportNode = getReportNode(reportNode, "saContingency", String.format("(.*)%s(.*)", contingencyId)); - if (contingencyReportNode != null) { - contingencyReportNode.newReportNode() - .withSeverity(postContingencyResult.getStatus() == PostContingencyComputationStatus.CONVERGED ? TypedValue.INFO_SEVERITY : TypedValue.ERROR_SEVERITY) - .withMessageTemplate("dynamicsecurityanalysis.server.saContingencyStatus") - .withUntypedValue("contingencyStatus", postContingencyResult.getStatus().name()).add(); - if (isNotEmpty(limitViolations)) { - ReportNode limitViolationsReportNode = contingencyReportNode.newReportNode() - .withMessageTemplate("dynamicsecurityanalysis.server.limitViolations") - .add(); - for (LimitViolation limitViolation : limitViolations) { - limitViolationsReportNode.newReportNode() - .withSeverity(TypedValue.DETAIL_SEVERITY) - .withMessageTemplate("dynamicsecurityanalysis.server.limitViolation") - .withUntypedValue("count", limitViolations.indexOf(limitViolation) + 1) - .withUntypedValue("limitViolation", limitViolation.toString()) - .add(); - } - } - } - } - } - } diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/ParametersService.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/ParametersService.java index 9816eeb..4ed9aba 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/ParametersService.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/ParametersService.java @@ -8,24 +8,29 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.contingency.Contingency; import com.powsybl.dynamicsimulation.DynamicSimulationParameters; import com.powsybl.dynamicsimulation.DynamicSimulationProvider; import com.powsybl.dynawo.DumpFileParameters; import com.powsybl.dynawo.DynawoSimulationParameters; import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig; import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; -import jakarta.transaction.Transactional; +import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.computation.dto.ReportInfos; import org.gridsuite.computation.error.ComputationException; -import org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisException; +import org.gridsuite.dynamicsecurityanalysis.server.dto.contingency.ContingencyInfos; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; +import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersValues; import org.gridsuite.dynamicsecurityanalysis.server.entities.parameters.DynamicSecurityAnalysisParametersEntity; +import org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisException; import org.gridsuite.dynamicsecurityanalysis.server.repositories.DynamicSecurityAnalysisParametersRepository; +import org.gridsuite.dynamicsecurityanalysis.server.service.client.ActionsClient; import org.gridsuite.dynamicsecurityanalysis.server.service.contexts.DynamicSecurityAnalysisRunContext; import org.gridsuite.dynamicsecurityanalysis.server.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.io.UncheckedIOException; @@ -36,6 +41,7 @@ import java.util.UUID; import static org.gridsuite.computation.error.ComputationBusinessErrorCode.PARAMETERS_NOT_FOUND; +import static org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisBusinessErrorCode.CONTINGENCIES_NOT_FOUND; import static org.gridsuite.dynamicsecurityanalysis.server.error.DynamicSecurityAnalysisBusinessErrorCode.PROVIDER_NOT_FOUND; /** @@ -49,13 +55,18 @@ public class ParametersService { private final String defaultProvider; private final DynamicSecurityAnalysisParametersRepository dynamicSecurityAnalysisParametersRepository; + private final ActionsClient actionsClient; @Autowired - public ParametersService(@Value("${dynamic-security-analysis.default-provider}") String defaultProvider, DynamicSecurityAnalysisParametersRepository dynamicSecurityAnalysisParametersRepository) { + public ParametersService(@Value("${dynamic-security-analysis.default-provider}") String defaultProvider, + DynamicSecurityAnalysisParametersRepository dynamicSecurityAnalysisParametersRepository, + ActionsClient actionsClient) { this.defaultProvider = defaultProvider; this.dynamicSecurityAnalysisParametersRepository = dynamicSecurityAnalysisParametersRepository; + this.actionsClient = actionsClient; } + @Transactional(readOnly = true) public DynamicSecurityAnalysisRunContext createRunContext(UUID networkUuid, String variantId, String receiver, String provider, ReportInfos reportInfos, String userId, UUID dynamicSimulationResultUuid, @@ -63,7 +74,7 @@ public DynamicSecurityAnalysisRunContext createRunContext(UUID networkUuid, Stri boolean debug) { // get parameters from the local database - DynamicSecurityAnalysisParametersInfos dynamicSecurityAnalysisParametersInfos = getParameters(dynamicSecurityAnalysisParametersUuid); + DynamicSecurityAnalysisParametersInfos dynamicSecurityAnalysisParametersInfos = doGetParameters(dynamicSecurityAnalysisParametersUuid); // build run context DynamicSecurityAnalysisRunContext runContext = DynamicSecurityAnalysisRunContext.builder() @@ -118,7 +129,6 @@ public List unZipDynamicModel(byte[] dynamicSimulationZipped try { // unzip dynamic model List dynamicModel = Utils.unzip(dynamicSimulationZippedDynamicModel, objectMapper, new TypeReference<>() { }); - Utils.postDeserializerDynamicModel(dynamicModel); return dynamicModel; } catch (IOException e) { throw new UncheckedIOException("Error occurred while unzip the dynamic model", e); @@ -136,20 +146,31 @@ public DynamicSimulationParameters unZipDynamicSimulationParameters(byte[] dynam // --- Dynamic security analysis parameters related methods --- // + @Transactional(readOnly = true) public DynamicSecurityAnalysisParametersInfos getParameters(UUID parametersUuid) { + return doGetParameters(parametersUuid); + } + + private DynamicSecurityAnalysisParametersInfos doGetParameters(UUID parametersUuid) { DynamicSecurityAnalysisParametersEntity entity = dynamicSecurityAnalysisParametersRepository.findById(parametersUuid) .orElseThrow(() -> new ComputationException(PARAMETERS_NOT_FOUND, MSG_PARAMETERS_UUID_NOT_FOUND + parametersUuid)); return new DynamicSecurityAnalysisParametersInfos(parametersUuid, entity.getProvider(), entity.getScenarioDuration(), entity.getContingenciesStartTime(), entity.getContingencyListIds()); } + @Transactional public UUID createParameters(DynamicSecurityAnalysisParametersInfos parametersInfos) { + return doCreateParameters(parametersInfos); + } + + private UUID doCreateParameters(DynamicSecurityAnalysisParametersInfos parametersInfos) { return dynamicSecurityAnalysisParametersRepository.save(new DynamicSecurityAnalysisParametersEntity(parametersInfos)).getId(); } + @Transactional public UUID createDefaultParameters() { DynamicSecurityAnalysisParametersInfos defaultParametersInfos = getDefaultParametersValues(defaultProvider); - return createParameters(defaultParametersInfos); + return doCreateParameters(defaultParametersInfos); } public DynamicSecurityAnalysisParametersInfos getDefaultParametersValues(String provider) { @@ -168,9 +189,10 @@ public UUID duplicateParameters(UUID sourceParametersUuid) { .orElseThrow(() -> new ComputationException(PARAMETERS_NOT_FOUND, MSG_PARAMETERS_UUID_NOT_FOUND + sourceParametersUuid)); DynamicSecurityAnalysisParametersInfos duplicatedParametersInfos = entity.toDto(); duplicatedParametersInfos.setId(null); - return createParameters(duplicatedParametersInfos); + return doCreateParameters(duplicatedParametersInfos); } + @Transactional(readOnly = true) public List getAllParameters() { return dynamicSecurityAnalysisParametersRepository.findAll().stream() .map(DynamicSecurityAnalysisParametersEntity::toDto) @@ -189,6 +211,7 @@ public void updateParameters(UUID parametersUuid, DynamicSecurityAnalysisParamet } } + @Transactional public void deleteParameters(UUID parametersUuid) { dynamicSecurityAnalysisParametersRepository.deleteById(parametersUuid); } @@ -200,4 +223,23 @@ public void updateProvider(UUID parametersUuid, String provider) { entity.setProvider(provider != null ? provider : defaultProvider); } + // --- Dynamic security analysis evaluated parameters related methods --- // + + public List getContingencies(List contingencyListIds, UUID networkUuid, String variantId) { + List contingencyList = actionsClient.getContingencyList(contingencyListIds, networkUuid, variantId); + List contingencies = contingencyList.stream().map(ContingencyInfos::getContingency).filter(Objects::nonNull).toList(); + if (CollectionUtils.isEmpty(contingencies)) { + throw new DynamicSecurityAnalysisException(CONTINGENCIES_NOT_FOUND, "No contingencies"); + } + return contingencies; + } + + @Transactional(readOnly = true) + public DynamicSecurityAnalysisParametersValues getParametersValues(UUID parametersUuid, UUID networkUuid, String variantId) { + DynamicSecurityAnalysisParametersEntity entity = dynamicSecurityAnalysisParametersRepository.findById(parametersUuid) + .orElseThrow(() -> new ComputationException(PARAMETERS_NOT_FOUND, MSG_PARAMETERS_UUID_NOT_FOUND + parametersUuid)); + List contingencyList = getContingencies(entity.getContingencyListIds(), networkUuid, variantId); + return new DynamicSecurityAnalysisParametersValues(entity.getContingenciesStartTime(), contingencyList); + } + } diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/ActionsClient.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/ActionsClient.java index 216c16f..f5c0c75 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/ActionsClient.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/ActionsClient.java @@ -50,7 +50,7 @@ public List getContingencyList(List ids, @NonNull UUID n throw new DynamicSecurityAnalysisException(CONTINGENCY_LIST_EMPTY, "Contingency list parameter must not be null or empty"); } String endPointUrl = buildEndPointUrl(getBaseUri(), API_VERSION, ACTIONS_END_POINT_CONTINGENCY); - UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(endPointUrl + "/contingency-infos/export") + UriComponents uriComponents = UriComponentsBuilder.fromUriString(endPointUrl + "/contingency-infos/export") .queryParam("networkUuid", networkUuid.toString()) .queryParamIfPresent("variantId", Optional.ofNullable(variantId)) .queryParam("ids", ids) diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/DynamicSimulationClient.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/DynamicSimulationClient.java index a04b010..6d914e2 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/DynamicSimulationClient.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/client/DynamicSimulationClient.java @@ -42,7 +42,7 @@ public DynamicSimulationClient(@Value("${gridsuite.services.dynamic-simulation-s private byte[] getDynamicSimulationResultElement(@NonNull UUID dynamicSimulationResultUuid, @NonNull String resultElementEndpoint) { String endPointUrl = buildEndPointUrl(getBaseUri(), API_VERSION, DYNAMIC_SIMULATION_END_POINT_RESULT); - UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(endPointUrl + "/{resultUuid}/{resultElementEndpoint}") + UriComponents uriComponents = UriComponentsBuilder.fromUriString(endPointUrl + "/{resultUuid}/{resultElementEndpoint}") .buildAndExpand(dynamicSimulationResultUuid, resultElementEndpoint); // call dynamic-simulation REST API diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/contexts/DynamicSecurityAnalysisRunContext.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/contexts/DynamicSecurityAnalysisRunContext.java index 28bfe71..2f22bbc 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/contexts/DynamicSecurityAnalysisRunContext.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/service/contexts/DynamicSecurityAnalysisRunContext.java @@ -6,6 +6,7 @@ */ package org.gridsuite.dynamicsecurityanalysis.server.service.contexts; +import com.powsybl.contingency.Contingency; import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig; import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; import lombok.Builder; @@ -13,7 +14,6 @@ import lombok.Setter; import org.gridsuite.computation.dto.ReportInfos; import org.gridsuite.computation.service.AbstractComputationRunContext; -import org.gridsuite.dynamicsecurityanalysis.server.dto.contingency.ContingencyInfos; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; import java.nio.file.Path; @@ -32,7 +32,7 @@ public class DynamicSecurityAnalysisRunContext extends AbstractComputationRunCon // --- Fields which are enriched in worker service --- // private Path workDir; - private List contingencies; + private List contingencies; private List dynamicModelContent; private DynamicSecurityAnalysisParameters dynamicSecurityAnalysisParameters; diff --git a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/utils/Utils.java b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/utils/Utils.java index f11e0f7..0bd74f2 100644 --- a/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/utils/Utils.java +++ b/src/main/java/org/gridsuite/dynamicsecurityanalysis/server/utils/Utils.java @@ -9,18 +9,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.powsybl.commons.report.ReportNode; -import com.powsybl.dynawo.suppliers.Property; -import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig; -import com.powsybl.iidm.network.TwoSides; -import jakarta.annotation.Nullable; -import jakarta.validation.constraints.NotNull; import java.io.*; import java.nio.file.Path; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -35,19 +26,6 @@ private Utils() { throw new AssertionError("Utility class should not be instantiated"); } - public static void postDeserializerDynamicModel(List dynamicModelConfigList) { - // enum TwoSide have been serialized as a string => when deserialize the value is a string and not enum TwoSide - // so need convert from a string to enum - dynamicModelConfigList.forEach(dynamicModelConfig -> - dynamicModelConfig.properties().forEach(property -> { - if (property.propertyClass() == TwoSides.class) { - Property replaceProperty = new Property(property.name(), TwoSides.valueOf(String.valueOf(property.value())), property.propertyClass()); - int currIdx = dynamicModelConfig.properties().indexOf(property); - dynamicModelConfig.properties().set(currIdx, replaceProperty); - } - })); - } - public static byte[] zip(InputStream is) throws IOException { try (ByteArrayOutputStream os = new ByteArrayOutputStream(); GZIPOutputStream zipOs = new GZIPOutputStream(os)) { @@ -93,29 +71,4 @@ public static T unzip(byte[] zippedBytes, ObjectMapper objectMapper, Class deque = new ArrayDeque<>(); - deque.push(rootNode); - while (!deque.isEmpty()) { - ReportNode reportNode = deque.pop(); - String key = reportNode.getMessageKey(); - String message = reportNode.getMessage(); - if ((keyRegex == null || key != null && key.matches(keyRegex)) && - (messageRegex == null || message != null && message.matches(messageRegex))) { - return reportNode; - } - - if (reportNode.getChildren() != null) { - deque.addAll(reportNode.getChildren()); - } - } - return null; - } } diff --git a/src/main/resources/org/gridsuite/dynamicsecurityanalysis/server/reports.properties b/src/main/resources/org/gridsuite/dynamicsecurityanalysis/server/reports.properties index 792cfd9..8b13789 100644 --- a/src/main/resources/org/gridsuite/dynamicsecurityanalysis/server/reports.properties +++ b/src/main/resources/org/gridsuite/dynamicsecurityanalysis/server/reports.properties @@ -1,3 +1 @@ -dynamicsecurityanalysis.server.saContingencyStatus = Status : ${contingencyStatus} -dynamicsecurityanalysis.server.limitViolations = Limit Violations -dynamicsecurityanalysis.server.limitViolation = ${count}. ${limitViolation} + diff --git a/src/test/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializerTest.java b/src/test/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializerTest.java new file mode 100644 index 0000000..8139536 --- /dev/null +++ b/src/test/java/com/powsybl/dynawo/suppliers/dynamicmodels/DynamicModelConfigsJsonSerializerTest.java @@ -0,0 +1,37 @@ +/** + * 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 com.powsybl.dynawo.suppliers.dynamicmodels; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Thang PHAM + */ +class DynamicModelConfigsJsonSerializerTest { + + @Test + void testModelConfigDeserializerSerializer() throws IOException { + + ObjectMapper objectMapper = DynamicModelConfigJsonUtils.createObjectMapper(); + List dynamicModelConfigs = objectMapper.readValue(getClass().getResourceAsStream("/data/dynamicModels.json"), new TypeReference<>() { }); + assertEquals(2, dynamicModelConfigs.size()); + + String dynamicModelConfigsJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dynamicModelConfigs); + List dynamicModelConfigs2 = objectMapper.readValue(dynamicModelConfigsJson, new TypeReference<>() { }); + + Assertions.assertThat(dynamicModelConfigs2).usingRecursiveComparison().isEqualTo(dynamicModelConfigs); + } +} diff --git a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/AbstractDynamicSecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/AbstractDynamicSecurityAnalysisControllerTest.java index 1f2b96f..3e7ba81 100644 --- a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/AbstractDynamicSecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/AbstractDynamicSecurityAnalysisControllerTest.java @@ -102,9 +102,6 @@ public void setUp() throws IOException { // DynamicSecurityAnalysisParametersRepository mock initDynamicSecurityAnalysisParametersRepositoryMock(); - // ParametersService spy - initParametersServiceSpy(); - // DynamicSecurityAnalysisWorkerService spy initDynamicSecurityAnalysisWorkerServiceSpy(); } @@ -137,8 +134,6 @@ public void tearDown() { protected abstract void initDynamicSecurityAnalysisParametersRepositoryMock(); - protected abstract void initParametersServiceSpy(); - private void initDynamicSecurityAnalysisWorkerServiceSpy() { // setup spy bean when(dynamicSecurityAnalysisWorkerService.getComputationManager()).thenReturn(computationManager); diff --git a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerIEEE14Test.java b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerIEEE14Test.java index abc91be..bbb6953 100644 --- a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerIEEE14Test.java +++ b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerIEEE14Test.java @@ -131,18 +131,12 @@ protected void initActionsClientMock() { @Override protected void initDynamicSecurityAnalysisParametersRepositoryMock() { - given(dynamicSecurityAnalysisParametersRepository.findById(PARAMETERS_UUID)).willReturn(Optional.of(new DynamicSecurityAnalysisParametersEntity())); - } - - @Override - protected void initParametersServiceSpy() { DynamicSecurityAnalysisParametersInfos defaultParams = parametersService.getDefaultParametersValues("Dynawo"); defaultParams.setScenarioDuration(50.0); defaultParams.setContingenciesStartTime(5.0); defaultParams.setContingencyListIds(List.of(CONTINGENCY_UUID)); - - // setup spy bean - when(parametersService.getParameters(PARAMETERS_UUID)).thenReturn(defaultParams); + DynamicSecurityAnalysisParametersEntity entity = new DynamicSecurityAnalysisParametersEntity(defaultParams); + given(dynamicSecurityAnalysisParametersRepository.findById(PARAMETERS_UUID)).willReturn(Optional.of(entity)); } @Test diff --git a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerTest.java index 9407092..8ab185b 100644 --- a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisControllerTest.java @@ -9,7 +9,6 @@ import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; -import com.powsybl.commons.report.ReportNode; import com.powsybl.contingency.Contingency; import com.powsybl.iidm.network.Importers; import com.powsybl.iidm.network.Network; @@ -24,7 +23,6 @@ import org.gridsuite.dynamicsecurityanalysis.server.dto.contingency.ContingencyInfos; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; import org.gridsuite.dynamicsecurityanalysis.server.entities.parameters.DynamicSecurityAnalysisParametersEntity; -import org.gridsuite.dynamicsecurityanalysis.server.service.contexts.DynamicSecurityAnalysisRunContext; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; @@ -160,18 +158,12 @@ protected void initActionsClientMock() { @Override protected void initDynamicSecurityAnalysisParametersRepositoryMock() { - given(dynamicSecurityAnalysisParametersRepository.findById(PARAMETERS_UUID)).willReturn(Optional.of(new DynamicSecurityAnalysisParametersEntity())); - } - - @Override - protected void initParametersServiceSpy() { DynamicSecurityAnalysisParametersInfos defaultParams = parametersService.getDefaultParametersValues("Dynawo"); defaultParams.setScenarioDuration(50.0); defaultParams.setContingenciesStartTime(5.0); defaultParams.setContingencyListIds(List.of(CONTINGENCY_UUID)); - - // setup spy bean - when(parametersService.getParameters(PARAMETERS_UUID)).thenReturn(defaultParams); + DynamicSecurityAnalysisParametersEntity entity = new DynamicSecurityAnalysisParametersEntity(defaultParams); + given(dynamicSecurityAnalysisParametersRepository.findById(PARAMETERS_UUID)).willReturn(Optional.of(entity)); } @Test @@ -318,21 +310,6 @@ void testRunWithReport() throws Exception { doAnswer(invocation -> null).when(reportService).deleteReport(any()); doAnswer(invocation -> null).when(reportService).sendReport(any(), any()); - doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - if (args[0] instanceof DynamicSecurityAnalysisRunContext runContext && runContext.getReportInfos().reportUuid() != null) { - ReportNode dsaReportNode = runContext.getReportNode().newReportNode() - .withResourceBundles("i18n.reports") - .withMessageTemplate("dsa").add(); - dsaReportNode.newReportNode().withMessageTemplate("saContingency") - .withUntypedValue("contingencyId", "contingencyId01") - .add(); - } - invocation.callRealMethod(); - return null; - }) - .when(dynamicSecurityAnalysisWorkerService).postRun(any(), any(), any()); - doReturn(CompletableFuture.completedFuture(new SecurityAnalysisReport( new SecurityAnalysisResult( new PreContingencyResult(), diff --git a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersControllerTest.java b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersControllerTest.java index 5735783..f0dba95 100644 --- a/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersControllerTest.java +++ b/src/test/java/org/gridsuite/dynamicsecurityanalysis/server/controller/DynamicSecurityAnalysisParametersControllerTest.java @@ -9,17 +9,23 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.contingency.Contingency; import org.gridsuite.dynamicsecurityanalysis.server.DynamicSecurityAnalysisApplication; +import org.gridsuite.dynamicsecurityanalysis.server.dto.contingency.ContingencyInfos; import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersInfos; +import org.gridsuite.dynamicsecurityanalysis.server.dto.parameters.DynamicSecurityAnalysisParametersValues; import org.gridsuite.dynamicsecurityanalysis.server.entities.parameters.DynamicSecurityAnalysisParametersEntity; import org.gridsuite.dynamicsecurityanalysis.server.repositories.DynamicSecurityAnalysisParametersRepository; import org.gridsuite.dynamicsecurityanalysis.server.service.ParametersService; +import org.gridsuite.dynamicsecurityanalysis.server.service.client.ActionsClient; +import org.gridsuite.dynamicsecurityanalysis.server.utils.assertions.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -27,7 +33,11 @@ import java.util.Optional; import java.util.UUID; +import static org.gridsuite.computation.service.AbstractResultContext.VARIANT_ID_HEADER; import static org.gridsuite.dynamicsecurityanalysis.server.utils.assertions.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -54,6 +64,9 @@ class DynamicSecurityAnalysisParametersControllerTest { @Autowired DynamicSecurityAnalysisParametersRepository parametersRepository; + @MockitoBean + protected ActionsClient actionsClient; + @AfterEach void tearDown() { // delete all parameters @@ -238,4 +251,34 @@ void testUpdateProvider() throws Exception { // check provider assertThat(updatedParametersEntityOpt.get().getProvider()).isEqualTo(newProvider); } + + @Test + void testGetParametersValues() throws Exception { + // --- Setup --- // + DynamicSecurityAnalysisParametersInfos parametersInfos = getParametersInfos(); + DynamicSecurityAnalysisParametersEntity parametersEntity = new DynamicSecurityAnalysisParametersEntity(parametersInfos); + UUID parametersUuid = parametersRepository.saveAndFlush(parametersEntity).getId(); + + UUID networkUuid = UUID.fromString("75d2edb9-cccc-468f-9797-6888ba9a5948"); + String variantId = "variantId"; + + when(actionsClient.getContingencyList(anyList(), eq(networkUuid), eq(variantId))) + .thenReturn(List.of(new ContingencyInfos(Contingency.load("_LOAD__11_EC")))); + + // --- Execute --- // + MvcResult result = mockMvc.perform(get("/v1/parameters/" + parametersUuid + "/values") + .param("networkUuid", networkUuid.toString()) + .param(VARIANT_ID_HEADER, variantId)) + .andExpect(status().isOk()) + .andReturn(); + String resultParametersValuesJson = result.getResponse().getContentAsString(); + DynamicSecurityAnalysisParametersValues parametersValues = objectMapper.readValue(resultParametersValuesJson, DynamicSecurityAnalysisParametersValues.class); + + // -- Verify --- // + Assertions.assertThat(parametersValues.getContingencies()).isNotEmpty(); + Assertions.assertThat(parametersValues.getContingencies().getFirst().getId()).isEqualTo("_LOAD__11_EC"); + Assertions.assertThat(parametersValues.getContingenciesStartTime()).isEqualTo(parametersInfos.getContingenciesStartTime()); + + verify(actionsClient, times(1)).getContingencyList(anyList(), eq(networkUuid), eq(variantId)); + } } diff --git a/src/test/resources/data/dynamicModels.json b/src/test/resources/data/dynamicModels.json new file mode 100644 index 0000000..71605fc --- /dev/null +++ b/src/test/resources/data/dynamicModels.json @@ -0,0 +1,37 @@ +{ + "models":[ + { + "model":"LoadAlphaBeta", + "group": "_DM", + "groupType": "SUFFIX", + "properties":[ + { + "name":"staticId", + "value":"LOAD", + "type":"STRING" + } + ] + }, + { + "model": "TapChangerBlockingAutomationSystem", + "group": "tcb_par", + "properties":[ + { + "name": "dynamicModelId", + "value": "TCB1", + "type": "STRING" + }, + { + "name": "transformers", + "values": ["NGEN_NHV1", "NHV2_NLOAD"], + "type": "STRING" + }, + { + "name": "uMeasurements", + "arrays": [["OldNGen", "NGEN"], ["NHV1", "NHV2"]], + "type": "STRING" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/data/ieee14/_01/input/dynamicModel.dmp b/src/test/resources/data/ieee14/_01/input/dynamicModel.dmp index cbafcf4..62e6a76 100644 --- a/src/test/resources/data/ieee14/_01/input/dynamicModel.dmp +++ b/src/test/resources/data/ieee14/_01/input/dynamicModel.dmp @@ -1 +1 @@ -[{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__10_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__11_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__12_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__13_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__14_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___2_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___3_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___9_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___4_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___6_EC","propertyClass":"java.lang.String"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___5_EC","propertyClass":"java.lang.String"}]},{"model":"GeneratorSynchronousThreeWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____6_SM","propertyClass":"java.lang.String"}]},{"model":"GeneratorSynchronousThreeWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____8_SM","propertyClass":"java.lang.String"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____1_SM","propertyClass":"java.lang.String"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____2_SM","propertyClass":"java.lang.String"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____3_SM","propertyClass":"java.lang.String"}]},{"model":"StaticVarCompensator","group":"SVarCT","groupType":"FIXED","properties":[{"name":"staticId","value":"SVC2","propertyClass":"java.lang.String"}]},{"model":"OverloadManagementSystem","group":"CLA_2_4","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"CLA_1","propertyClass":"java.lang.String"},{"name":"iMeasurement","value":"_BUS____2-BUS____4-1_AC","propertyClass":"java.lang.String"},{"name":"iMeasurementSide","value":"TWO","propertyClass":"com.powsybl.iidm.network.TwoSides"},{"name":"controlledBranch","value":"_BUS____2-BUS____4-1_AC","propertyClass":"java.lang.String"}]},{"model":"OverloadManagementSystem","group":"CLA_2_5","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"CLA_2","propertyClass":"java.lang.String"},{"name":"iMeasurement","value":"_BUS____2-BUS____5-1_AC","propertyClass":"java.lang.String"},{"name":"iMeasurementSide","value":"TWO","propertyClass":"com.powsybl.iidm.network.TwoSides"},{"name":"controlledBranch","value":"_BUS____2-BUS____5-1_AC","propertyClass":"java.lang.String"}]},{"model":"TapChangerBlockingAutomaton","group":"TCB_2_4","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"TCB_1","propertyClass":"java.lang.String"},{"name":"uMeasurements","value":["_BUS___11_TN","_BUS___12_TN"],"propertyClass":"java.util.Collection"},{"name":"transformers","value":["_LOAD__11_EC","_LOAD__12_EC"],"propertyClass":"java.util.Collection"}]},{"model":"TapChangerBlockingAutomaton","group":"TCB_2_5","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"TCB_2","propertyClass":"java.lang.String"},{"name":"uMeasurements","value":"_BUS____4_TN","propertyClass":"java.lang.String"},{"name":"transformers","value":"_BUS____4-BUS____9-1_PT","propertyClass":"java.lang.String"}]}] \ No newline at end of file +{"models":[{"model":"StaticVarCompensator","group":"SVarCT","groupType":"FIXED","properties":[{"name":"staticId","value":"SVC2","type":"STRING"}]},{"model":"GeneratorSynchronousThreeWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____6_SM","type":"STRING"}]},{"model":"GeneratorSynchronousThreeWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____8_SM","type":"STRING"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____1_SM","type":"STRING"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____2_SM","type":"STRING"}]},{"model":"GeneratorSynchronousFourWindingsProportionalRegulations","group":"IEEE14","groupType":"PREFIX","properties":[{"name":"staticId","value":"_GEN____3_SM","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__10_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__11_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__12_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__13_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD__14_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___2_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___3_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___9_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___4_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___6_EC","type":"STRING"}]},{"model":"LoadAlphaBeta","group":"LAB","groupType":"FIXED","properties":[{"name":"staticId","value":"_LOAD___5_EC","type":"STRING"}]},{"model":"OverloadManagementSystem","group":"CLA_2_4","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"CLA_1","type":"STRING"},{"name":"iMeasurement","value":"_BUS____2-BUS____4-1_AC","type":"STRING"},{"name":"iMeasurementSide","value":"TWO","type":"TWO_SIDES"},{"name":"controlledBranch","value":"_BUS____2-BUS____4-1_AC","type":"STRING"}]},{"model":"OverloadManagementSystem","group":"CLA_2_5","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"CLA_2","type":"STRING"},{"name":"iMeasurement","value":"_BUS____2-BUS____5-1_AC","type":"STRING"},{"name":"iMeasurementSide","value":"TWO","type":"TWO_SIDES"},{"name":"controlledBranch","value":"_BUS____2-BUS____5-1_AC","type":"STRING"}]},{"model":"TapChangerBlockingAutomaton","group":"TCB_2_4","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"TCB_1","type":"STRING"},{"name":"uMeasurements","values":["_BUS___11_TN","_BUS___12_TN"],"type":"STRING"},{"name":"transformers","values":["_LOAD__11_EC","_LOAD__12_EC"],"type":"STRING"}]},{"model":"TapChangerBlockingAutomaton","group":"TCB_2_5","groupType":"FIXED","properties":[{"name":"dynamicModelId","value":"TCB_2","type":"STRING"},{"name":"uMeasurements","value":"_BUS____4_TN","type":"STRING"},{"name":"transformers","value":"_BUS____4-BUS____9-1_PT","type":"STRING"}]}]} \ No newline at end of file