diff --git a/src/main/java/org/gridsuite/ds/server/DynamicSimulationException.java b/src/main/java/org/gridsuite/ds/server/DynamicSimulationException.java index 0618d0f..5e640a2 100644 --- a/src/main/java/org/gridsuite/ds/server/DynamicSimulationException.java +++ b/src/main/java/org/gridsuite/ds/server/DynamicSimulationException.java @@ -26,6 +26,8 @@ public enum Type { DELETE_TIME_SERIES_ERROR, MAPPING_NOT_LAST_RULE_WITH_EMPTY_FILTER_ERROR, DUMP_FILE_ERROR, + DYNAMIC_SIMULATION_PARAMETERS_ERROR, + DYNAMIC_MODEL_ERROR, } private final Type type; diff --git a/src/main/java/org/gridsuite/ds/server/RestResponseEntityExceptionHandler.java b/src/main/java/org/gridsuite/ds/server/RestResponseEntityExceptionHandler.java index 9ca5d4b..f32f13a 100644 --- a/src/main/java/org/gridsuite/ds/server/RestResponseEntityExceptionHandler.java +++ b/src/main/java/org/gridsuite/ds/server/RestResponseEntityExceptionHandler.java @@ -38,7 +38,9 @@ protected ResponseEntity handleDynamicSimulationException(DynamicSimulat EXPORT_PARAMETERS_ERROR, CREATE_TIME_SERIES_ERROR, DELETE_TIME_SERIES_ERROR, - DUMP_FILE_ERROR + DUMP_FILE_ERROR, + DYNAMIC_SIMULATION_PARAMETERS_ERROR, + DYNAMIC_MODEL_ERROR -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(exception.getMessage()); case MAPPING_NOT_PROVIDED, MAPPING_NOT_LAST_RULE_WITH_EMPTY_FILTER_ERROR diff --git a/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java b/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java index a2d1a55..4a427a1 100644 --- a/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java +++ b/src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java @@ -6,13 +6,13 @@ */ package org.gridsuite.ds.server.controller; +import com.powsybl.ws.commons.computation.dto.ReportInfos; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.apache.commons.collections4.CollectionUtils; -import com.powsybl.ws.commons.computation.dto.ReportInfos; import org.gridsuite.ds.server.dto.DynamicSimulationParametersInfos; import org.gridsuite.ds.server.dto.DynamicSimulationStatus; import org.gridsuite.ds.server.service.DynamicSimulationResultService; @@ -26,8 +26,8 @@ import java.util.List; import java.util.UUID; -import static org.gridsuite.ds.server.DynamicSimulationApi.API_VERSION; import static com.powsybl.ws.commons.computation.service.NotificationService.HEADER_USER_ID; +import static org.gridsuite.ds.server.DynamicSimulationApi.API_VERSION; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; @@ -112,6 +112,39 @@ public ResponseEntity getStatus(@Parameter(description ResponseEntity.noContent().build(); } + @GetMapping(value = "/results/{resultUuid}/output-state", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @Operation(summary = "Get the dynamic simulation output state in gzip format from the database") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation output state"), + @ApiResponse(responseCode = "204", description = "Dynamic simulation output state is empty"), + @ApiResponse(responseCode = "404", description = "Dynamic simulation result uuid has not been found")}) + public ResponseEntity getOutputState(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + byte[] result = dynamicSimulationResultService.getOutputState(resultUuid); + return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(result) : + ResponseEntity.noContent().build(); + } + + @GetMapping(value = "/results/{resultUuid}/dynamic-model", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @Operation(summary = "Get the dynamic simulation dynamic model in gzip format from the database") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation dynamic model"), + @ApiResponse(responseCode = "204", description = "Dynamic simulation dynamic model is empty"), + @ApiResponse(responseCode = "404", description = "Dynamic simulation result uuid has not been found")}) + public ResponseEntity getDynamicModel(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + byte[] result = dynamicSimulationResultService.getDynamicModel(resultUuid); + return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(result) : + ResponseEntity.noContent().build(); + } + + @GetMapping(value = "/results/{resultUuid}/parameters", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @Operation(summary = "Get the dynamic simulation parameters in gzip format from the database") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation parameters"), + @ApiResponse(responseCode = "204", description = "Dynamic simulation parameters is empty"), + @ApiResponse(responseCode = "404", description = "Dynamic simulation result uuid has not been found")}) + public ResponseEntity getParameters(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + byte[] result = dynamicSimulationResultService.getParameters(resultUuid); + return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(result) : + ResponseEntity.noContent().build(); + } + @PutMapping(value = "/results/invalidate-status", produces = "application/json") @Operation(summary = "Invalidate the dynamic simulation status from the database") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation result uuids have been invalidated"), diff --git a/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java b/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java index f386fc7..025562f 100644 --- a/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java +++ b/src/main/java/org/gridsuite/ds/server/model/ResultEntity.java @@ -25,7 +25,7 @@ @Entity public class ResultEntity implements Serializable { - public interface WithoutOutputState { + public interface BasicFields { DynamicSimulationStatus getStatus(); UUID getTimeSeriesId(); @@ -33,16 +33,26 @@ public interface WithoutOutputState { UUID getTimeLineId(); } - public interface OutputStateOnly { + public interface OutputState { byte[] getOutputState(); } - public ResultEntity(UUID id, UUID timeSeriesId, UUID timeLineId, DynamicSimulationStatus status, byte[] outputState) { + public interface Parameters { + byte[] getParameters(); + } + + public interface DynamicModel { + byte[] getDynamicModel(); + } + + public ResultEntity(UUID id, UUID timeSeriesId, UUID timeLineId, DynamicSimulationStatus status, byte[] outputState, byte[] parameters, byte[] dynamicModel) { this.id = id; this.timeSeriesId = timeSeriesId; this.timeLineId = timeLineId; this.status = status; this.outputState = outputState; + this.parameters = parameters; + this.dynamicModel = dynamicModel; } @Id @@ -62,4 +72,10 @@ public ResultEntity(UUID id, UUID timeSeriesId, UUID timeLineId, DynamicSimulati @Column(name = "outputState") private byte[] outputState; + @Column(name = "parameters") + private byte[] parameters; + + @Column(name = "dynamicModel") + private byte[] dynamicModel; + } 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 6eb1eb8..0f0af0e 100644 --- a/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java +++ b/src/main/java/org/gridsuite/ds/server/repository/ResultRepository.java @@ -32,8 +32,10 @@ public interface ResultRepository extends JpaRepository { int updateStatus(@Param("resultUuids") List resultUuids, @Param("status") DynamicSimulationStatus status); @Modifying - @Query("UPDATE ResultEntity r SET r.status = :status, r.timeSeriesId = :timeSeriesId, r.timeLineId = :timeLineId, r.outputState = :outputState" + + @Query("UPDATE ResultEntity r SET r.status = :status, r.timeSeriesId = :timeSeriesId, r.timeLineId = :timeLineId," + + " r.outputState = :outputState, r.parameters = :parameters, r.dynamicModel = :dynamicModel" + " WHERE r.id = :resultUuid") int updateResult(@Param("resultUuid") UUID resultUuid, @Param("timeSeriesId") UUID timeSeriesId, @Param("timeLineId") UUID timeLineId, - @Param("status") DynamicSimulationStatus status, @Param("outputState") byte[] outputState); + @Param("status") DynamicSimulationStatus status, @Param("outputState") byte[] outputState, + @Param("parameters") byte[] parameters, @Param("dynamicModel") byte[] dynamicModel); } diff --git a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultService.java b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultService.java index 8db46d5..005e52c 100644 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultService.java +++ b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationResultService.java @@ -33,32 +33,47 @@ public DynamicSimulationResultService(ResultRepository resultRepository, TimeSer public UUID getTimeSeriesId(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return resultRepository.findById(resultUuid, ResultEntity.WithoutOutputState.class) + return resultRepository.findById(resultUuid, ResultEntity.BasicFields.class) .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) .getTimeSeriesId(); } public UUID getTimeLineId(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return resultRepository.findById(resultUuid, ResultEntity.WithoutOutputState.class) + return resultRepository.findById(resultUuid, ResultEntity.BasicFields.class) .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) .getTimeLineId(); } public byte[] getOutputState(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return resultRepository.findById(resultUuid, ResultEntity.OutputStateOnly.class) + return resultRepository.findById(resultUuid, ResultEntity.OutputState.class) .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) .getOutputState(); } + public byte[] getParameters(UUID resultUuid) { + Objects.requireNonNull(resultUuid); + return resultRepository.findById(resultUuid, ResultEntity.Parameters.class) + .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) + .getParameters(); + } + + public byte[] getDynamicModel(UUID resultUuid) { + Objects.requireNonNull(resultUuid); + return resultRepository.findById(resultUuid, ResultEntity.DynamicModel.class) + .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) + .getDynamicModel(); + } + @Transactional public List updateStatus(List resultUuids, DynamicSimulationStatus status) { return resultRepository.updateStatus(resultUuids, status) > 0 ? resultUuids : Collections.emptyList(); } @Transactional - public void updateResult(UUID resultUuid, List> timeSeries, List> timeLineSeries, DynamicSimulationStatus status, byte[] outputState) { + public void updateResult(UUID resultUuid, List> timeSeries, List> timeLineSeries, + DynamicSimulationStatus status, byte[] outputState, byte[] parameters, byte[] dynamicModel) { // send time-series/timeline to time-series-server UUID timeSeriesUuid = Optional.ofNullable(timeSeriesClient.sendTimeSeries(timeSeries)) @@ -72,7 +87,7 @@ public void updateResult(UUID resultUuid, List> timeSeries, Lis resultUuid, timeSeriesUuid, timeLineUuid, status); // update time-series/timeline uuids, status and outputState to the db - resultRepository.updateResult(resultUuid, timeSeriesUuid, timeLineUuid, status, outputState); + resultRepository.updateResult(resultUuid, timeSeriesUuid, timeLineUuid, status, outputState, parameters, dynamicModel); } @Override @@ -80,14 +95,14 @@ public void updateResult(UUID resultUuid, List> timeSeries, Lis public void insertStatus(List resultUuids, DynamicSimulationStatus status) { Objects.requireNonNull(resultUuids); resultRepository.saveAll(resultUuids.stream() - .map(uuid -> new ResultEntity(uuid, null, null, status, null)).toList()); + .map(uuid -> new ResultEntity(uuid, null, null, status, null, null, null)).toList()); } @Override @Transactional public void delete(UUID resultUuid) { Objects.requireNonNull(resultUuid); - ResultEntity.WithoutOutputState resultEntity = resultRepository.findById(resultUuid, ResultEntity.WithoutOutputState.class).orElse(null); + ResultEntity.BasicFields resultEntity = resultRepository.findById(resultUuid, ResultEntity.BasicFields.class).orElse(null); if (resultEntity == null) { return; } @@ -102,10 +117,10 @@ public void delete(UUID resultUuid) { @Override @Transactional public void deleteAll() { - List resultEntities = resultRepository.findBy(ResultEntity.WithoutOutputState.class); + List resultEntities = resultRepository.findBy(ResultEntity.BasicFields.class); // call time series client to delete time-series and timeline - for (ResultEntity.WithoutOutputState resultEntity : resultEntities) { + for (ResultEntity.BasicFields resultEntity : resultEntities) { timeSeriesClient.deleteTimeSeriesGroup(resultEntity.getTimeSeriesId()); timeSeriesClient.deleteTimeSeriesGroup(resultEntity.getTimeLineId()); } @@ -117,7 +132,7 @@ public void deleteAll() { @Override public DynamicSimulationStatus findStatus(UUID resultUuid) { Objects.requireNonNull(resultUuid); - return resultRepository.findById(resultUuid, ResultEntity.WithoutOutputState.class) + return resultRepository.findById(resultUuid, ResultEntity.BasicFields.class) .orElseThrow(() -> new DynamicSimulationException(RESULT_UUID_NOT_FOUND, MSG_RESULT_UUID_NOT_FOUND + resultUuid)) .getStatus(); } 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 c77cb8e..695d423 100644 --- a/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java +++ b/src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java @@ -57,7 +57,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import static org.gridsuite.ds.server.DynamicSimulationException.Type.DUMP_FILE_ERROR; +import static org.gridsuite.ds.server.DynamicSimulationException.Type.*; import static org.gridsuite.ds.server.service.DynamicSimulationService.COMPUTATION_TYPE; /** @@ -100,7 +100,7 @@ protected DynamicSimulationResultContext fromMessage(Message message) { return DynamicSimulationResultContext.fromMessage(message, objectMapper); } - public void updateResult(UUID resultUuid, DynamicSimulationResult result, byte[] outputState) { + public void updateResult(UUID resultUuid, DynamicSimulationResult result, byte[] outputState, byte[] parameters, byte[] dynamicModel) { Objects.requireNonNull(resultUuid); List> timeSeries = new ArrayList<>(result.getCurves().values()); List> timeLineSeries = new ArrayList<>(); @@ -123,19 +123,26 @@ public void updateResult(UUID resultUuid, DynamicSimulationResult result, byte[] DynamicSimulationStatus.CONVERGED : DynamicSimulationStatus.DIVERGED; - resultService.updateResult(resultUuid, timeSeries, timeLineSeries, status, outputState); + resultService.updateResult(resultUuid, timeSeries, timeLineSeries, status, outputState, parameters, dynamicModel); } @Override protected void saveResult(Network network, AbstractResultContext resultContext, DynamicSimulationResult result) { // read dump file - Path dumpDir = getDumpDir(resultContext.getRunContext().getDynamicSimulationParameters()); + DynamicSimulationParameters parameters = resultContext.getRunContext().getDynamicSimulationParameters(); + Path dumpDir = getDumpDir(parameters); byte[] outputState = null; if (dumpDir != null) { outputState = zipDumpFile(dumpDir); } - updateResult(resultContext.getResultUuid(), result, outputState); + // serialize parameters to reuse in dynamic security analysis + byte[] zippedJsonParameters = zipParameters(parameters); + + // serialize dynamic model to reuse in dynamic security analysis + byte[] zippedJsonDynamicModel = zipDynamicModel(resultContext.getRunContext().getDynamicModelContent()); + + updateResult(resultContext.getResultUuid(), result, outputState, zippedJsonParameters, zippedJsonDynamicModel); } @Override @@ -163,8 +170,8 @@ public void preRun(DynamicSimulationRunContext runContext) { List eventModel = parametersService.getEventModel(parametersInfos.getEvents()); // set start and stop times - parameters.setStartTime(parametersInfos.getStartTime().intValue()); // TODO remove intValue() when correct startTime to double in powsybl - parameters.setStopTime(parametersInfos.getStopTime().intValue()); // TODO remove intValue() when correct stopTime to double in powsybl + parameters.setStartTime(parametersInfos.getStartTime()); + parameters.setStopTime(parametersInfos.getStopTime()); // groovy scripts String curveModel = parametersService.getCurveModel(parametersInfos.getCurves()); @@ -265,6 +272,28 @@ private byte[] zipDumpFile(Path dumpDir) { return outputState; } + private byte[] zipParameters(DynamicSimulationParameters parameters) { + byte[] zippedJsonParameters; + try { + String jsonParameters = objectMapper.writeValueAsString(parameters); + zippedJsonParameters = Utils.zip(jsonParameters); + } catch (IOException e) { + throw new DynamicSimulationException(DYNAMIC_SIMULATION_PARAMETERS_ERROR, "Error occurred while zipping the dynamic simulation parameters"); + } + return zippedJsonParameters; + } + + private byte[] zipDynamicModel(List dynamicModelContent) { + byte[] zippedJsonDynamicModelContent; + try { + String jsonDynamicModelContent = objectMapper.writeValueAsString(dynamicModelContent); + zippedJsonDynamicModelContent = Utils.zip(jsonDynamicModelContent); + } catch (IOException e) { + throw new DynamicSimulationException(DYNAMIC_MODEL_ERROR, "Error occurred while zipping the dynamic model"); + } + return zippedJsonDynamicModelContent; + } + private Path createWorkingDirectory() { Path workDir; Path localDir = getComputationManager().getLocalDir(); diff --git a/src/main/java/org/gridsuite/ds/server/service/parameters/impl/ParametersServiceImpl.java b/src/main/java/org/gridsuite/ds/server/service/parameters/impl/ParametersServiceImpl.java index aef2e48..d7afa21 100644 --- a/src/main/java/org/gridsuite/ds/server/service/parameters/impl/ParametersServiceImpl.java +++ b/src/main/java/org/gridsuite/ds/server/service/parameters/impl/ParametersServiceImpl.java @@ -110,6 +110,10 @@ public DynamicSimulationParameters getDynamicSimulationParameters(byte[] dynamic dynawoSimulationParameters.setModelsParameters(modelsParameters); parameters.addExtension(DynawoSimulationParameters.class, dynawoSimulationParameters); + // TODO : a bug in powsybl-dynawo while deserializing in dynamic security analysis server, TO REMOVE + Set specificLogs = EnumSet.of(DynawoSimulationParameters.SpecificLog.NETWORK); + dynawoSimulationParameters.setSpecificLogs(specificLogs); + // --- SOLVER PAR --- // // solver from input parameter SolverInfos inputSolver = inputParameters.getSolvers().stream().filter(elem -> elem.getId().equals(inputParameters.getSolverId())).findFirst().orElse(null); diff --git a/src/main/java/org/gridsuite/ds/server/utils/Utils.java b/src/main/java/org/gridsuite/ds/server/utils/Utils.java index f72d7f8..1e9a737 100644 --- a/src/main/java/org/gridsuite/ds/server/utils/Utils.java +++ b/src/main/java/org/gridsuite/ds/server/utils/Utils.java @@ -7,6 +7,8 @@ package org.gridsuite.ds.server.utils; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.dynawo.suppliers.Property; import com.powsybl.dynawo.suppliers.PropertyBuilder; import com.powsybl.dynawo.suppliers.PropertyType; @@ -36,9 +38,7 @@ private Utils() { } public static List convertStringToList(String stringArray) { - List converted = new ArrayList<>(); - converted.addAll(Arrays.asList(stringArray.split(","))); - return converted; + return new ArrayList<>(Arrays.asList(stringArray.split(","))); } public static Property convertProperty(EventPropertyInfos property) { @@ -90,9 +90,8 @@ public static Property convertProperty(BasicProperty property) { return propertyBuilder.build(); } - public static byte[] zip(Path filePath) { - try (InputStream is = Files.newInputStream(filePath); - ByteArrayOutputStream os = new ByteArrayOutputStream(); + private static byte[] zip(InputStream is) throws IOException { + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); GZIPOutputStream zipOs = new GZIPOutputStream(os)) { byte[] buffer = new byte[1024]; int length; @@ -101,22 +100,52 @@ public static byte[] zip(Path filePath) { } zipOs.finish(); return os.toByteArray(); + } + } + + public static byte[] zip(String content) throws IOException { + try (InputStream is = new ByteArrayInputStream(content.getBytes())) { + return zip(is); + } + } + + public static byte[] zip(Path filePath) { + try (InputStream is = Files.newInputStream(filePath)) { + return zip(is); } catch (IOException e) { throw new UncheckedIOException("Error occurred while zipping the file " + filePath.toAbsolutePath(), e); } } - public static void unzip(byte[] zippedBytes, Path filePath) { + private static void unzipToStream(byte[] zippedBytes, OutputStream outputStream) throws IOException { try (ByteArrayInputStream is = new ByteArrayInputStream(zippedBytes); - FileOutputStream fos = new FileOutputStream(new File(filePath.toUri())); - GZIPInputStream zipIs = new GZIPInputStream(is)) { + GZIPInputStream zipIs = new GZIPInputStream(is); + BufferedOutputStream bufferedOut = new BufferedOutputStream(outputStream)) { byte[] buffer = new byte[1024]; int length; while ((length = zipIs.read(buffer)) > 0) { - fos.write(buffer, 0, length); + bufferedOut.write(buffer, 0, length); } - } catch (IOException e) { - throw new UncheckedIOException("Error occurred while unzipping a zipped content to the file " + filePath.toAbsolutePath(), e); + } + } + + public static void unzip(byte[] zippedBytes, Path filePath) throws IOException { + try (FileOutputStream fos = new FileOutputStream(new File(filePath.toUri()))) { + unzipToStream(zippedBytes, fos); + } + } + + public static T unzip(byte[] zippedBytes, ObjectMapper objectMapper, TypeReference valueTypeRef) throws IOException { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + unzipToStream(zippedBytes, bos); + return objectMapper.readValue(bos.toByteArray(), valueTypeRef); + } + } + + public static T unzip(byte[] zippedBytes, ObjectMapper objectMapper, Class valueType) throws IOException { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + unzipToStream(zippedBytes, bos); + return objectMapper.readValue(bos.toByteArray(), valueType); } } } diff --git a/src/main/resources/db/changelog/changesets/changelog_20241205T235011Z.xml b/src/main/resources/db/changelog/changesets/changelog_20241205T235011Z.xml new file mode 100644 index 0000000..42592c0 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20241205T235011Z.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index e8c1d7d..40bc658 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -12,3 +12,6 @@ databaseChangeLog: - include: file: changesets/changelog_20241029T195403Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20241205T235011Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/ds/server/controller/DynamicSimulationControllerIEEE14Test.java b/src/test/java/org/gridsuite/ds/server/controller/DynamicSimulationControllerIEEE14Test.java index 54a50d8..5288810 100644 --- a/src/test/java/org/gridsuite/ds/server/controller/DynamicSimulationControllerIEEE14Test.java +++ b/src/test/java/org/gridsuite/ds/server/controller/DynamicSimulationControllerIEEE14Test.java @@ -6,13 +6,16 @@ */ package org.gridsuite.ds.server.controller; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; +import com.powsybl.dynamicsimulation.DynamicSimulationParameters; import com.powsybl.dynamicsimulation.DynamicSimulationResult; import com.powsybl.dynamicsimulation.json.DynamicSimulationResultDeserializer; +import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig; import com.powsybl.iidm.network.Importers; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VariantManagerConstants; @@ -29,7 +32,6 @@ import org.gridsuite.ds.server.dto.dynamicmapping.ParameterFile; import org.gridsuite.ds.server.dto.event.EventInfos; import org.gridsuite.ds.server.dto.timeseries.TimeSeriesGroupInfos; -import org.gridsuite.ds.server.service.DynamicSimulationResultService; import org.gridsuite.ds.server.service.client.dynamicmapping.DynamicMappingClientTest; import org.gridsuite.ds.server.service.client.timeseries.TimeSeriesClientTest; import org.gridsuite.ds.server.utils.Utils; @@ -57,8 +59,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** @@ -99,9 +100,6 @@ public class DynamicSimulationControllerIEEE14Test extends AbstractDynamicSimula @Autowired private ObjectMapper objectMapper; - @Autowired - private DynamicSimulationResultService dynamicSimulationResultService; - @Override public OutputDestination getOutputDestination() { return output; @@ -250,15 +248,63 @@ public void test01GivenCurvesAndEvents() throws Exception { assertThat(objectMapper.readTree(jsonResultTimeSeries)).isEqualTo(objectMapper.readTree(jsonExpectedTimeSeries)); // check dump file not empty - byte[] outputState = dynamicSimulationResultService.getOutputState(runUuid); - assertThat(outputState) + result = mockMvc.perform( + get("/v1/results/{resultUuid}/output-state", runUuid)) + .andExpect(status().isOk()) + .andReturn(); + byte[] zippedOutputState = result.getResponse().getContentAsByteArray(); + + assertThat(zippedOutputState) .withFailMessage("Expecting Output state of dynamic simulation to be not empty but was empty.") .isNotEmpty(); - logger.info("Size of zipped output state = {} KB ", outputState.length / 1024); + logger.info("Size of zipped output state = {} KB ", zippedOutputState.length / 1024); // export dump file content to manual check File file = new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource(".")).getFile() + outputDir + RESOURCE_PATH_DELIMITER + "outputState.dmp"); - Utils.unzip(outputState, file.toPath()); + Utils.unzip(zippedOutputState, file.toPath()); + + // check dynamic model persisted in result in gzip format not empty + result = mockMvc.perform( + get("/v1/results/{resultUuid}/dynamic-model", runUuid)) + .andExpect(status().isOk()) + .andReturn(); + byte[] zippedDynamicModel = result.getResponse().getContentAsByteArray(); + + assertThat(zippedDynamicModel) + .withFailMessage("Expecting dynamic model of dynamic simulation to be not empty but was empty.") + .isNotEmpty(); + logger.info("Size of zipped dynamic model = {} B ", zippedDynamicModel.length); + + // export dynamic model in json and dump files to manual check + List dynamicModel = Utils.unzip(zippedDynamicModel, objectMapper, new TypeReference<>() { }); + String jsonDynamicModel = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dynamicModel); + FileUtils.writeBytesToFile(this, outputDir + RESOURCE_PATH_DELIMITER + "dynamicModel.json", jsonDynamicModel.getBytes()); + + file = new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource(".")).getFile() + + outputDir + RESOURCE_PATH_DELIMITER + "dynamicModel.dmp"); + Utils.unzip(zippedDynamicModel, file.toPath()); + + // check parameters persisted in result in gzip format not empty + result = mockMvc.perform( + get("/v1/results/{resultUuid}/parameters", runUuid)) + .andExpect(status().isOk()) + .andReturn(); + byte[] zippedDynamicSimulationParameters = result.getResponse().getContentAsByteArray(); + + assertThat(zippedDynamicSimulationParameters) + .withFailMessage("Expecting parameters of dynamic simulation to be not empty but was empty.") + .isNotEmpty(); + logger.info("Size of zipped parameters = {} KB ", zippedDynamicSimulationParameters.length / 1024); + + // export dynamic model in json and dump files to manual check + DynamicSimulationParameters dynamicSimulationParameters = Utils.unzip(zippedDynamicSimulationParameters, objectMapper, DynamicSimulationParameters.class); + String jsonDynamicSimulationParameters = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dynamicSimulationParameters); + FileUtils.writeBytesToFile(this, outputDir + RESOURCE_PATH_DELIMITER + "dynamicSimulationParameters.json", jsonDynamicSimulationParameters.getBytes()); + + file = new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource(".")).getFile() + + outputDir + RESOURCE_PATH_DELIMITER + "dynamicSimulationParameters.dmp"); + Utils.unzip(zippedDynamicSimulationParameters, file.toPath()); + } } diff --git a/src/test/java/org/gridsuite/ds/server/service/DynamicSimulationResultServiceTest.java b/src/test/java/org/gridsuite/ds/server/service/DynamicSimulationResultServiceTest.java index 2f31124..d45dd7d 100644 --- a/src/test/java/org/gridsuite/ds/server/service/DynamicSimulationResultServiceTest.java +++ b/src/test/java/org/gridsuite/ds/server/service/DynamicSimulationResultServiceTest.java @@ -43,7 +43,7 @@ @SpringBootTest public class DynamicSimulationResultServiceTest { - static Logger LOGGER = LoggerFactory.getLogger(DynamicSimulationResultServiceTest.class); + static final Logger LOGGER = LoggerFactory.getLogger(DynamicSimulationResultServiceTest.class); @Autowired ResultRepository resultRepository; @@ -103,6 +103,8 @@ public void testCrud() { List.of(mock(StoredDoubleTimeSeries.class)), List.of(mock(StringTimeSeries.class)), DynamicSimulationStatus.CONVERGED, + null, + null, null ); @@ -117,6 +119,8 @@ public void testCrud() { null, null, DynamicSimulationStatus.CONVERGED, + null, + null, null ); // no uuids time-series and timeline @@ -132,8 +136,8 @@ public void testCrud() { // --- delete all --- // resultRepository.saveAllAndFlush(List.of( - new ResultEntity(uuidGeneratorService.generate(), null, null, DynamicSimulationStatus.RUNNING, null), - new ResultEntity(uuidGeneratorService.generate(), null, null, DynamicSimulationStatus.RUNNING, null) + new ResultEntity(uuidGeneratorService.generate(), null, null, DynamicSimulationStatus.RUNNING, null, null, null), + new ResultEntity(uuidGeneratorService.generate(), null, null, DynamicSimulationStatus.RUNNING, null, null, null) )).stream().map(ResultEntity::getId).toList(); dynamicSimulationResultService.deleteAll();