diff --git a/pom.xml b/pom.xml index f1066524..7db2a7c8 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,8 @@ + + org.gridsuite @@ -98,13 +100,16 @@ pom import + + + com.powsybl - powsybl-network-store-iidm-impl + powsybl-iidm-api org.springframework.boot @@ -132,15 +137,15 @@ com.powsybl - powsybl-dynawaltz + powsybl-dynamic-simulation-dsl com.powsybl - powsybl-dynawaltz-dsl + powsybl-dynawaltz com.powsybl - powsybl-dynamic-simulation-dsl + powsybl-dynawaltz-dsl com.powsybl @@ -189,6 +194,12 @@ ${h2database.version} test + + com.powsybl + powsybl-commons + test-jar + test + com.powsybl powsybl-config-test diff --git a/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplier.java b/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplier.java new file mode 100644 index 00000000..fab65bd1 --- /dev/null +++ b/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplier.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.dynamicsimulation.groovy; + +import com.powsybl.dsl.ExpressionDslLoader; +import com.powsybl.dsl.GroovyScripts; +import com.powsybl.dynamicsimulation.Curve; +import com.powsybl.dynamicsimulation.CurvesSupplier; +import com.powsybl.iidm.network.Network; +import groovy.lang.Binding; +import groovy.lang.GroovyCodeSource; +import groovy.lang.GroovyShell; +import org.codehaus.groovy.control.CompilerConfiguration; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * TODO: remove and use core one when switching to 5.1.0 + * @author Mathieu Bague + */ +public class GroovyCurvesSupplier implements CurvesSupplier { + + private final GroovyCodeSource codeSource; + + private final List extensions; + + public GroovyCurvesSupplier(InputStream is, List extensions) { + this.codeSource = GroovyScripts.load(is); + this.extensions = Objects.requireNonNull(extensions); + } + + @Override + public String getName() { + return null; + } + + @Override + public List get(Network network) { + List curves = new ArrayList<>(); + + Binding binding = new Binding(); + binding.setVariable("network", network); + + ExpressionDslLoader.prepareClosures(binding); + extensions.forEach(e -> e.load(binding, curves::add)); + + GroovyShell shell = new GroovyShell(binding, new CompilerConfiguration()); + shell.evaluate(codeSource); + + return curves; + } +} diff --git a/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelsSupplier.java b/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelsSupplier.java new file mode 100644 index 00000000..216e54a4 --- /dev/null +++ b/src/main/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelsSupplier.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2020, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.dynamicsimulation.groovy; + +import com.powsybl.dsl.ExpressionDslLoader; +import com.powsybl.dsl.GroovyScripts; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.dynamicsimulation.EventModelsSupplier; +import com.powsybl.iidm.network.Network; +import groovy.lang.Binding; +import groovy.lang.GroovyCodeSource; +import groovy.lang.GroovyShell; +import org.codehaus.groovy.control.CompilerConfiguration; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * TODO: remove and use core one when switching to 5.1.0 + * @author Marcos de Miguel + */ +public class GroovyEventModelsSupplier implements EventModelsSupplier { + + private final GroovyCodeSource codeSource; + + private final List extensions; + + public GroovyEventModelsSupplier(InputStream is, List extensions) { + this.codeSource = GroovyScripts.load(is); + this.extensions = Objects.requireNonNull(extensions); + } + + @Override + public String getName() { + return null; + } + + @Override + public List get(Network network) { + List eventModels = new ArrayList<>(); + + Binding binding = new Binding(); + binding.setVariable("network", network); + + ExpressionDslLoader.prepareClosures(binding); + extensions.forEach(e -> e.load(binding, eventModels::add)); + + GroovyShell shell = new GroovyShell(binding, new CompilerConfiguration()); + shell.evaluate(codeSource); + + return eventModels; + } +} diff --git a/src/main/java/org/gridsuite/ds/server/DynamicSimulationApi.java b/src/main/java/org/gridsuite/ds/server/DynamicSimulationApi.java index 28653119..0064fc37 100644 --- a/src/main/java/org/gridsuite/ds/server/DynamicSimulationApi.java +++ b/src/main/java/org/gridsuite/ds/server/DynamicSimulationApi.java @@ -10,10 +10,10 @@ * @author Abdelsalem Hedhili */ -final class DynamicSimulationApi { +public final class DynamicSimulationApi { private DynamicSimulationApi() { } - static final String API_VERSION = "v1"; + public static final String API_VERSION = "v1"; } diff --git a/src/main/java/org/gridsuite/ds/server/DynamicSimulationSwaggerConfig.java b/src/main/java/org/gridsuite/ds/server/config/SwaggerConfig.java similarity index 82% rename from src/main/java/org/gridsuite/ds/server/DynamicSimulationSwaggerConfig.java rename to src/main/java/org/gridsuite/ds/server/config/SwaggerConfig.java index 50100054..ad7c890f 100644 --- a/src/main/java/org/gridsuite/ds/server/DynamicSimulationSwaggerConfig.java +++ b/src/main/java/org/gridsuite/ds/server/config/SwaggerConfig.java @@ -1,13 +1,14 @@ -/** - * Copyright (c) 2021, RTE (http://www.rte-france.com) +/* + * Copyright (c) 2021-2022, 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.ds.server; +package org.gridsuite.ds.server.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import org.gridsuite.ds.server.DynamicSimulationApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,7 +16,7 @@ * @author Abdelsalem Hedhili */ @Configuration -public class DynamicSimulationSwaggerConfig { +public class SwaggerConfig { @Bean public OpenAPI createOpenApi() { diff --git a/src/main/java/org/gridsuite/ds/server/DynamicSimulationController.java b/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java similarity index 60% rename from src/main/java/org/gridsuite/ds/server/DynamicSimulationController.java rename to src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java index 43a4859e..620cc21b 100644 --- a/src/main/java/org/gridsuite/ds/server/DynamicSimulationController.java +++ b/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java @@ -4,30 +4,31 @@ * 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.ds.server; +package org.gridsuite.ds.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.gridsuite.ds.server.dto.DynamicSimulationStatus; import org.gridsuite.ds.server.service.DynamicSimulationService; import org.springframework.boot.context.properties.bind.DefaultValue; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.http.codec.multipart.FilePart; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; import java.util.UUID; -import static org.springframework.http.MediaType.*; +import static org.gridsuite.ds.server.DynamicSimulationApi.API_VERSION; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /** * @author Abdelsalem Hedhili */ @RestController -@RequestMapping(value = "/" + DynamicSimulationApi.API_VERSION) +@RequestMapping(value = "/" + API_VERSION) @Tag(name = "Dynamic simulation server") public class DynamicSimulationController { @@ -44,17 +45,28 @@ public ResponseEntity> run(@PathVariable("networkUuid") UUID networkU @RequestParam(name = "variantId", required = false) String variantId, @DefaultValue("0") @RequestParam("startTime") int startTime, @RequestParam("stopTime") int stopTime, - @RequestPart("dynamicModel") FilePart dynamicModel) { - Mono resultUuid = dynamicSimulationService.runAndSaveResult(networkUuid, variantId, startTime, stopTime, dynamicModel); + @RequestParam("mappingName") String mappingName, + @RequestParam(name = "receiver", required = false) String receiver) { + Mono resultUuid = dynamicSimulationService.runAndSaveResult(receiver, networkUuid, variantId, startTime, stopTime, mappingName); return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(resultUuid); } - @GetMapping(value = "/results/{resultUuid}", produces = "application/json") + @GetMapping(value = "/results/{resultUuid}/timeseries", produces = "application/json") @Operation(summary = "Get a dynamic simulation result from the database") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation result"), @ApiResponse(responseCode = "404", description = "Dynamic simulation result has not been found")}) - public Mono> getResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { - Mono result = dynamicSimulationService.getResult(resultUuid); + public Mono> getTimeSeriesResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + Mono result = dynamicSimulationService.getTimeSeriesId(resultUuid); + return result.map(r -> ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(r)) + .defaultIfEmpty(ResponseEntity.notFound().build()); + } + + @GetMapping(value = "/results/{resultUuid}/timeline", produces = "application/json") + @Operation(summary = "Get a dynamic simulation result from the database") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation result"), + @ApiResponse(responseCode = "404", description = "Dynamic simulation result has not been found")}) + public Mono> getTimeLineResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + Mono result = dynamicSimulationService.getTimeLineId(resultUuid); return result.map(r -> ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(r)) .defaultIfEmpty(ResponseEntity.notFound().build()); } @@ -63,8 +75,8 @@ public Mono> getResult(@Parameter(description = "Result @Operation(summary = "Get the dynamic simulation status from the database") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation status"), @ApiResponse(responseCode = "404", description = "Dynamic simulation status has not been found")}) - public Mono> getStatus(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { - Mono result = dynamicSimulationService.getStatus(resultUuid); + public Mono> getStatus(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + Mono result = dynamicSimulationService.getStatus(resultUuid); return result.map(r -> ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(r)) .defaultIfEmpty(ResponseEntity.notFound().build()); } @@ -85,4 +97,12 @@ public ResponseEntity> deleteResults() { return ResponseEntity.ok().body(result); } + @PutMapping(value = "/results/{resultUuid}/stop", produces = APPLICATION_JSON_VALUE) + @Operation(summary = "Stop a dynamic simulation computation") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation has been stopped")}) + public ResponseEntity> stop(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, + @Parameter(description = "Result receiver") @RequestParam(name = "receiver", required = false) String receiver) { + Mono result = dynamicSimulationService.stop(receiver, resultUuid); + return ResponseEntity.ok().body(result); + } } diff --git a/src/main/java/org/gridsuite/ds/server/dto/DynamicSimulationStatus.java b/src/main/java/org/gridsuite/ds/server/dto/DynamicSimulationStatus.java index cbe11762..fe48b08c 100644 --- a/src/main/java/org/gridsuite/ds/server/dto/DynamicSimulationStatus.java +++ b/src/main/java/org/gridsuite/ds/server/dto/DynamicSimulationStatus.java @@ -10,6 +10,8 @@ * @author Abdelsalem Hedhili */ public enum DynamicSimulationStatus { + NOT_DONE, RUNNING, - COMPLETED + CONVERGED, + DIVERGED } diff --git a/src/main/java/org/gridsuite/ds/server/dto/dynamicmapping/Script.java b/src/main/java/org/gridsuite/ds/server/dto/dynamicmapping/Script.java new file mode 100644 index 00000000..22d37a26 --- /dev/null +++ b/src/main/java/org/gridsuite/ds/server/dto/dynamicmapping/Script.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2022, 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.ds.server.dto.dynamicmapping; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +/** + * @author Thang PHAM + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class Script { + + private String name; + + // name of the original mapping + private String parentName; + + private String script; + + private Date createdDate; + + private String parametersFile; + +} diff --git a/src/main/java/org/gridsuite/ds/server/dto/timeseries/TimeSeriesGroupInfos.java b/src/main/java/org/gridsuite/ds/server/dto/timeseries/TimeSeriesGroupInfos.java new file mode 100644 index 00000000..cb0604c6 --- /dev/null +++ b/src/main/java/org/gridsuite/ds/server/dto/timeseries/TimeSeriesGroupInfos.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2023, 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.ds.server.dto.timeseries; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; + +/** + * @author Thang PHAM + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class TimeSeriesGroupInfos { + + private UUID id; +} diff --git a/src/main/java/org/gridsuite/ds/server/repository/AbstractManuallyAssignedIdentifierEntity.java b/src/main/java/org/gridsuite/ds/server/model/AbstractManuallyAssignedIdentifierEntity.java similarity index 95% rename from src/main/java/org/gridsuite/ds/server/repository/AbstractManuallyAssignedIdentifierEntity.java rename to src/main/java/org/gridsuite/ds/server/model/AbstractManuallyAssignedIdentifierEntity.java index 93b1eb90..1970f9ac 100644 --- a/src/main/java/org/gridsuite/ds/server/repository/AbstractManuallyAssignedIdentifierEntity.java +++ b/src/main/java/org/gridsuite/ds/server/model/AbstractManuallyAssignedIdentifierEntity.java @@ -4,7 +4,7 @@ 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.ds.server.repository; +package org.gridsuite.ds.server.model; import org.springframework.data.domain.Persistable; diff --git a/src/main/java/org/gridsuite/ds/server/repository/ResultEntity.java b/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java similarity index 72% rename from src/main/java/org/gridsuite/ds/server/repository/ResultEntity.java rename to src/main/java/org/gridsuite/ds/server/model/ResultEntity.java index fa002fab..15289243 100644 --- a/src/main/java/org/gridsuite/ds/server/repository/ResultEntity.java +++ b/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java @@ -4,13 +4,13 @@ * 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.ds.server.repository; +package org.gridsuite.ds.server.model; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import javax.persistence.*; +import javax.persistence.*; import java.io.Serializable; import java.util.UUID; @@ -24,9 +24,10 @@ @Entity public class ResultEntity extends AbstractManuallyAssignedIdentifierEntity implements Serializable { - public ResultEntity(UUID id, Boolean result, String status) { + public ResultEntity(UUID id, UUID timeSeriesId, UUID timeLineId, String status) { this.id = id; - this.result = result; + this.timeSeriesId = timeSeriesId; + this.timeLineId = timeLineId; this.status = status; } @@ -35,8 +36,11 @@ public ResultEntity(UUID id, Boolean result, String status) { @GeneratedValue private UUID id; - @Column(name = "result") - private Boolean result; + @Column(name = "timeSeriesUuid") + private UUID timeSeriesId; + + @Column(name = "timeLineUuid") + private UUID timeLineId; @Column(name = "status") private String status; diff --git a/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java b/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java index 601f5e62..536330b8 100644 --- a/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java +++ b/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java @@ -6,6 +6,7 @@ */ package org.gridsuite.ds.server.repository; +import org.gridsuite.ds.server.model.ResultEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultContext.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultContext.java deleted file mode 100644 index dcdd3d86..00000000 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultContext.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2021, 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.ds.server.service; - -import com.powsybl.commons.PowsyblException; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -import java.util.*; - -/** - * @author Abdelsalem Hedhili - */ -public class DynamicSimulationResultContext { - - private final UUID resultUuid; - - private final DynamicSimulationRunContext runContext; - - public DynamicSimulationResultContext(UUID resultUuid, DynamicSimulationRunContext runContext) { - this.resultUuid = Objects.requireNonNull(resultUuid); - this.runContext = Objects.requireNonNull(runContext); - } - - public UUID getResultUuid() { - return resultUuid; - } - - public DynamicSimulationRunContext getRunContext() { - return runContext; - } - - private static String getNonNullHeader(MessageHeaders headers, String name) { - String header = (String) headers.get(name); - if (header == null) { - throw new PowsyblException("Header '" + name + "' not found"); - } - return header; - } - - public static DynamicSimulationResultContext fromMessage(Message message) { - Objects.requireNonNull(message); - MessageHeaders headers = message.getHeaders(); - UUID resultUuid = UUID.fromString(getNonNullHeader(headers, "resultUuid")); - UUID networkUuid = UUID.fromString(getNonNullHeader(headers, "networkUuid")); - String variantId = (String) headers.get("variantId"); - int startTime = Integer.parseInt(getNonNullHeader(headers, "startTime")); - int stopTIme = Integer.parseInt(getNonNullHeader(headers, "stopTime")); - byte[] dynamicModelContent = (byte[]) headers.get("dynamicModelContent"); - DynamicSimulationRunContext runContext = new DynamicSimulationRunContext(networkUuid, variantId, startTime, stopTIme, dynamicModelContent); - return new DynamicSimulationResultContext(resultUuid, runContext); - } - - public Message toMessage() { - return MessageBuilder.withPayload("") - .setHeader("resultUuid", resultUuid.toString()) - .setHeader("networkUuid", runContext.getNetworkUuid().toString()) - .setHeader("variantId", runContext.getVariantId()) - .setHeader("startTime", String.valueOf(runContext.getStartTime())) - .setHeader("stopTime", String.valueOf(runContext.getStopTime())) - .setHeader("dynamicModelContent", runContext.getDynamicModelContent()) - .build(); - } - -} diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationRunContext.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationRunContext.java deleted file mode 100644 index a602d519..00000000 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationRunContext.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2021, 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.ds.server.service; - -import java.util.Objects; -import java.util.UUID; - -/** - * @author Abdelsalem Hedhili - */ -public class DynamicSimulationRunContext { - - private final UUID networkUuid; - - private final String variantId; - - private final int startTime; - - private final int stopTime; - - private final byte[] dynamicModelContent; - - public DynamicSimulationRunContext(UUID networkUuid, String variantId, int startTime, int stopTime, byte[] dynamicModelContent) { - this.networkUuid = Objects.requireNonNull(networkUuid); - this.variantId = variantId; - this.startTime = startTime; - this.stopTime = stopTime; - this.dynamicModelContent = dynamicModelContent; - } - - public UUID getNetworkUuid() { - return networkUuid; - } - - public String getVariantId() { - return variantId; - } - - public int getStartTime() { - return startTime; - } - - public int getStopTime() { - return stopTime; - } - - public byte[] getDynamicModelContent() { - return dynamicModelContent; - } -} - diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationService.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationService.java index 95499eb0..3296793b 100644 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationService.java +++ b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationService.java @@ -6,20 +6,23 @@ */ package org.gridsuite.ds.server.service; +import com.powsybl.dynamicsimulation.DynamicSimulationParameters; import org.gridsuite.ds.server.dto.DynamicSimulationStatus; -import org.gridsuite.ds.server.repository.ResultEntity; +import org.gridsuite.ds.server.model.ResultEntity; import org.gridsuite.ds.server.repository.ResultRepository; +import org.gridsuite.ds.server.service.client.dynamicmapping.DynamicMappingClient; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationCancelContext; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationResultContext; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationRunContext; +import org.gridsuite.ds.server.service.notification.NotificationService; +import org.gridsuite.ds.server.service.parameters.ParametersService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.stream.function.StreamBridge; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.http.codec.multipart.FilePart; import org.springframework.messaging.Message; import org.springframework.stereotype.Service; -import org.springframework.util.StreamUtils; import reactor.core.publisher.Mono; +import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.UUID; @@ -28,69 +31,82 @@ */ @Service public class DynamicSimulationService { - - private final ResultRepository resultRepository; - - @Autowired - private StreamBridge publishRun; - private static final String CATEGORY_BROKER_OUTPUT = DynamicSimulationService.class.getName() + ".output-broker-messages"; - private static final Logger LOGGER = LoggerFactory.getLogger(CATEGORY_BROKER_OUTPUT); + private final ResultRepository resultRepository; + private final NotificationService notificationService; + private final DynamicMappingClient dynamicMappingClient; + private final ParametersService parametersService; - public DynamicSimulationService(ResultRepository resultRepository) { + public DynamicSimulationService(ResultRepository resultRepository, NotificationService notificationService, DynamicMappingClient dynamicMappingClient, ParametersService parametersService) { this.resultRepository = Objects.requireNonNull(resultRepository); + this.notificationService = Objects.requireNonNull(notificationService); + this.dynamicMappingClient = Objects.requireNonNull(dynamicMappingClient); + this.parametersService = Objects.requireNonNull(parametersService); } - public Mono runAndSaveResult(UUID networkUuid, String variantId, int startTime, int stopTime, FilePart dynamicModel) { - - Mono fileBytes; - fileBytes = dynamicModel.content().collectList().flatMap(all -> Mono.fromCallable(() -> - StreamUtils.copyToByteArray(new DefaultDataBufferFactory().join(all).asInputStream()))); - - return fileBytes.flatMap(bytes -> { - DynamicSimulationRunContext runContext = new DynamicSimulationRunContext(networkUuid, variantId, startTime, stopTime, bytes); - // update status to running status and store the dynamicModel file - return insertStatus(DynamicSimulationStatus.RUNNING.name()) - .flatMap(resultEntity -> - Mono.fromRunnable(() -> { - Message message = new DynamicSimulationResultContext(resultEntity.getId(), runContext).toMessage(); - sendRunMessage(message); - }) - .thenReturn(resultEntity.getId()) - ); - }); + public Mono runAndSaveResult(String receiver, UUID networkUuid, String variantId, int startTime, int stopTime, String mappingName) { + + return dynamicMappingClient.createFromMapping(mappingName) // get script and parameters file from dynamic mapping server + .flatMap(scriptObj -> { + // get all dynamic simulation parameters + String parametersFile = scriptObj.getParametersFile(); + DynamicSimulationParameters parameters = parametersService.getDynamicSimulationParameters(parametersFile.getBytes(StandardCharsets.UTF_8)); + + // set start and stop times + parameters.setStartTime(startTime); + parameters.setStopTime(stopTime); + + String script = scriptObj.getScript(); + byte[] dynamicModel = script.getBytes(StandardCharsets.UTF_8); + byte[] eventModel = parametersService.getEventModel(); + byte[] curveModel = parametersService.getCurveModel(); + + DynamicSimulationRunContext runContext = new DynamicSimulationRunContext(receiver, networkUuid, variantId, dynamicModel, eventModel, curveModel, parameters); + + return insertStatus(DynamicSimulationStatus.RUNNING.name()) // update status to running status + .map(resultEntity -> { + Message message = new DynamicSimulationResultContext(resultEntity.getId(), runContext).toMessage(); + notificationService.emitRunDynamicSimulationMessage(message); + return resultEntity.getId(); + }); + } + ); } public Mono insertStatus(String status) { - return Mono.fromCallable(() -> resultRepository.save(new ResultEntity(null, null, status))); + return Mono.fromCallable(() -> resultRepository.save(new ResultEntity(null, null, null, status))); } - public Mono getResult(UUID resultUuid) { + public Mono getTimeSeriesId(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return Mono.fromCallable(() -> resultRepository.findById(resultUuid).map(ResultEntity::getResult) - .orElse(null)); + return Mono.fromCallable(() -> resultRepository.findById(resultUuid).map(ResultEntity::getTimeSeriesId) + .orElse(null)); } - public Mono getStatus(UUID resultUuid) { + public Mono getTimeLineId(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return Mono.fromCallable(() -> resultRepository.findById(resultUuid).map(ResultEntity::getStatus).orElse(null)); + return Mono.fromCallable(() -> resultRepository.findById(resultUuid).map(ResultEntity::getTimeLineId) + .orElse(null)); + } + + public Mono getStatus(UUID resultUuid) { + Objects.requireNonNull(resultUuid); + return Mono.fromCallable(() -> resultRepository.findById(resultUuid).map(ResultEntity::getStatus).orElse(null)).map(DynamicSimulationStatus::valueOf); } public Mono deleteResult(UUID resultUuid) { Objects.requireNonNull(resultUuid); return Mono.fromRunnable(() -> resultRepository.deleteById(resultUuid)) - .doOnError(throwable -> LOGGER.error(throwable.toString(), throwable)).then(); + .doOnError(throwable -> LOGGER.error(throwable.toString(), throwable)).then(); } public Mono deleteResults() { return Mono.fromRunnable(resultRepository::deleteAll) - .doOnError(throwable -> LOGGER.error(throwable.toString(), throwable)).then(); + .doOnError(throwable -> LOGGER.error(throwable.toString(), throwable)).then(); } - private void sendRunMessage(Message message) { - LOGGER.debug("Sending message : {}", message); - publishRun.send("publishRun-out-0", message); + public Mono stop(String receiver, UUID resultUuid) { + return Mono.fromRunnable(() -> notificationService.emitCancelDynamicSimulationMessage(new DynamicSimulationCancelContext(receiver, resultUuid).toMessage())); } - } diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java index af0ab5dd..68c3a1cd 100644 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java +++ b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java @@ -7,23 +7,25 @@ package org.gridsuite.ds.server.service; import com.powsybl.commons.PowsyblException; -import com.powsybl.dynamicsimulation.DynamicModelsSupplier; -import com.powsybl.dynamicsimulation.DynamicSimulation; -import com.powsybl.dynamicsimulation.DynamicSimulationParameters; -import com.powsybl.dynamicsimulation.DynamicSimulationResult; -import com.powsybl.dynamicsimulation.groovy.DynamicModelGroovyExtension; -import com.powsybl.dynamicsimulation.groovy.GroovyDynamicModelsSupplier; -import com.powsybl.dynamicsimulation.groovy.GroovyExtension; +import com.powsybl.dynamicsimulation.*; +import com.powsybl.dynamicsimulation.groovy.*; import com.powsybl.dynawaltz.DynaWaltzProvider; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VariantManagerConstants; import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.network.store.client.PreloadingStrategy; -import org.gridsuite.ds.server.repository.ResultRepository; +import com.powsybl.timeseries.StringTimeSeries; +import com.powsybl.timeseries.TimeSeries; +import com.powsybl.dynamicsimulation.groovy.GroovyCurvesSupplier; +import com.powsybl.dynamicsimulation.groovy.GroovyEventModelsSupplier; +import org.gridsuite.ds.server.dto.DynamicSimulationStatus; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationCancelContext; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationResultContext; +import org.gridsuite.ds.server.service.contexts.DynamicSimulationRunContext; +import org.gridsuite.ds.server.service.notification.NotificationService; +import org.gridsuite.ds.server.service.client.timeseries.TimeSeriesClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.stream.function.StreamBridge; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.http.HttpStatus; @@ -32,13 +34,10 @@ import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; import java.io.ByteArrayInputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -56,48 +55,54 @@ public class DynamicSimulationWorkerService { private static final Logger LOGGER_BROKER_INPUT = LoggerFactory.getLogger(CATEGORY_BROKER_INPUT); - private static final String CATEGORY_BROKER_OUTPUT = DynamicSimulationService.class.getName() + ".output-broker-messages"; - - private static final Logger OUTPUT_MESSAGE_LOGGER = LoggerFactory.getLogger(CATEGORY_BROKER_OUTPUT); - - private final ResultRepository resultRepository; - private final NetworkStoreService networkStoreService; - private FileSystem fileSystem = FileSystems.getDefault(); + private final NotificationService notificationService; - @Autowired - private StreamBridge publishResult; + private final TimeSeriesClient timeSeriesClient; - private DynamicSimulationWorkerUpdateResult dynamicSimulationWorkerUpdateResult; + private final DynamicSimulationWorkerUpdateResult dynamicSimulationWorkerUpdateResult; public DynamicSimulationWorkerService(NetworkStoreService networkStoreService, - ResultRepository resultRepository, + NotificationService notificationService, + TimeSeriesClient timeSeriesClient, DynamicSimulationWorkerUpdateResult dynamicSimulationWorkerUpdateResult) { this.networkStoreService = networkStoreService; - this.resultRepository = resultRepository; + this.notificationService = notificationService; + this.timeSeriesClient = timeSeriesClient; this.dynamicSimulationWorkerUpdateResult = dynamicSimulationWorkerUpdateResult; } public Mono run(DynamicSimulationRunContext context) { Objects.requireNonNull(context); - LOGGER.info("Run dynamic simulation on network {}, startTime {}, stopTime {},", context.getNetworkUuid(), context.getStartTime(), context.getStopTime()); + LOGGER.info("Run dynamic simulation on network {}, startTime {}, stopTime {},", context.getNetworkUuid(), context.getParameters().getStartTime(), context.getParameters().getStopTime()); Network network = getNetwork(context.getNetworkUuid()); - List extensions = GroovyExtension.find(DynamicModelGroovyExtension.class, DynaWaltzProvider.NAME); - GroovyDynamicModelsSupplier dynamicModelsSupplier = new GroovyDynamicModelsSupplier(new ByteArrayInputStream(context.getDynamicModelContent()), extensions); - DynamicSimulationParameters parameters = new DynamicSimulationParameters(context.getStartTime(), context.getStopTime()); + + List dynamicModelExtensions = GroovyExtension.find(DynamicModelGroovyExtension.class, DynaWaltzProvider.NAME); + DynamicModelsSupplier dynamicModelsSupplier = new GroovyDynamicModelsSupplier(new ByteArrayInputStream(context.getDynamicModelContent()), dynamicModelExtensions); + + List eventModelExtensions = GroovyExtension.find(EventModelGroovyExtension.class, DynaWaltzProvider.NAME); + EventModelsSupplier eventModelsSupplier = new GroovyEventModelsSupplier(new ByteArrayInputStream(context.getEventModelContent()), eventModelExtensions); + + List curveExtensions = GroovyExtension.find(CurveGroovyExtension.class, DynaWaltzProvider.NAME); + CurvesSupplier curvesSupplier = new GroovyCurvesSupplier(new ByteArrayInputStream(context.getCurveContent()), curveExtensions); + return Mono.fromCompletionStage(runAsync(network, - context.getVariantId() != null ? context.getVariantId() : VariantManagerConstants.INITIAL_VARIANT_ID, - dynamicModelsSupplier, - parameters)); + context.getVariantId() != null ? context.getVariantId() : VariantManagerConstants.INITIAL_VARIANT_ID, + dynamicModelsSupplier, + eventModelsSupplier, + curvesSupplier, + context.getParameters())); } public CompletableFuture runAsync(Network network, String variantId, DynamicModelsSupplier dynamicModelsSupplier, + EventModelsSupplier eventModelsSupplier, + CurvesSupplier curvesSupplier, DynamicSimulationParameters dynamicSimulationParameters) { - return DynamicSimulation.runAsync(network, dynamicModelsSupplier, n1 -> null, n1 -> null, variantId, dynamicSimulationParameters); + return DynamicSimulation.runAsync(network, dynamicModelsSupplier, eventModelsSupplier, curvesSupplier, variantId, dynamicSimulationParameters); } private Network getNetwork(UUID networkUuid) { @@ -109,40 +114,59 @@ private Network getNetwork(UUID networkUuid) { } @Bean - public Consumer> consumeRun() { + public Consumer> consumeRun() { return message -> { - LOGGER_BROKER_INPUT.debug("consume {}", message); + LOGGER_BROKER_INPUT.debug("consumeRun {}", message); + DynamicSimulationResultContext resultContext = DynamicSimulationResultContext.fromMessage(message); try { - DynamicSimulationResultContext resultContext = DynamicSimulationResultContext.fromMessage(message); - run(resultContext.getRunContext()) - .flatMap(result -> updateResult(resultContext.getResultUuid(), result.isOk())) - .doOnSuccess(unused -> { - Message sendMessage = MessageBuilder - .withPayload("") - .setHeader("resultUuid", resultContext.getResultUuid().toString()) - .build(); - sendResultMessage(sendMessage); - LOGGER.info("Dynamic simulation complete (resultUuid='{}')", resultContext.getResultUuid()); - }).block(); + .flatMap(result -> updateResult(resultContext.getResultUuid(), result)) + .doOnSuccess(result -> { + Message sendMessage = MessageBuilder + .withPayload("") + .setHeader("resultUuid", resultContext.getResultUuid().toString()) + .setHeader("receiver", resultContext.getRunContext().getReceiver()) + .build(); + notificationService.emitResultDynamicSimulationMessage(sendMessage); + LOGGER.info("Dynamic simulation complete (resultUuid='{}')", resultContext.getResultUuid()); + }) + .block(); } catch (Exception e) { + dynamicSimulationWorkerUpdateResult.doUpdateResult(resultContext.getResultUuid(), null, null, DynamicSimulationStatus.NOT_DONE); LOGGER.error("error in consumeRun", e); + // S2142 Restore interrupted state... + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } } }; } - public Mono updateResult(UUID resultUuid, Boolean result) { + public Mono updateResult(UUID resultUuid, DynamicSimulationResult result) { Objects.requireNonNull(resultUuid); - return Mono.fromRunnable(() -> dynamicSimulationWorkerUpdateResult.doUpdateResult(resultUuid, result)); - } - - public void setFileSystem(FileSystem fs) { - this.fileSystem = fs; + List timeSeries = new ArrayList<>(result.getCurves().values()); + StringTimeSeries timeLine = result.getTimeLine(); + return Mono.zip( + timeSeriesClient.sendTimeSeries(timeSeries).subscribeOn(Schedulers.boundedElastic()), + timeSeriesClient.sendTimeSeries(Arrays.asList(timeLine)).subscribeOn(Schedulers.boundedElastic()) + ) + .map(uuidTuple -> { + UUID timeSeriesUuid = uuidTuple.getT1().getId(); + UUID timeLineUuid = uuidTuple.getT2().getId(); + DynamicSimulationStatus status = result.isOk() ? DynamicSimulationStatus.CONVERGED : DynamicSimulationStatus.DIVERGED; + + dynamicSimulationWorkerUpdateResult.doUpdateResult(resultUuid, timeSeriesUuid, timeLineUuid, status); + return result; + }); } - private void sendResultMessage(Message message) { - OUTPUT_MESSAGE_LOGGER.debug("Sending message : {}", message); - publishResult.send("publishResult-out-0", message); + @Bean + public Consumer> consumeCancel() { + return message -> { + LOGGER_BROKER_INPUT.debug("consumeCancel {}", message); + DynamicSimulationCancelContext cancelContext = DynamicSimulationCancelContext.fromMessage(message); + // TODO cancel implementation + }; } } diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerUpdateResult.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerUpdateResult.java index e1079ca4..c34a106d 100644 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerUpdateResult.java +++ b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerUpdateResult.java @@ -18,9 +18,10 @@ public DynamicSimulationWorkerUpdateResult(ResultRepository resultRepository) { } @Transactional - public void doUpdateResult(UUID resultUuid, Boolean result) { - var res = resultRepository.getOne(resultUuid); - res.setResult(result); - res.setStatus(DynamicSimulationStatus.COMPLETED.name()); + public void doUpdateResult(UUID resultUuid, UUID timeSeriesUuid, UUID timeLineUuid, DynamicSimulationStatus status) { + var res = resultRepository.findById(resultUuid).orElseThrow(); + res.setTimeSeriesId(timeSeriesUuid); + res.setTimeLineId(timeLineUuid); + res.setStatus(status.name()); } } diff --git a/src/main/java/org/gridsuite/ds/server/service/client/dynamicmapping/DynamicMappingClient.java b/src/main/java/org/gridsuite/ds/server/service/client/dynamicmapping/DynamicMappingClient.java new file mode 100644 index 00000000..ee1cf5d2 --- /dev/null +++ b/src/main/java/org/gridsuite/ds/server/service/client/dynamicmapping/DynamicMappingClient.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022-2023, 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.ds.server.service.client.dynamicmapping; + +import org.gridsuite.ds.server.dto.dynamicmapping.Script; +import reactor.core.publisher.Mono; + +/** + * @author Thang PHAM + */ +public interface DynamicMappingClient { + String API_VERSION = ""; + String DELIMITER = "/"; + String DYNAMIC_MAPPING_SCRIPT_BASE_END_POINT = "scripts"; + String DYNAMIC_MAPPING_SCRIPT_CREATE_END_POINT = DYNAMIC_MAPPING_SCRIPT_BASE_END_POINT + DELIMITER + "from"; + + Mono