diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5642ea..8e65bda3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Renaming power fields in `EvModel` [#208](https://github.com/ie3-institute/simonaAPI/issues/208) +- Enhancing external data connections [#219](https://github.com/ie3-institute/simonaAPI/issues/219) ## [0.5.0] - 2024-08-09 diff --git a/src/main/java/edu/ie3/simona/api/ExtLinkInterface.java b/src/main/java/edu/ie3/simona/api/ExtLinkInterface.java index e69a27c1..0761008a 100644 --- a/src/main/java/edu/ie3/simona/api/ExtLinkInterface.java +++ b/src/main/java/edu/ie3/simona/api/ExtLinkInterface.java @@ -6,20 +6,25 @@ package edu.ie3.simona.api; -import edu.ie3.simona.api.data.ExtDataSimulation; +import edu.ie3.simona.api.exceptions.NoExtSimulationException; +import edu.ie3.simona.api.simulation.ExtSimAdapterData; import edu.ie3.simona.api.simulation.ExtSimulation; -import java.util.List; /** * Every external simulation has to provide a class {@code edu.ie3.simona.api.ExtLink} which * implements this interface. - * - *

{@link #getExtSimulation()} and {@link #getExtDataSimulations()} can return references to the - * same object, if that object implements both {@link ExtSimulation} and one or more variants of - * {@link ExtDataSimulation}. */ public interface ExtLinkInterface { - ExtSimulation getExtSimulation(); + /** Returns the external simulation. */ + default ExtSimulation getExtSimulation() { + throw new NoExtSimulationException(this.getClass()); + } - List getExtDataSimulations(); + /** + * Method to set up an external simulation. Everything that needs to be set up before the external + * simulation can be retrieved should be done here. + * + * @param data used for setting up the external simulation + */ + void setup(ExtSimAdapterData data); } diff --git a/src/main/java/edu/ie3/simona/api/data/ExtData.java b/src/main/java/edu/ie3/simona/api/data/ExtDataConnection.java similarity index 61% rename from src/main/java/edu/ie3/simona/api/data/ExtData.java rename to src/main/java/edu/ie3/simona/api/data/ExtDataConnection.java index fd35fe7a..9de5c6a3 100644 --- a/src/main/java/edu/ie3/simona/api/data/ExtData.java +++ b/src/main/java/edu/ie3/simona/api/data/ExtDataConnection.java @@ -6,4 +6,5 @@ package edu.ie3.simona.api.data; -public interface ExtData {} +/** Interface that defines a data connection between SIMONA and an external simulation. */ +public interface ExtDataConnection {} diff --git a/src/main/java/edu/ie3/simona/api/data/ExtDataSimulation.java b/src/main/java/edu/ie3/simona/api/data/ExtDataSimulation.java deleted file mode 100644 index ffb06ec1..00000000 --- a/src/main/java/edu/ie3/simona/api/data/ExtDataSimulation.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.api.data; - -/** - * Represents a data flow inside the external simulation. An external simulation can operate - * multiple data flows of different types. For each data type, there needs to be an interface - * extending this interface which provides methods that receive a data adapter. - * - *

See {@link edu.ie3.simona.api.data.ev.ExtEvSimulation} for an example. - */ -public interface ExtDataSimulation {} diff --git a/src/main/java/edu/ie3/simona/api/data/ExtInputDataConnection.java b/src/main/java/edu/ie3/simona/api/data/ExtInputDataConnection.java new file mode 100644 index 00000000..d8a29a75 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/ExtInputDataConnection.java @@ -0,0 +1,21 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.api.data; + +import org.apache.pekko.actor.ActorRef; + +public interface ExtInputDataConnection extends ExtDataConnection { + + /** + * Sets the actor refs for data and control flow. + * + * @param dataService actor ref to the adapter of the data service for schedule activation + * messages + * @param extSimAdapter actor ref to the extSimAdapter + */ + void setActorRefs(ActorRef dataService, ActorRef extSimAdapter); +} diff --git a/src/main/java/edu/ie3/simona/api/data/ExtOutputData.java b/src/main/java/edu/ie3/simona/api/data/ExtOutputData.java deleted file mode 100644 index 80d949a4..00000000 --- a/src/main/java/edu/ie3/simona/api/data/ExtOutputData.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * © 2024. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.api.data; - -/** - * Interface for a connection between SIMONA and an external simulation with data flow from SIMONA - * to external - */ -public interface ExtOutputData extends ExtData {} diff --git a/src/main/java/edu/ie3/simona/api/data/ExtOutputDataConnection.java b/src/main/java/edu/ie3/simona/api/data/ExtOutputDataConnection.java new file mode 100644 index 00000000..6ac15e75 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/ExtOutputDataConnection.java @@ -0,0 +1,27 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.api.data; + +import org.apache.pekko.actor.ActorRef; + +/** + * Interface for a connection between SIMONA and an external simulation with data flow from SIMONA + * to external + */ +public interface ExtOutputDataConnection extends ExtDataConnection { + + /** + * Sets the actor refs for data and control flow + * + * @param extResultDataService actor ref to the adapter of the data service for data messages + * @param dataServiceActivation actor ref to the adapter of the data service for schedule + * activation messages + * @param extSimAdapter actor ref to the extSimAdapter + */ + void setActorRefs( + ActorRef extResultDataService, ActorRef dataServiceActivation, ActorRef extSimAdapter); +} diff --git a/src/main/java/edu/ie3/simona/api/data/em/ExtEmData.java b/src/main/java/edu/ie3/simona/api/data/em/ExtEmDataConnection.java similarity index 65% rename from src/main/java/edu/ie3/simona/api/data/em/ExtEmData.java rename to src/main/java/edu/ie3/simona/api/data/em/ExtEmDataConnection.java index 3e80ddf4..5a0bbaa6 100644 --- a/src/main/java/edu/ie3/simona/api/data/em/ExtEmData.java +++ b/src/main/java/edu/ie3/simona/api/data/em/ExtEmDataConnection.java @@ -7,16 +7,18 @@ package edu.ie3.simona.api.data.em; import edu.ie3.datamodel.models.value.PValue; -import edu.ie3.simona.api.data.ExtData; -import edu.ie3.simona.api.data.ExtInputDataContainer; +import edu.ie3.datamodel.models.value.Value; +import edu.ie3.simona.api.data.ExtInputDataConnection; import edu.ie3.simona.api.data.em.ontology.EmDataMessageFromExt; import edu.ie3.simona.api.data.em.ontology.ProvideEmSetPointData; import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage; import java.util.*; +import java.util.stream.Collectors; import org.apache.pekko.actor.ActorRef; +import org.slf4j.Logger; /** Enables data connection of em data between SIMONA and SimonaAPI */ -public class ExtEmData implements ExtData { +public class ExtEmDataConnection implements ExtInputDataConnection { /** Actor reference to service that handles ev data within SIMONA */ private ActorRef emDataService; @@ -27,16 +29,33 @@ public class ExtEmData implements ExtData { /** Assets that provide primary data to SIMONA */ private final Map extEmMapping; - public ExtEmData(Map extEmMapping) { + public ExtEmDataConnection(Map extEmMapping) { this.extEmMapping = extEmMapping; } - /** Sets the actor refs for data and control flow */ + @Override public void setActorRefs(ActorRef emDataService, ActorRef extSimAdapter) { this.emDataService = emDataService; this.extSimAdapter = extSimAdapter; } + public void convertAndSend( + long tick, Map data, Optional maybeNextTick, Logger log) { + // filtering the data and converting the keys + Map convertedMap = + data.entrySet().stream() + .filter(e -> extEmMapping.containsKey(e.getKey())) + .collect( + Collectors.toMap(e -> extEmMapping.get(e.getKey()), e -> (PValue) e.getValue())); + + if (convertedMap.isEmpty()) { + log.warn("No em data found! Sending no em data to SIMONA for tick {}.", tick); + } else { + log.debug("Provided SIMONA with em data."); + provideEmData(tick, convertedMap, maybeNextTick); + } + } + /** Returns a list of the uuids of the em agents that expect external set points */ public List getControlledEms() { return extEmMapping.values().stream().toList(); @@ -59,26 +78,4 @@ public void sendExtMsg(EmDataMessageFromExt msg) { // we need to schedule data receiver activation with scheduler extSimAdapter.tell(new ScheduleDataServiceMessage(emDataService), ActorRef.noSender()); } - - /** Converts an input data package from an external simulation to a map of set points */ - public Map convertExternalInputToEmSetPoints( - ExtInputDataContainer extInputDataContainer) { - Map emDataForSimona = new HashMap<>(); - extInputDataContainer - .getSimonaInputMap() - .forEach( - (id, value) -> { - if (extEmMapping.containsKey(id)) { - if (value instanceof PValue pValue) { - emDataForSimona.put(extEmMapping.get(id), pValue); - } else { - throw new IllegalArgumentException("EmData can only handle PValue's!"); - } - } else { - throw new IllegalArgumentException( - "Input for asset with id " + id + " was provided, but it wasn't requested!"); - } - }); - return emDataForSimona; - } } diff --git a/src/main/java/edu/ie3/simona/api/data/ev/ExtEvData.java b/src/main/java/edu/ie3/simona/api/data/ev/ExtEvDataConnection.java similarity index 92% rename from src/main/java/edu/ie3/simona/api/data/ev/ExtEvData.java rename to src/main/java/edu/ie3/simona/api/data/ev/ExtEvDataConnection.java index 303ff1fc..53a71e87 100644 --- a/src/main/java/edu/ie3/simona/api/data/ev/ExtEvData.java +++ b/src/main/java/edu/ie3/simona/api/data/ev/ExtEvDataConnection.java @@ -6,28 +6,30 @@ package edu.ie3.simona.api.data.ev; -import edu.ie3.simona.api.data.ExtData; +import edu.ie3.simona.api.data.ExtInputDataConnection; import edu.ie3.simona.api.data.ev.model.EvModel; import edu.ie3.simona.api.data.ev.ontology.*; import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; import org.apache.pekko.actor.ActorRef; -public class ExtEvData implements ExtData { +public class ExtEvDataConnection implements ExtInputDataConnection { /** Data message queue containing messages from SIMONA */ public final LinkedBlockingQueue receiveTriggerQueue = new LinkedBlockingQueue<>(); /** Actor reference to service that handles ev data within SIMONA */ - private final ActorRef dataService; + private ActorRef dataService; /** Actor reference to adapter that handles scheduler control flow in SIMONA */ - private final ActorRef extSimAdapter; + private ActorRef extSimAdapter; - // important trigger queue must be the same as hold in actor - // to make it safer one might consider asking the actor for ara reference on its trigger queue?! - public ExtEvData(ActorRef dataService, ActorRef extSimAdapter) { + @Override + public void setActorRefs(ActorRef dataService, ActorRef extSimAdapter) { this.dataService = dataService; this.extSimAdapter = extSimAdapter; } diff --git a/src/main/java/edu/ie3/simona/api/data/ev/ExtEvSimulation.java b/src/main/java/edu/ie3/simona/api/data/ev/ExtEvSimulation.java deleted file mode 100644 index b14fb1f3..00000000 --- a/src/main/java/edu/ie3/simona/api/data/ev/ExtEvSimulation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.api.data.ev; - -import edu.ie3.simona.api.data.ExtDataSimulation; - -/** - * An external simulation that provides an ev mobility simulation should implement this interface - * and handle the ExtEvData that is handed over. - */ -public interface ExtEvSimulation extends ExtDataSimulation { - - /** - * Hand over an ExtEvData which enables communication regarding ev movements. - * - * @param evData the ev data - */ - void setExtEvData(ExtEvData evData); -} diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnection.java similarity index 66% rename from src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java rename to src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnection.java index 048353b8..10fe1842 100644 --- a/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnection.java @@ -7,18 +7,19 @@ package edu.ie3.simona.api.data.primarydata; import edu.ie3.datamodel.models.value.Value; -import edu.ie3.simona.api.data.ExtData; -import edu.ie3.simona.api.data.ExtInputDataContainer; +import edu.ie3.simona.api.data.ExtInputDataConnection; import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage; import edu.ie3.simona.api.data.primarydata.ontology.PrimaryDataMessageFromExt; import edu.ie3.simona.api.data.primarydata.ontology.ProvidePrimaryData; import java.util.*; +import java.util.stream.Collectors; import org.apache.pekko.actor.ActorRef; +import org.slf4j.Logger; /** Enables data connection of primary data between SIMONA and SimonaAPI */ -public class ExtPrimaryData implements ExtData { +public class ExtPrimaryDataConnection implements ExtInputDataConnection { - /** Actor reference to service that handles ev data within SIMONA */ + /** Actor reference to service that handles primary data within SIMONA */ private ActorRef dataService; /** Actor reference to adapter that handles scheduler control flow in SIMONA */ @@ -27,16 +28,33 @@ public class ExtPrimaryData implements ExtData { /** Assets that provide primary data to SIMONA */ private final Map extPrimaryDataMapping; - public ExtPrimaryData(Map extPrimaryDataMapping) { + public ExtPrimaryDataConnection(Map extPrimaryDataMapping) { this.extPrimaryDataMapping = extPrimaryDataMapping; } - /** Sets the actor refs for data and control flow */ + @Override public void setActorRefs(ActorRef dataService, ActorRef extSimAdapter) { this.dataService = dataService; this.extSimAdapter = extSimAdapter; } + public void convertAndSend( + long tick, Map data, Optional maybeNextTick, Logger log) { + // filtering the data and converting the keys + Map convertedMap = + data.entrySet().stream() + .filter(e -> extPrimaryDataMapping.containsKey(e.getKey())) + .collect( + Collectors.toMap(e -> extPrimaryDataMapping.get(e.getKey()), Map.Entry::getValue)); + + if (convertedMap.isEmpty()) { + log.warn("No primary data found! Sending no primary data to SIMONA for tick {}.", tick); + } else { + log.debug("Provided SIMONA with primary data."); + providePrimaryData(tick, convertedMap, maybeNextTick); + } + } + /** Returns a list of the uuids of the system participants that expect external primary data */ public List getPrimaryDataAssets() { return extPrimaryDataMapping.values().stream().toList(); @@ -60,22 +78,4 @@ public void sendExtMsg(PrimaryDataMessageFromExt msg) { // we need to schedule data receiver activation with scheduler extSimAdapter.tell(new ScheduleDataServiceMessage(dataService), ActorRef.noSender()); } - - /** Converts an input data package from an external simulation to a map of primary data */ - public Map convertExternalInputToPrimaryData( - ExtInputDataContainer extInputDataContainer) { - Map primaryDataForSimona = new HashMap<>(); - extInputDataContainer - .getSimonaInputMap() - .forEach( - (id, value) -> { - if (extPrimaryDataMapping.containsKey(id)) { - primaryDataForSimona.put(extPrimaryDataMapping.get(id), value); - } else { - throw new IllegalArgumentException( - "Input for asset with id " + id + " was provided, but it wasn't requested!"); - } - }); - return primaryDataForSimona; - } } diff --git a/src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java b/src/main/java/edu/ie3/simona/api/data/results/ExtResultDataConnection.java similarity index 97% rename from src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java rename to src/main/java/edu/ie3/simona/api/data/results/ExtResultDataConnection.java index 389d62b7..de90fa41 100644 --- a/src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java +++ b/src/main/java/edu/ie3/simona/api/data/results/ExtResultDataConnection.java @@ -9,7 +9,7 @@ import edu.ie3.datamodel.models.result.ModelResultEntity; import edu.ie3.datamodel.models.result.NodeResult; import edu.ie3.datamodel.models.result.system.SystemParticipantResult; -import edu.ie3.simona.api.data.ExtOutputData; +import edu.ie3.simona.api.data.ExtOutputDataConnection; import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage; import edu.ie3.simona.api.data.results.ontology.ProvideResultEntities; import edu.ie3.simona.api.data.results.ontology.RequestResultEntities; @@ -23,7 +23,7 @@ import org.apache.pekko.actor.ActorRef; /** Enables data connection of results between SIMONA and SimonaAPI */ -public class ExtResultData implements ExtOutputData { +public class ExtResultDataConnection implements ExtOutputDataConnection { /** Data message queue containing messages from SIMONA */ public final LinkedBlockingQueue receiveTriggerQueue = @@ -44,7 +44,7 @@ public class ExtResultData implements ExtOutputData { /** Map uuid to external id of system participants */ private final Map participantResultAssetMapping; - public ExtResultData( + public ExtResultDataConnection( Map participantResultAssetMapping, Map gridResultAssetMapping) { this.participantResultAssetMapping = participantResultAssetMapping; this.gridResultAssetMapping = gridResultAssetMapping; diff --git a/src/main/java/edu/ie3/simona/api/exceptions/ConversionException.java b/src/main/java/edu/ie3/simona/api/exceptions/ConversionException.java deleted file mode 100644 index c9fb5477..00000000 --- a/src/main/java/edu/ie3/simona/api/exceptions/ConversionException.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * © 2024. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.api.exceptions; - -public class ConversionException extends Exception { - - public ConversionException(final String message) { - super(message); - } -} diff --git a/src/main/java/edu/ie3/simona/api/exceptions/NoExtSimulationException.java b/src/main/java/edu/ie3/simona/api/exceptions/NoExtSimulationException.java new file mode 100644 index 00000000..f4a242b3 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/exceptions/NoExtSimulationException.java @@ -0,0 +1,20 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.api.exceptions; + +import edu.ie3.simona.api.ExtLinkInterface; + +public class NoExtSimulationException extends RuntimeException { + + public NoExtSimulationException(Class linkClass) { + this("No external simulation was set up in ExtLinkInterface: ." + linkClass.getSimpleName()); + } + + public NoExtSimulationException(final String message) { + super(message); + } +} diff --git a/src/main/java/edu/ie3/simona/api/simulation/ExtCoSimulation.java b/src/main/java/edu/ie3/simona/api/simulation/ExtCoSimulation.java index 9d926d51..f07ec9bd 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/ExtCoSimulation.java +++ b/src/main/java/edu/ie3/simona/api/simulation/ExtCoSimulation.java @@ -7,14 +7,18 @@ package edu.ie3.simona.api.simulation; import edu.ie3.datamodel.models.result.ModelResultEntity; +import edu.ie3.datamodel.models.value.Value; import edu.ie3.simona.api.data.DataQueueExtSimulationExtSimulator; import edu.ie3.simona.api.data.ExtInputDataContainer; -import edu.ie3.simona.api.data.em.ExtEmData; -import edu.ie3.simona.api.data.primarydata.ExtPrimaryData; +import edu.ie3.simona.api.data.em.ExtEmDataConnection; +import edu.ie3.simona.api.data.primarydata.ExtPrimaryDataConnection; import edu.ie3.simona.api.data.results.ExtResultContainer; -import edu.ie3.simona.api.data.results.ExtResultData; +import edu.ie3.simona.api.data.results.ExtResultDataConnection; +import edu.ie3.simona.api.simulation.mapping.DataType; +import edu.ie3.simona.api.simulation.mapping.ExtEntityMapping; import java.util.Map; import java.util.Optional; +import java.util.UUID; import org.slf4j.Logger; /** @@ -42,42 +46,133 @@ protected ExtCoSimulation(String simulationName, String extSimulatorName) { this.dataQueueSimonaApiToExtCoSimulator = new DataQueueExtSimulationExtSimulator<>(); } - /** Function to send primary data to SIMONA using ExtPrimaryData */ - protected void sendPrimaryDataToSimona(ExtPrimaryData extPrimaryData, long tick, Logger log) - throws InterruptedException { + /** + * Builds an {@link ExtPrimaryDataConnection}. + * + * @param mapping between the external simulation and SIMONA. + * @param log logger + * @return an ext primary data connection + */ + protected static ExtPrimaryDataConnection buildPrimaryConnection( + ExtEntityMapping mapping, Logger log) { + Map primaryMapping = mapping.getExtId2UuidMapping(DataType.EXT_PRIMARY_INPUT); + ExtPrimaryDataConnection extPrimaryDataConnection = + new ExtPrimaryDataConnection(primaryMapping); + + if (primaryMapping.isEmpty()) { + log.warn("Primary with 0 entities created."); + } else { + log.info("Primary connection with {} entities created.", primaryMapping.size()); + } + + return extPrimaryDataConnection; + } + + /** + * Builds an {@link ExtEmDataConnection}. + * + * @param mapping between the external simulation and SIMONA. + * @param log logger + * @return an ext em data connection + */ + protected static ExtEmDataConnection buildEmConnection(ExtEntityMapping mapping, Logger log) { + Map emMapping = mapping.getExtId2UuidMapping(DataType.EXT_EM_INPUT); + ExtEmDataConnection extEmDataConnection = new ExtEmDataConnection(emMapping); + + if (emMapping.isEmpty()) { + log.warn("Em connection with 0 entities created."); + } else { + log.info("Em connection with {} entities created.", emMapping.size()); + } + + return extEmDataConnection; + } + + /** + * Builds an {@link ExtResultDataConnection}. + * + * @param mapping between the external simulation and SIMONA. + * @param log logger + * @return an ext result data connection + */ + protected static ExtResultDataConnection buildResultConnection( + ExtEntityMapping mapping, Logger log) { + Map resultParticipantMapping = + mapping.getExtUuid2IdMapping(DataType.EXT_PARTICIPANT_RESULT); + Map resultGridMapping = mapping.getExtUuid2IdMapping(DataType.EXT_GRID_RESULT); + ExtResultDataConnection extResultDataConnection = + new ExtResultDataConnection(resultParticipantMapping, resultGridMapping); + + if (resultParticipantMapping.isEmpty() && resultGridMapping.isEmpty()) { + log.warn("Result connection with 0 participants and 0 grid assets created."); + } else { + log.info( + "Result connection with {} participants and {} grid assets created.", + resultParticipantMapping.size(), + resultGridMapping.size()); + } + + return extResultDataConnection; + } + + /** + * Function to send primary data to SIMONA using ExtPrimaryData + * + * @param extPrimaryDataConnection the connection to SIMONA + * @param tick for which data is sent + * @param dataMap map: id to value + * @param maybeNextTick option for the next tick data is sent + * @param log logger + */ + protected void sendPrimaryDataToSimona( + ExtPrimaryDataConnection extPrimaryDataConnection, + long tick, + Map dataMap, + Optional maybeNextTick, + Logger log) { log.debug("Wait for Primary Data from {}", extSimulatorName); - ExtInputDataContainer inputData = dataQueueExtCoSimulatorToSimonaApi.takeData(); log.debug("Received Primary Data from {}", extSimulatorName); - extPrimaryData.providePrimaryData( - tick, - extPrimaryData.convertExternalInputToPrimaryData(inputData), - inputData.getMaybeNextTick()); + extPrimaryDataConnection.convertAndSend(tick, dataMap, maybeNextTick, log); log.debug("Provided Primary Data to SIMONA!"); } /** * Function to send em data to SIMONA using ExtPrimaryData nextTick is necessary, because the em * agents have an own scheduler that should know, when the next set point arrives. + * + * @param extEmDataConnection the connection to SIMONA + * @param tick for which data is sent + * @param dataMap map: id to value + * @param maybeNextTick option for the next tick data is sent + * @param log logger */ - protected void sendEmDataToSimona(ExtEmData extEmData, long tick, long nextTick, Logger log) - throws InterruptedException { - log.debug("Wait for EmData from {}", extSimulatorName); - ExtInputDataContainer inputData = dataQueueExtCoSimulatorToSimonaApi.takeData(); + protected void sendEmDataToSimona( + ExtEmDataConnection extEmDataConnection, + long tick, + Map dataMap, + Optional maybeNextTick, + Logger log) { log.debug("Received EmData from {}", extSimulatorName); - extEmData.provideEmData( - tick, extEmData.convertExternalInputToEmSetPoints(inputData), Optional.of(nextTick)); + extEmDataConnection.convertAndSend(tick, dataMap, maybeNextTick, log); log.debug("Provided EmData to SIMONA!"); } - /** Function to get result data from SIMONA using ExtResultData */ - protected void sendResultsToExtCoSimulator( - ExtResultData extResultData, long tick, Optional nextTick, Logger log) + /** + * Function to get result data from SIMONA using the available {@link ExtResultDataConnection} + * + * @param connection the connection to SIMONA + * @param tick for which data is received + * @param maybeNextTick option for the next tick data is received + * @param log logger + */ + protected void sendDataToExt( + ExtResultDataConnection connection, long tick, Optional maybeNextTick, Logger log) throws InterruptedException { log.debug("Request results from SIMONA!"); - Map resultsToBeSend = extResultData.requestResults(tick); + Map resultsToBeSend = connection.requestResults(tick); log.debug("Received results from SIMONA!"); dataQueueSimonaApiToExtCoSimulator.queueData( - new ExtResultContainer(tick, resultsToBeSend, nextTick)); + new ExtResultContainer(tick, resultsToBeSend, maybeNextTick)); log.debug("Sent results to {}", extSimulatorName); } } diff --git a/src/main/java/edu/ie3/simona/api/simulation/ExtSimulation.java b/src/main/java/edu/ie3/simona/api/simulation/ExtSimulation.java index 2be68558..0e52a819 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/ExtSimulation.java +++ b/src/main/java/edu/ie3/simona/api/simulation/ExtSimulation.java @@ -6,13 +6,10 @@ package edu.ie3.simona.api.simulation; -import edu.ie3.simona.api.data.ExtData; -import edu.ie3.simona.api.data.ev.ExtEvData; -import edu.ie3.simona.api.data.ev.ExtEvSimulation; +import edu.ie3.simona.api.data.ExtDataConnection; import edu.ie3.simona.api.simulation.ontology.*; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; +import java.util.Set; /** * Every external simulation must extend this class in order to get triggered by the main @@ -104,22 +101,14 @@ protected void terminate(Boolean simulationSuccessful) { // to be overwritten in subclass } - public final List> getRequiredAdapters() { - ArrayList> classes = new ArrayList<>(); - - if (this instanceof ExtEvSimulation) classes.add(ExtEvData.class); - - return classes; - } - - public final void setup(ExtSimAdapterData data, List adapters) { + /** + * Method to set the external simulation adapter data. This method should be called during {@link + * edu.ie3.simona.api.ExtLinkInterface#setup(ExtSimAdapterData)}. + * + * @param data to set up + */ + public final void setAdapterData(ExtSimAdapterData data) { this.data = data; - - // todo sanity check if all required data is available - for (ExtData adapter : adapters) { - if (adapter instanceof ExtEvData && this instanceof ExtEvSimulation) - ((ExtEvSimulation) this).setExtEvData((ExtEvData) adapter); - } } /** @@ -131,5 +120,11 @@ protected String[] getMainArgs() { return data.getMainArgs(); } - public abstract List getDataConnections(); + /** Returns the name of this external simulation. */ + public final String getSimulationName() { + return simulationName; + } + + /** Returns all {@link ExtDataConnection} of this simulation. */ + public abstract Set getDataConnections(); } diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/DataType.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/DataType.java new file mode 100644 index 00000000..67bfaa12 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/simulation/mapping/DataType.java @@ -0,0 +1,32 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.api.simulation.mapping; + +import edu.ie3.datamodel.exceptions.ParsingException; + +public enum DataType { + EXT_PRIMARY_INPUT("primary_input"), + EXT_EM_INPUT("em_input"), + EXT_GRID_RESULT("grid_result"), + EXT_PARTICIPANT_RESULT("participant_result"); + + public final String type; + + DataType(String type) { + this.type = type; + } + + public static DataType parse(String type) throws ParsingException { + return switch (type) { + case "primary_input" -> EXT_PRIMARY_INPUT; + case "em_input" -> EXT_EM_INPUT; + case "grid_result" -> EXT_GRID_RESULT; + case "participant_result" -> EXT_PARTICIPANT_RESULT; + default -> throw new ParsingException("Data type " + type + " is not supported!"); + }; + } +} diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityEntry.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityEntry.java index 23460236..1444c3e1 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityEntry.java +++ b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityEntry.java @@ -16,31 +16,28 @@ * @param uuid SIMONA uuid * @param id external id * @param columnScheme data types the external asset expects - * @param resultType data types the external asset expects + * @param dataType data types the external asset expects */ public record ExtEntityEntry( UUID uuid, String id, ColumnScheme columnScheme, // FIXME: placeholder -> ColumnScheme should handle more data types - String resultType) + DataType dataType) implements InputEntity { - public static final String EXT_INPUT = "input"; - public static final String EXT_RESULT_PARTICIPANT = "result_participant"; - public static final String EXT_RESULT_GRID = "result_grid"; public String toString() { return "ExtEntityEntry={" + "UUID=" - + uuid() + + uuid + ", " - + "ExtId=" - + id() + + "extId=" + + id + ", " - + "ColumnScheme=" - + columnScheme() + + "columnScheme=" + + columnScheme + ", " - + "ResultType=" - + resultType() + + "dataType=" + + dataType + "}"; } } diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityFactory.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityFactory.java index 343c577b..9a820258 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityFactory.java +++ b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityFactory.java @@ -6,6 +6,8 @@ package edu.ie3.simona.api.simulation.mapping; +import edu.ie3.datamodel.exceptions.FactoryException; +import edu.ie3.datamodel.exceptions.ParsingException; import edu.ie3.datamodel.io.factory.EntityData; import edu.ie3.datamodel.io.factory.EntityFactory; import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme; @@ -22,7 +24,7 @@ public class ExtEntityFactory extends EntityFactory public static final String DATA_TYPE = "dataType"; public ExtEntityFactory() { - super(new Class[] {ExtEntityEntry.class}); + super(ExtEntityEntry.class); } @Override @@ -36,7 +38,13 @@ protected ExtEntityEntry buildModel(EntityData data) { UUID simonaUuid = data.getUUID(SIMONA_UUID); String extId = data.getField(EXT_ID); Optional columnScheme = ColumnScheme.parse(data.getField(COLUMN_SCHEME)); - String inputType = data.getField(DATA_TYPE); + + DataType inputType; + try { + inputType = DataType.parse(data.getField(DATA_TYPE)); + } catch (ParsingException e) { + throw new FactoryException(e); + } return new ExtEntityEntry( simonaUuid, diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMapping.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMapping.java index a0826fda..8fd033e6 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMapping.java +++ b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMapping.java @@ -7,14 +7,16 @@ package edu.ie3.simona.api.simulation.mapping; import java.util.*; +import java.util.stream.Collectors; /** Contains the mapping between SIMONA uuid, the external id and the data type the assets hold */ public class ExtEntityMapping { - private final List extEntities; + private final Map> extEntities; public ExtEntityMapping(List extEntityEntryList) { - this.extEntities = extEntityEntryList; + this.extEntities = + extEntityEntryList.stream().collect(Collectors.groupingBy(ExtEntityEntry::dataType)); } /** @@ -23,15 +25,9 @@ public ExtEntityMapping(List extEntityEntryList) { * @param dataType data type the external asset expects * @return Mapping external id to SIMONA uuid */ - public Map getExtId2UuidMapping(String dataType) { - Map extId2UuidMapping = new HashMap<>(); - extEntities.forEach( - ent -> { - if (Objects.equals(ent.resultType(), dataType)) { - extId2UuidMapping.put(ent.id(), ent.uuid()); - } - }); - return extId2UuidMapping; + public Map getExtId2UuidMapping(DataType dataType) { + return extEntities.getOrDefault(dataType, Collections.emptyList()).stream() + .collect(Collectors.toMap(ExtEntityEntry::id, ExtEntityEntry::uuid)); } /** @@ -40,14 +36,8 @@ public Map getExtId2UuidMapping(String dataType) { * @param dataType data type the external asset expects * @return Mapping SIMONA uuid to external id */ - public Map getExtUuid2IdMapping(String dataType) { - Map extUuid2IdMapping = new HashMap<>(); - extEntities.forEach( - ent -> { - if (Objects.equals(ent.resultType(), dataType)) { - extUuid2IdMapping.put(ent.uuid(), ent.id()); - } - }); - return extUuid2IdMapping; + public Map getExtUuid2IdMapping(DataType dataType) { + return extEntities.getOrDefault(dataType, Collections.emptyList()).stream() + .collect(Collectors.toMap(ExtEntityEntry::uuid, ExtEntityEntry::id)); } } diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingCsvSource.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingCsvSource.java deleted file mode 100644 index e1bf9204..00000000 --- a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingCsvSource.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * © 2024. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.api.simulation.mapping; - -import edu.ie3.datamodel.exceptions.SourceException; -import edu.ie3.datamodel.io.factory.EntityData; -import edu.ie3.datamodel.io.naming.FileNamingStrategy; -import edu.ie3.datamodel.io.source.csv.CsvDataSource; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.stream.Stream; - -/** Source that is capable of providing information around external mapping from csv files. */ -public class ExtEntityMappingCsvSource extends ExtEntityMappingSource { - - private final ExtEntityMapping extEntities; - - private final CsvDataSource dataSource; - - private final Path mappingPath; - - public ExtEntityMappingCsvSource( - String csvSep, Path folderPath, FileNamingStrategy fileNamingStrategy) { - super(); - this.dataSource = new CsvDataSource(csvSep, folderPath, fileNamingStrategy); - this.mappingPath = folderPath; - this.extEntities = buildExtEntityMapping(); - } - - public ExtEntityMapping getMapping() { - return extEntities; - } - - /** - * Method to retrieve the fields found in the source. - * - * @return an option for the found fields - */ - public Optional> getSourceFields() throws SourceException { - return dataSource.getSourceFields(ExtEntityEntry.class); - } - - /** - * Builds the mapping from CSV - * - * @return Mapping external id and SIMONA uuid - */ - protected final ExtEntityMapping buildExtEntityMapping() { - return new ExtEntityMapping(buildStream().map(this::createExtEntityEntry).toList()); - } - - private Stream> - buildStream() { // TODO: Interim version -> maybe it would be easier to use PSDM methods for - // reading the CSV - Path pathToCsv = mappingPath; - - try (Stream lines = Files.lines(pathToCsv)) { - // Read the header line - List headers = - lines - .findFirst() - .map(line -> Arrays.asList(line.split(","))) - .orElseThrow(() -> new RuntimeException("No header line found")); - - // Stream the rest of the lines - Stream> mapStream = - Files.lines(pathToCsv) - .skip(1) // Skip the header line - .map( - line -> { - String[] values = line.split(","); - Map map = new HashMap<>(); - for (int i = 0; i < headers.size(); i++) { - map.put(headers.get(i), values[i]); - } - return map; - }); - - return mapStream; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private ExtEntityEntry createExtEntityEntry(Map fieldToValues) { - return factory.get(new EntityData(fieldToValues, ExtEntityEntry.class)).getOrThrow(); - } - - public static ExtEntityMapping createExtEntityMapping(Path mappingPath) { - ExtEntityMappingCsvSource mappingSource = - new ExtEntityMappingCsvSource(",", mappingPath, new FileNamingStrategy()); - return mappingSource.getMapping(); - } -} diff --git a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSource.java b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSource.java index 8a564713..1d24d7ff 100644 --- a/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSource.java +++ b/src/main/java/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSource.java @@ -6,12 +6,83 @@ package edu.ie3.simona.api.simulation.mapping; -/** Interface definition of a source, that is able to provide external mapping. */ -public abstract class ExtEntityMappingSource { +import edu.ie3.datamodel.exceptions.SourceException; +import edu.ie3.datamodel.io.factory.EntityData; +import edu.ie3.datamodel.io.naming.EntityPersistenceNamingStrategy; +import edu.ie3.datamodel.io.naming.FileNamingStrategy; +import edu.ie3.datamodel.io.source.DataSource; +import edu.ie3.datamodel.io.source.csv.CsvDataSource; +import edu.ie3.datamodel.models.Entity; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +/** Source for external entity mapping. */ +public class ExtEntityMappingSource { + + protected final DataSource dataSource; protected final ExtEntityFactory factory; - protected ExtEntityMappingSource() { + protected ExtEntityMappingSource(DataSource dataSource) { + this.dataSource = dataSource; this.factory = new ExtEntityFactory(); } + + /** + * Creates an {@link ExtEntityMapping} from a given file. + * + * @param filepath path to the file including its name + * @return a new mapping + * @throws SourceException if an error occurred + */ + public static ExtEntityMapping fromFile(Path filepath) throws SourceException { + String filename = filepath.getFileName().toString(); + Path directoryPath; + + if (!filename.contains(".csv")) { + directoryPath = filepath; + } else { + directoryPath = filepath.getParent(); + } + + ExtEntityNaming naming = new ExtEntityNaming(filename); + CsvDataSource source = new CsvDataSource(",", directoryPath, new FileNamingStrategy(naming)); + + return new ExtEntityMappingSource(source).getMapping(); + } + + /** Return the mapping from a given {@link DataSource}. */ + public ExtEntityMapping getMapping() throws SourceException { + return new ExtEntityMapping( + dataSource.getSourceData(ExtEntityEntry.class).map(this::createExtEntityEntry).toList()); + } + + /** + * Creates an ext entity entry from a given map. + * + * @param fieldToValues map: field name to value + * @return a new {@link ExtEntityEntry} + */ + private ExtEntityEntry createExtEntityEntry(Map fieldToValues) { + return factory.get(new EntityData(fieldToValues, ExtEntityEntry.class)).getOrThrow(); + } + + /** Csv naming for ext entity mapping. */ + private static class ExtEntityNaming extends EntityPersistenceNamingStrategy { + + private final String filename; + + private ExtEntityNaming(String filename) { + this.filename = filename.replace(".csv", ""); + } + + @Override + public Optional getEntityName(Class cls) { + if (ExtEntityEntry.class.isAssignableFrom(cls)) { + return Optional.of(filename); + } else { + return super.getEntityName(cls); + } + } + } } diff --git a/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataConnectionTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataConnectionTest.groovy new file mode 100644 index 00000000..d8f798f5 --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataConnectionTest.groovy @@ -0,0 +1,98 @@ +package edu.ie3.simona.api.data.em + +import edu.ie3.datamodel.models.value.PValue +import edu.ie3.datamodel.models.value.Value +import edu.ie3.simona.api.data.em.ontology.ProvideEmSetPointData +import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage +import edu.ie3.simona.api.data.primarydata.ontology.ProvidePrimaryData +import edu.ie3.simona.api.test.common.DataServiceTestData +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.testkit.TestProbe +import org.apache.pekko.testkit.javadsl.TestKit +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import spock.lang.Shared +import spock.lang.Specification + +class ExtEmDataConnectionTest extends Specification implements DataServiceTestData { + + @Shared + ActorSystem actorSystem + + @Shared + Map extEmDataMapping = Map.of( + "Em", + inputUuid + ) + + def setupSpec() { + actorSystem = ActorSystem.create() + } + + def cleanupSpec() { + TestKit.shutdownActorSystem(actorSystem) + actorSystem = null + } + + def "ExtEmDataConnection should provide em data correctly"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extEmDataConnection = new ExtEmDataConnection(extEmDataMapping) + extEmDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + + def emData = [:] as HashMap + def uuid = UUID.randomUUID() + emData.put(uuid.toString(), pValue) + + def convertedEmData = Map.of(uuid, pValue as PValue) + + when: + extEmDataConnection.provideEmData(0L, convertedEmData, Optional.of(900L)) + + then: + dataService.expectMsg(new ProvideEmSetPointData(0, convertedEmData, Optional.of(900L))) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + } + + def "ExtEmDataConnection should convert input data correctly"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extEmDataConnection = new ExtEmDataConnection(extEmDataMapping) + extEmDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + def inputDataMap = Map.of("Em", pValue) + + when: + extEmDataConnection.convertAndSend(0L, inputDataMap, Optional.of(900L), log) + + then: + dataService.expectMsg(new ProvideEmSetPointData(0L, Map.of(inputUuid, pValue), Optional.of(900L))) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + } + + def "ExtEmDataConnection should send no message, if input data for a not requested asset was provided"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extEmDataConnection = new ExtEmDataConnection(extEmDataMapping) + extEmDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + def inputDataMap = Map.of("Load", pValue) + + when: + extEmDataConnection.convertAndSend(0L, inputDataMap, Optional.of(900L), log) + + then: + dataService.expectNoMessage() + } + +} diff --git a/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataTest.groovy deleted file mode 100644 index 8152e3ce..00000000 --- a/src/test/groovy/edu/ie3/simona/api/data/em/ExtEmDataTest.groovy +++ /dev/null @@ -1,86 +0,0 @@ -package edu.ie3.simona.api.data.em - - -import edu.ie3.datamodel.models.value.PValue -import edu.ie3.datamodel.models.value.Value -import edu.ie3.simona.api.data.ExtInputDataContainer -import edu.ie3.simona.api.data.em.ontology.ProvideEmSetPointData -import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage -import edu.ie3.simona.api.test.common.DataServiceTestData -import org.apache.pekko.actor.ActorSystem -import org.apache.pekko.testkit.TestProbe -import org.apache.pekko.testkit.javadsl.TestKit -import spock.lang.Shared -import spock.lang.Specification - -class ExtEmDataTest extends Specification implements DataServiceTestData { - - @Shared - ActorSystem actorSystem - - @Shared - Map extEmDataMapping = Map.of( - "Em", - inputUuid - ) - - def setupSpec() { - actorSystem = ActorSystem.create() - } - - def cleanupSpec() { - TestKit.shutdownActorSystem(actorSystem) - actorSystem = null - } - - def "ExtEmData should provide em data correctly"() { - given: - def dataService = new TestProbe(actorSystem) - def extSimAdapter = new TestProbe(actorSystem) - def extEmData = new ExtEmData(extEmDataMapping) - extEmData.setActorRefs( - dataService.ref(), - extSimAdapter.ref() - ) - - def emData = [:] as HashMap - def uuid = UUID.randomUUID() - emData.put(uuid.toString(), pValue) - - def convertedEmData = Map.of(uuid, pValue as PValue) - - when: - extEmData.provideEmData(0L, convertedEmData, Optional.of(900L)) - - then: - dataService.expectMsg(new ProvideEmSetPointData(0, convertedEmData, Optional.of(900L))) - extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) - } - - def "ExtEmData should convert ExtInputDataPackage to a map"() { - given: - def extEmData = new ExtEmData(extEmDataMapping) - def inputDataMap = Map.of("Em", pValue) - def inputDataContainer = new ExtInputDataContainer(0L, inputDataMap, 900L) - - when: - def emDataMap = extEmData.convertExternalInputToEmSetPoints(inputDataContainer) - - then: - emDataMap.get(inputUuid) == pValue - } - - def "ExtEmData should throw an exception, if input data for a not requested asset was provided"() { - given: - def extEmData = new ExtEmData(extEmDataMapping) - def inputDataMap = Map.of("Load", pValue) - def inputDataContainer = new ExtInputDataContainer(0L, inputDataMap, 900L) - - when: - extEmData.convertExternalInputToEmSetPoints(inputDataContainer) - - then: - thrown IllegalArgumentException - } - -} diff --git a/src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataConnectionTest.groovy similarity index 66% rename from src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataTest.groovy rename to src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataConnectionTest.groovy index 63441e4d..8487efc3 100644 --- a/src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataTest.groovy +++ b/src/test/groovy/edu/ie3/simona/api/data/ev/ExtEvDataConnectionTest.groovy @@ -15,7 +15,7 @@ import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage import spock.lang.Shared import spock.lang.Specification -class ExtEvDataTest extends Specification { +class ExtEvDataConnectionTest extends Specification { @Shared ActorSystem actorSystem @@ -29,18 +29,19 @@ class ExtEvDataTest extends Specification { actorSystem = null } - def "ExtEvData should request and receive free evcs lots correctly"() { + def "ExtEvDataConnection should request and receive free evcs lots correctly"() { given: def dataService = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extEvData = new ExtEvData(dataService.ref(), extSimAdapter.ref()) + def extEvDataConnection = new ExtEvDataConnection() + extEvDataConnection.setActorRefs(dataService.ref(), extSimAdapter.ref()) def sentMsg = new ProvideEvcsFreeLots() when: // we need to queue the msg beforehand because the receive method is blocking - extEvData.queueExtResponseMsg(sentMsg) - def actualReceivedEvcs = extEvData.requestAvailablePublicEvcs() + extEvDataConnection.queueExtResponseMsg(sentMsg) + def actualReceivedEvcs = extEvDataConnection.requestAvailablePublicEvcs() then: dataService.expectMsg(new RequestEvcsFreeLots()) @@ -48,18 +49,19 @@ class ExtEvDataTest extends Specification { actualReceivedEvcs == sentMsg.evcs() } - def "ExtEvData should request and receive current charging prices correctly"() { + def "ExtEvDataConnection should request and receive current charging prices correctly"() { given: def dataService = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extEvData = new ExtEvData(dataService.ref(), extSimAdapter.ref()) + def extEvDataConnection = new ExtEvDataConnection() + extEvDataConnection.setActorRefs(dataService.ref(), extSimAdapter.ref()) def sentMsg = new ProvideCurrentPrices() when: // we need to queue the msg beforehand because the receive method is blocking - extEvData.queueExtResponseMsg(sentMsg) - def actualReceivedPrices = extEvData.requestCurrentPrices() + extEvDataConnection.queueExtResponseMsg(sentMsg) + def actualReceivedPrices = extEvDataConnection.requestCurrentPrices() then: dataService.expectMsg(new RequestCurrentPrices()) @@ -67,11 +69,12 @@ class ExtEvDataTest extends Specification { actualReceivedPrices == sentMsg.prices() } - def "ExtEvData should request and receive departing EVs correctly"() { + def "ExtEvDataConnection should request and receive departing EVs correctly"() { given: def dataService = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extEvData = new ExtEvData(dataService.ref(), extSimAdapter.ref()) + def extEvDataConnection = new ExtEvDataConnection() + extEvDataConnection.setActorRefs(dataService.ref(), extSimAdapter.ref()) def requestedDepartingEvs = new HashMap>() requestedDepartingEvs.put(UUID.randomUUID(), new ArrayList()) @@ -79,8 +82,8 @@ class ExtEvDataTest extends Specification { when: // we need to queue the msg beforehand because the receive method is blocking - extEvData.queueExtResponseMsg(sentMsg) - def actualReceivedEvs = extEvData.requestDepartingEvs(requestedDepartingEvs) + extEvDataConnection.queueExtResponseMsg(sentMsg) + def actualReceivedEvs = extEvDataConnection.requestDepartingEvs(requestedDepartingEvs) then: dataService.expectMsg(new RequestDepartingEvs(requestedDepartingEvs)) @@ -88,35 +91,37 @@ class ExtEvDataTest extends Specification { actualReceivedEvs == sentMsg.departedEvs() } - def "ExtEvData should provide arriving EVs correctly"() { + def "ExtEvDataConnection should provide arriving EVs correctly"() { given: def dataService = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extEvData = new ExtEvData(dataService.ref(), extSimAdapter.ref()) + def extEvDataConnection = new ExtEvDataConnection() + extEvDataConnection.setActorRefs(dataService.ref(), extSimAdapter.ref()) def arrivingEvs = new HashMap>() arrivingEvs.put(UUID.randomUUID(), new ArrayList()) when: - extEvData.provideArrivingEvs(arrivingEvs, Optional.of(60L)) + extEvDataConnection.provideArrivingEvs(arrivingEvs, Optional.of(60L)) then: dataService.expectMsg(new ProvideArrivingEvs(arrivingEvs, Optional.of(60L))) extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) } - def "ExtEvData should fail if wrong response is sent"() { + def "ExtEvDataConnection should fail if wrong response is sent"() { given: def dataService = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extEvData = new ExtEvData(dataService.ref(), extSimAdapter.ref()) + def extEvDataConnection = new ExtEvDataConnection() + extEvDataConnection.setActorRefs(dataService.ref(), extSimAdapter.ref()) def unexpectedMsg = new ProvideCurrentPrices() when: // we need to queue the msg beforehand because the receive method is blocking - extEvData.queueExtResponseMsg(unexpectedMsg) - extEvData.requestAvailablePublicEvcs() + extEvDataConnection.queueExtResponseMsg(unexpectedMsg) + extEvDataConnection.requestAvailablePublicEvcs() then: dataService.expectMsg(new RequestEvcsFreeLots()) diff --git a/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnectionTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnectionTest.groovy new file mode 100644 index 00000000..5aba77d4 --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataConnectionTest.groovy @@ -0,0 +1,93 @@ +package edu.ie3.simona.api.data.primarydata + +import edu.ie3.datamodel.models.value.Value +import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage +import edu.ie3.simona.api.data.primarydata.ontology.ProvidePrimaryData +import edu.ie3.simona.api.test.common.DataServiceTestData +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.testkit.TestProbe +import org.apache.pekko.testkit.javadsl.TestKit +import spock.lang.Shared +import spock.lang.Specification + +class ExtPrimaryDataConnectionTest extends Specification implements DataServiceTestData { + + @Shared + ActorSystem actorSystem + + @Shared + Map extPrimaryDataMapping = Map.of( + "Pv", + inputUuid + ) + + def setupSpec() { + actorSystem = ActorSystem.create() + } + + def cleanupSpec() { + TestKit.shutdownActorSystem(actorSystem) + actorSystem = null + } + + def "ExtPrimaryDataConnection should provide primary data correctly"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extPrimaryDataConnection = new ExtPrimaryDataConnection(extPrimaryDataMapping) + extPrimaryDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + + def primaryData = [:] as HashMap + def uuid = UUID.randomUUID() + primaryData.put(uuid.toString(), pValue) + + def convertedPrimaryData = Map.of(uuid, pValue as Value) + + when: + extPrimaryDataConnection.providePrimaryData(0L, convertedPrimaryData, Optional.of(900L)) + + then: + dataService.expectMsg(new ProvidePrimaryData(0L, convertedPrimaryData, Optional.of(900L))) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + } + + def "ExtPrimaryDataConnection should convert input data correctly"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extPrimaryDataConnection = new ExtPrimaryDataConnection(extPrimaryDataMapping) + extPrimaryDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + def inputDataMap = Map.of("Pv", pValue) + + when: + extPrimaryDataConnection.convertAndSend(0L, inputDataMap, Optional.of(900L), log) + + then: + dataService.expectMsg(new ProvidePrimaryData(0L, Map.of(inputUuid, pValue), Optional.of(900L))) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + } + + def "ExtPrimaryDataConnection should send no message, if input data for a not requested asset was provided"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extPrimaryDataConnection = new ExtPrimaryDataConnection(extPrimaryDataMapping) + extPrimaryDataConnection.setActorRefs( + dataService.ref(), + extSimAdapter.ref() + ) + def inputDataMap = Map.of("Load", pValue) + + when: + extPrimaryDataConnection.convertAndSend(0L, inputDataMap, Optional.empty(), log) + + then: + dataService.expectNoMessage() + } +} diff --git a/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataTest.groovy deleted file mode 100644 index 099087f8..00000000 --- a/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataTest.groovy +++ /dev/null @@ -1,83 +0,0 @@ -package edu.ie3.simona.api.data.primarydata - -import edu.ie3.datamodel.models.value.Value -import edu.ie3.simona.api.data.ExtInputDataContainer -import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage -import edu.ie3.simona.api.data.primarydata.ontology.ProvidePrimaryData -import edu.ie3.simona.api.test.common.DataServiceTestData -import org.apache.pekko.actor.ActorSystem -import org.apache.pekko.testkit.TestProbe -import org.apache.pekko.testkit.javadsl.TestKit -import spock.lang.Shared -import spock.lang.Specification - -class ExtPrimaryDataTest extends Specification implements DataServiceTestData { - - @Shared - ActorSystem actorSystem - - @Shared - Map extPrimaryDataMapping = Map.of( - "Pv", - inputUuid - ) - - def setupSpec() { - actorSystem = ActorSystem.create() - } - - def cleanupSpec() { - TestKit.shutdownActorSystem(actorSystem) - actorSystem = null - } - - def "ExtPrimaryData should provide primary data correctly"() { - given: - def dataService = new TestProbe(actorSystem) - def extSimAdapter = new TestProbe(actorSystem) - def extPrimaryData = new ExtPrimaryData(extPrimaryDataMapping) - extPrimaryData.setActorRefs( - dataService.ref(), - extSimAdapter.ref() - ) - - def primaryData = [:] as HashMap - def uuid = UUID.randomUUID() - primaryData.put(uuid.toString(), pValue) - - def convertedPrimaryData = Map.of(uuid, pValue as Value) - - when: - extPrimaryData.providePrimaryData(0L, convertedPrimaryData, Optional.of(900L)) - - then: - dataService.expectMsg(new ProvidePrimaryData(0L, convertedPrimaryData, Optional.of(900L))) - extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) - } - - def "ExtPrimaryData should convert ExtInputDataPackage to a map"() { - given: - def extPrimaryData = new ExtPrimaryData(extPrimaryDataMapping) - def inputDataMap = Map.of("Pv", pValue) - def inputDataContainer = new ExtInputDataContainer(0L, inputDataMap, 900L) - - when: - def primaryDataMap = extPrimaryData.convertExternalInputToPrimaryData(inputDataContainer) - - then: - primaryDataMap.get(inputUuid) == pValue - } - - def "ExtPrimaryData should throw an exception, if input data for a not requested asset was provided"() { - given: - def extPrimaryData = new ExtPrimaryData(extPrimaryDataMapping) - def inputDataMap = Map.of("Load", pValue) - def inputDataContainer = new ExtInputDataContainer(0L, inputDataMap, 900L) - - when: - extPrimaryData.convertExternalInputToPrimaryData(inputDataContainer) - - then: - thrown IllegalArgumentException - } -} diff --git a/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataConnectionTest.groovy similarity index 64% rename from src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy rename to src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataConnectionTest.groovy index dd11182c..7dc0dc22 100644 --- a/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy +++ b/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataConnectionTest.groovy @@ -2,7 +2,6 @@ package edu.ie3.simona.api.data.results import edu.ie3.datamodel.models.StandardUnits import edu.ie3.datamodel.models.result.connector.LineResult -import edu.ie3.datamodel.models.result.system.LoadResult import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage import edu.ie3.simona.api.data.results.ontology.ProvideResultEntities import edu.ie3.simona.api.data.results.ontology.RequestResultEntities @@ -20,7 +19,7 @@ import javax.measure.quantity.Angle import javax.measure.quantity.ElectricCurrent import java.time.ZonedDateTime -class ExtResultDataTest extends Specification implements DataServiceTestData { +class ExtResultDataConnectionTest extends Specification implements DataServiceTestData { @Shared ActorSystem actorSystem @@ -47,8 +46,8 @@ class ExtResultDataTest extends Specification implements DataServiceTestData { def dataService = new TestProbe(actorSystem) def dataServiceActivation = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extResultData = new ExtResultData(participantResultAssetMapping, gridResultAssetMapping) - extResultData.setActorRefs( + def extResultDataConnection = new ExtResultDataConnection(participantResultAssetMapping, gridResultAssetMapping) + extResultDataConnection.setActorRefs( dataService.ref(), dataServiceActivation.ref(), extSimAdapter.ref() @@ -58,8 +57,8 @@ class ExtResultDataTest extends Specification implements DataServiceTestData { when: // we need to queue the msg beforehand because the receive method is blocking - extResultData.queueExtResponseMsg(sentMsg) - def receivedResults = extResultData.requestResults(0L) + extResultDataConnection.queueExtResponseMsg(sentMsg) + def receivedResults = extResultDataConnection.requestResults(0L) then: dataService.expectMsg(new RequestResultEntities(0L)) @@ -72,8 +71,8 @@ class ExtResultDataTest extends Specification implements DataServiceTestData { def dataService = new TestProbe(actorSystem) def dataServiceActivation = new TestProbe(actorSystem) def extSimAdapter = new TestProbe(actorSystem) - def extResultData = new ExtResultData(participantResultAssetMapping, gridResultAssetMapping) - extResultData.setActorRefs( + def extResultDataConnection = new ExtResultDataConnection(participantResultAssetMapping, gridResultAssetMapping) + extResultDataConnection.setActorRefs( dataService.ref(), dataServiceActivation.ref(), extSimAdapter.ref() @@ -83,8 +82,8 @@ class ExtResultDataTest extends Specification implements DataServiceTestData { when: // we need to queue the msg beforehand because the receive method is blocking - extResultData.queueExtResponseMsg(unexpectedMsg) - extResultData.requestResults(0L) + extResultDataConnection.queueExtResponseMsg(unexpectedMsg) + extResultDataConnection.requestResults(0L) then: dataService.expectMsg(new RequestResultEntities(0L)) @@ -94,31 +93,31 @@ class ExtResultDataTest extends Specification implements DataServiceTestData { def "ExtResultData should convert a list of result entities correctly to a map of resultAssetMappingId to result entity"() { given: - def extResultData = new ExtResultData(participantResultAssetMapping, gridResultAssetMapping) + def extResultDataConnection = new ExtResultDataConnection(participantResultAssetMapping, gridResultAssetMapping) when: - def mapOfResults = extResultData.createResultMap([loadResult]) + def mapOfResults = extResultDataConnection.createResultMap([loadResult]) then: - mapOfResults.size() == 1 - mapOfResults.get("Load") == loadResult + mapOfResults.size() == 1 + mapOfResults.get("Load") == loadResult } def "ExtResultData should throw an exception, if a result with a wrong data type was provided"() { given: - def extResultData = new ExtResultData(participantResultAssetMapping, gridResultAssetMapping) - Quantity iAMag = Quantities.getQuantity(100, StandardUnits.ELECTRIC_CURRENT_MAGNITUDE) - Quantity iAAng = Quantities.getQuantity(45, StandardUnits.ELECTRIC_CURRENT_ANGLE) - Quantity iBMag = Quantities.getQuantity(150, StandardUnits.ELECTRIC_CURRENT_MAGNITUDE) - Quantity iBAng = Quantities.getQuantity(30, StandardUnits.ELECTRIC_CURRENT_ANGLE) - def wrongResult = new LineResult( - ZonedDateTime.parse("2020-01-30T17:26:44Z"), inputUuid, iAMag, iAAng, iBMag, iBAng - ) + def extResultDataConnection = new ExtResultDataConnection(participantResultAssetMapping, gridResultAssetMapping) + Quantity iAMag = Quantities.getQuantity(100, StandardUnits.ELECTRIC_CURRENT_MAGNITUDE) + Quantity iAAng = Quantities.getQuantity(45, StandardUnits.ELECTRIC_CURRENT_ANGLE) + Quantity iBMag = Quantities.getQuantity(150, StandardUnits.ELECTRIC_CURRENT_MAGNITUDE) + Quantity iBAng = Quantities.getQuantity(30, StandardUnits.ELECTRIC_CURRENT_ANGLE) + def wrongResult = new LineResult( + ZonedDateTime.parse("2020-01-30T17:26:44Z"), inputUuid, iAMag, iAAng, iBMag, iBAng + ) when: - extResultData.createResultMap([wrongResult]) + extResultDataConnection.createResultMap([wrongResult]) then: - thrown IllegalArgumentException + thrown IllegalArgumentException } } \ No newline at end of file diff --git a/src/test/groovy/edu/ie3/simona/api/simulation/ExtCoSimulationTest.groovy b/src/test/groovy/edu/ie3/simona/api/simulation/ExtCoSimulationTest.groovy new file mode 100644 index 00000000..2903384c --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/simulation/ExtCoSimulationTest.groovy @@ -0,0 +1,74 @@ +package edu.ie3.simona.api.simulation + +import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme +import edu.ie3.simona.api.simulation.mapping.DataType +import edu.ie3.simona.api.simulation.mapping.ExtEntityEntry +import edu.ie3.simona.api.simulation.mapping.ExtEntityMapping +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import spock.lang.Shared +import spock.lang.Specification + +class ExtCoSimulationTest extends Specification { + + @Shared + private static final Logger log = LoggerFactory.getLogger(ExtCoSimulationTest) + + def "An ExtCoSimulation can build a primary data connection correctly"() { + given: + UUID uuid1 = UUID.randomUUID() + UUID uuid2 = UUID.randomUUID() + UUID uuid3 = UUID.randomUUID() + + ExtEntityMapping mapping = new ExtEntityMapping([ + new ExtEntityEntry(uuid1, "primary1", ColumnScheme.ACTIVE_POWER, DataType.EXT_PRIMARY_INPUT), + new ExtEntityEntry(uuid2, "em1", ColumnScheme.ACTIVE_POWER, DataType.EXT_EM_INPUT), + new ExtEntityEntry(uuid3, "primary2", ColumnScheme.ACTIVE_POWER, DataType.EXT_PRIMARY_INPUT), + ]) + + when: + def actual = ExtCoSimulation.buildPrimaryConnection(mapping, log) + + then: + actual.getPrimaryDataAssets() == [uuid3, uuid1] + } + + def "An ExtCoSimulation can build an em data connection correctly"() { + given: + UUID uuid1 = UUID.randomUUID() + UUID uuid2 = UUID.randomUUID() + UUID uuid3 = UUID.randomUUID() + + ExtEntityMapping mapping = new ExtEntityMapping([ + new ExtEntityEntry(uuid1, "em1", ColumnScheme.ACTIVE_POWER, DataType.EXT_EM_INPUT), + new ExtEntityEntry(uuid2, "em2", ColumnScheme.ACTIVE_POWER, DataType.EXT_EM_INPUT), + new ExtEntityEntry(uuid3, "primary1", ColumnScheme.ACTIVE_POWER, DataType.EXT_PRIMARY_INPUT), + ]) + + when: + def actual = ExtCoSimulation.buildEmConnection(mapping, log) + + then: + actual.getControlledEms() == [uuid1, uuid2] + } + + def "An ExtCoSimulation can build a result data connection correctly"() { + given: + UUID uuid1 = UUID.randomUUID() + UUID uuid2 = UUID.randomUUID() + UUID uuid3 = UUID.randomUUID() + + ExtEntityMapping mapping = new ExtEntityMapping([ + new ExtEntityEntry(uuid1, "grid_result", ColumnScheme.ACTIVE_POWER, DataType.EXT_GRID_RESULT), + new ExtEntityEntry(uuid2, "participant_result", ColumnScheme.ACTIVE_POWER, DataType.EXT_PARTICIPANT_RESULT), + new ExtEntityEntry(uuid3, "primary1", ColumnScheme.ACTIVE_POWER, DataType.EXT_PRIMARY_INPUT), + ]) + + when: + def actual = ExtCoSimulation.buildResultConnection(mapping, log) + + then: + actual.getGridResultDataAssets() == [uuid1] + actual.getParticipantResultDataAssets() == [uuid2] + } +} diff --git a/src/test/groovy/edu/ie3/simona/api/simulation/ExtSimulationSpec.groovy b/src/test/groovy/edu/ie3/simona/api/simulation/ExtSimulationSpec.groovy index 8c2724a7..029056ff 100644 --- a/src/test/groovy/edu/ie3/simona/api/simulation/ExtSimulationSpec.groovy +++ b/src/test/groovy/edu/ie3/simona/api/simulation/ExtSimulationSpec.groovy @@ -3,7 +3,7 @@ package edu.ie3.simona.api.simulation import org.apache.pekko.actor.ActorSystem import org.apache.pekko.testkit.TestProbe import org.apache.pekko.testkit.javadsl.TestKit -import edu.ie3.simona.api.data.ExtData +import edu.ie3.simona.api.data.ExtDataConnection import edu.ie3.simona.api.simulation.ontology.ActivationMessage import edu.ie3.simona.api.simulation.ontology.CompletionMessage import edu.ie3.simona.api.simulation.ontology.ControlMessageToExt @@ -48,7 +48,7 @@ class ExtSimulationSpec extends Specification { } @Override - List getDataConnections() { + Set getDataConnections() { return [] } } @@ -68,86 +68,86 @@ class ExtSimulationSpec extends Specification { def "An ExtSimulation should handle initialization"() { given: - def tick = -1L - def newTick = 0L - def extSimAdapter = new TestProbe(actorSystem) - def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) - def extSim = new TestSimulation(newTick, Optional.of(-2L)) - extSim.setup(extSimData, new ArrayList()) + def tick = -1L + def newTick = 0L + def extSimAdapter = new TestProbe(actorSystem) + def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) + def extSim = new TestSimulation(newTick, Optional.of(-2L)) + extSim.setAdapterData(extSimData) when: - extSimData.queueExtMsg(new ActivationMessage(tick)) - def finishedActual = handleMessage.invoke(extSim) + extSimData.queueExtMsg(new ActivationMessage(tick)) + def finishedActual = handleMessage.invoke(extSim) then: - finishedActual == false - extSimAdapter.expectMsg(new CompletionMessage(Optional.of(newTick))) + finishedActual == false + extSimAdapter.expectMsg(new CompletionMessage(Optional.of(newTick))) } def "An ExtSimulation should handle activation and return given new triggers"() { given: - def extSimAdapter = new TestProbe(actorSystem) - def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) - def newTickOpt = newTick.isEmpty() ? + def extSimAdapter = new TestProbe(actorSystem) + def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) + def newTickOpt = newTick.isEmpty() ? Optional.empty() : Optional.of(newTick.first()) - def extSim = new TestSimulation(-2L, newTickOpt) - extSim.setup(extSimData, new ArrayList()) + def extSim = new TestSimulation(-2L, newTickOpt) + extSim.setAdapterData(extSimData) when: - extSimData.queueExtMsg(new ActivationMessage(tick)) - def finishedActual = handleMessage.invoke(extSim) + extSimData.queueExtMsg(new ActivationMessage(tick)) + def finishedActual = handleMessage.invoke(extSim) then: - finishedActual == finished - extSimAdapter.expectMsg(new CompletionMessage(newTickOpt)) + finishedActual == finished + extSimAdapter.expectMsg(new CompletionMessage(newTickOpt)) where: - tick | newTick || finished - 0L | [900L] || false - 3600L | [7200L] || false - 7200L | [] || true - 10800L | [] || true + tick | newTick || finished + 0L | [900L] || false + 3600L | [7200L] || false + 7200L | [] || true + 10800L | [] || true } def "An ExtSimulation should handle termination properly"() { given: - def extSimAdapter = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) - def extSim = new TestSimulation(-1L, Optional.empty()) - extSim.setup(extSimData, new ArrayList()) + def extSim = new TestSimulation(-1L, Optional.empty()) + extSim.setAdapterData(extSimData) when: - extSimData.queueExtMsg(new TerminationMessage(simlulationSuccessful)) - def finishedActual = handleMessage.invoke(extSim) + extSimData.queueExtMsg(new TerminationMessage(simlulationSuccessful)) + def finishedActual = handleMessage.invoke(extSim) then: - finishedActual == finished - extSimAdapter.expectMsg(new TerminationCompleted()) + finishedActual == finished + extSimAdapter.expectMsg(new TerminationCompleted()) where: - simlulationSuccessful || finished - false || true - true || true + simlulationSuccessful || finished + false || true + true || true } class UnknownMessage implements ControlMessageToExt {} def "An ExtSimulation should handle unknown messages by throwing an exception"() { given: - def extSimAdapter = new TestProbe(actorSystem) - def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) - def extSim = new TestSimulation(-1L, Optional.empty()) - extSim.setup(extSimData, new ArrayList()) + def extSimAdapter = new TestProbe(actorSystem) + def extSimData = new ExtSimAdapterData(extSimAdapter.ref(), new String[0]) + def extSim = new TestSimulation(-1L, Optional.empty()) + extSim.setAdapterData(extSimData) when: - extSimData.queueExtMsg(new UnknownMessage()) - handleMessage.invoke(extSim) + extSimData.queueExtMsg(new UnknownMessage()) + handleMessage.invoke(extSim) then: - Exception ex = thrown() - // since we call a private method through reflection, - // our expected exception is wrapped in an InvocationTargetException - ex.getCause().getClass() == IllegalArgumentException - extSimAdapter.expectNoMessage() + Exception ex = thrown() + // since we call a private method through reflection, + // our expected exception is wrapped in an InvocationTargetException + ex.getCause().getClass() == IllegalArgumentException + extSimAdapter.expectNoMessage() } } diff --git a/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSourceTest.groovy b/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSourceTest.groovy new file mode 100644 index 00000000..2589e219 --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingSourceTest.groovy @@ -0,0 +1,36 @@ +package edu.ie3.simona.api.simulation.mapping + +import edu.ie3.datamodel.models.input.NodeInput +import spock.lang.Specification + +import java.nio.file.Path + +class ExtEntityMappingSourceTest extends Specification { + + def "An ExtEntityMappingSource can create a naming correctly"() { + given: + def naming = new ExtEntityMappingSource.ExtEntityNaming("ext_entity_mapping") + + when: + def ext_entity_naming = naming.getEntityName(ExtEntityEntry) + def other = naming.getEntityName(NodeInput) + + then: + ext_entity_naming == Optional.of("ext_entity_mapping") + other == Optional.of("node_input") + } + + def "An ExtEntityMappingSource can read a mapping from file correctly"() { + given: + Path filePath = Path.of(ExtEntityMappingSourceTest.getResource("ext_entity_mapping.csv").toURI()) + + when: + def actual = ExtEntityMappingSource.fromFile(filePath) + + then: + actual.getExtUuid2IdMapping(DataType.EXT_PRIMARY_INPUT).size() == 2 + actual.getExtUuid2IdMapping(DataType.EXT_EM_INPUT).size() == 0 + actual.getExtUuid2IdMapping(DataType.EXT_GRID_RESULT).size() == 2 + actual.getExtUuid2IdMapping(DataType.EXT_PARTICIPANT_RESULT).size() == 0 + } +} \ No newline at end of file diff --git a/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingTest.groovy b/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingTest.groovy index cd92e957..9f49f614 100644 --- a/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingTest.groovy +++ b/src/test/groovy/edu/ie3/simona/api/simulation/mapping/ExtEntityMappingTest.groovy @@ -13,7 +13,7 @@ class ExtEntityMappingTest extends Specification { loadUuid, "Load", ColumnScheme.parse("p").get(), - "result_participant" + DataType.EXT_PARTICIPANT_RESULT ) @Shared @@ -21,7 +21,7 @@ class ExtEntityMappingTest extends Specification { loadUuid, "Load", ColumnScheme.parse("p").get(), - "input" + DataType.EXT_PRIMARY_INPUT ) def "ExtEntityMapping should return SIMONA uuid mapping correctly"() { @@ -30,21 +30,20 @@ class ExtEntityMappingTest extends Specification { def extEntryMapping = new ExtEntityMapping(extAssetList) when: - def inputMap = extEntryMapping.getExtId2UuidMapping("input") + def inputMap = extEntryMapping.getExtId2UuidMapping(DataType.EXT_PRIMARY_INPUT) then: inputMap.size() == 1 inputMap.get("Load") == loadUuid } - def "ExtEntityMapping should return external id mapping correctly"() { given: def extAssetList = List.of(extResultEntry, extInputEntry) def extEntryMapping = new ExtEntityMapping(extAssetList) when: - def inputMap = extEntryMapping.getExtUuid2IdMapping("input") + def inputMap = extEntryMapping.getExtUuid2IdMapping(DataType.EXT_PRIMARY_INPUT) then: inputMap.size() == 1 diff --git a/src/test/groovy/edu/ie3/simona/api/test/common/DataServiceTestData.groovy b/src/test/groovy/edu/ie3/simona/api/test/common/DataServiceTestData.groovy index c589bc1e..fa8e537d 100644 --- a/src/test/groovy/edu/ie3/simona/api/test/common/DataServiceTestData.groovy +++ b/src/test/groovy/edu/ie3/simona/api/test/common/DataServiceTestData.groovy @@ -3,11 +3,15 @@ package edu.ie3.simona.api.test.common import edu.ie3.datamodel.models.StandardUnits import edu.ie3.datamodel.models.result.system.LoadResult import edu.ie3.datamodel.models.value.PValue +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.units.indriya.quantity.Quantities import java.time.ZonedDateTime trait DataServiceTestData { + Logger log = LoggerFactory.getLogger(DataServiceTestData) + UUID inputUuid = UUID.fromString("22bea5fc-2cb2-4c61-beb9-b476e0107f52") PValue pValue = new PValue(Quantities.getQuantity(500.0, StandardUnits.ACTIVE_POWER_IN)) diff --git a/src/test/resources/edu/ie3/simona/api/simulation/mapping/ext_entity_mapping.csv b/src/test/resources/edu/ie3/simona/api/simulation/mapping/ext_entity_mapping.csv new file mode 100644 index 00000000..21635477 --- /dev/null +++ b/src/test/resources/edu/ie3/simona/api/simulation/mapping/ext_entity_mapping.csv @@ -0,0 +1,5 @@ +uuid,id,column_scheme,data_type +00d03670-7833-47ee-ad52-04d18d1c64fd,NS_Node_1,p,grid_result +dfae9806-9b44-4995-ba27-d66d8e4a43e0,NS_Node_2,p,grid_result +4dca3b1d-5d24-444a-b4df-f4fa23b9ef1b,LOAD_NS_Node_1,pq,primary_input +9c5991bc-24df-496b-b4ce-5ec27657454c,LOAD_NS_Node_2,pq,primary_input \ No newline at end of file