Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dynamic security analysis] Expose endpoints to transfer some binary data in result table #118

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ protected ResponseEntity<Object> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -112,6 +112,39 @@ public ResponseEntity<DynamicSimulationStatus> 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<byte[]> 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<byte[]> 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<byte[]> 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"),
Expand Down
22 changes: 19 additions & 3 deletions src/main/java/org/gridsuite/ds/server/model/ResultEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,34 @@
@Entity
public class ResultEntity implements Serializable {

public interface WithoutOutputState {
public interface BasicFields {
DynamicSimulationStatus getStatus();

UUID getTimeSeriesId();

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
Expand All @@ -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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ public interface ResultRepository extends JpaRepository<ResultEntity, UUID> {
int updateStatus(@Param("resultUuids") List<UUID> 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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<UUID> updateStatus(List<UUID> resultUuids, DynamicSimulationStatus status) {
return resultRepository.updateStatus(resultUuids, status) > 0 ? resultUuids : Collections.emptyList();
}

@Transactional
public void updateResult(UUID resultUuid, List<TimeSeries<?, ?>> timeSeries, List<TimeSeries<?, ?>> timeLineSeries, DynamicSimulationStatus status, byte[] outputState) {
public void updateResult(UUID resultUuid, List<TimeSeries<?, ?>> timeSeries, List<TimeSeries<?, ?>> timeLineSeries,
DynamicSimulationStatus status, byte[] outputState, byte[] parameters, byte[] dynamicModel) {

// send time-series/timeline to time-series-server
UUID timeSeriesUuid = Optional.ofNullable(timeSeriesClient.sendTimeSeries(timeSeries))
Expand All @@ -72,22 +87,22 @@ public void updateResult(UUID resultUuid, List<TimeSeries<?, ?>> 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
@Transactional
public void insertStatus(List<UUID> 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;
}
Expand All @@ -102,10 +117,10 @@ public void delete(UUID resultUuid) {
@Override
@Transactional
public void deleteAll() {
List<ResultEntity.WithoutOutputState> resultEntities = resultRepository.findBy(ResultEntity.WithoutOutputState.class);
List<ResultEntity.BasicFields> 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());
}
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -100,7 +100,7 @@ protected DynamicSimulationResultContext fromMessage(Message<String> 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<?, ?>> timeSeries = new ArrayList<>(result.getCurves().values());
List<TimeSeries<?, ?>> timeLineSeries = new ArrayList<>();
Expand All @@ -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<DynamicSimulationRunContext> 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
Expand Down Expand Up @@ -163,8 +170,8 @@ public void preRun(DynamicSimulationRunContext runContext) {
List<EventModelConfig > 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());
Expand Down Expand Up @@ -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<DynamicModelConfig> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<DynawoSimulationParameters.SpecificLog> 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);
Expand Down
Loading
Loading