diff --git a/CHANGELOG.md b/CHANGELOG.md index 250ecfeb..98b6335f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased/Snapshot] +### Added +- Implemented `ExtPrimaryData` and `ExtResultsData` [#145](https://github.com/ie3-institute/simonaAPI/issues/145) + ## [0.4.0] - 2023-11-22 ### Changed diff --git a/build.gradle b/build.gradle index a30ed5de..244b1574 100644 --- a/build.gradle +++ b/build.gradle @@ -51,12 +51,28 @@ dependencies{ // scala (needed for pekko) implementation "org.scala-lang:scala-library:${scalaBinaryVersion}" + //PSDM + implementation('com.github.ie3-institute:PowerSystemUtils:2.0') { + exclude group: 'org.apache.logging.log4j' + exclude group: 'org.slf4j' + /* Exclude our own nested dependencies */ + exclude group: 'com.github.ie3-institute' + } + implementation('com.github.ie3-institute:PowerSystemDataModel:4.1.0') { + exclude group: 'org.apache.logging.log4j' + exclude group: 'org.slf4j' + /* Exclude our own nested dependencies */ + exclude group: 'com.github.ie3-institute' + } + // pekko implementation "org.apache.pekko:pekko-actor_${scalaVersion}:${pekkoVersion}" testImplementation "org.apache.pekko:pekko-testkit_${scalaVersion}:${pekkoVersion}" // pekko testkit // TESTING testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' + + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.16.1' } task printVersion { diff --git a/gradle/scripts/jacoco.gradle b/gradle/scripts/jacoco.gradle index 0e1cc600..c1349b98 100644 --- a/gradle/scripts/jacoco.gradle +++ b/gradle/scripts/jacoco.gradle @@ -28,7 +28,7 @@ jacocoTestReport { ] getClassDirectories().setFrom(fileTree( - dir: "${layout.buildDirectory}/classes/", + dir: layout.buildDirectory.dir("classes"), excludes: excludes )) diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java new file mode 100644 index 00000000..7188e299 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryData.java @@ -0,0 +1,63 @@ +/* + * © 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.primarydata; + +import edu.ie3.datamodel.models.value.Value; +import edu.ie3.simona.api.data.ExtData; +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 edu.ie3.simona.api.exceptions.ConvertionException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.apache.pekko.actor.ActorRef; + +public class ExtPrimaryData implements ExtData { + + /** Actor reference to service that handles ev data within SIMONA */ + private final ActorRef dataService; + + /** Actor reference to adapter that handles scheduler control flow in SIMONA */ + private final ActorRef extSimAdapter; + + /** Factory to convert an external object to PSDM primary data */ + private final PrimaryDataFactory factory; + + public ExtPrimaryData(ActorRef dataService, ActorRef extSimAdapter, PrimaryDataFactory factory) { + this.dataService = dataService; + this.extSimAdapter = extSimAdapter; + this.factory = factory; + } + + /** Provide primary data from an external simulation in one tick. */ + public void providePrimaryData(Long tick, Map primaryData) { + Map convertedMap = new HashMap<>(); + primaryData.forEach( + (k, v) -> { + try { + convertedMap.put(UUID.fromString(k), factory.convert(v)); + } catch (ConvertionException e) { + throw new RuntimeException(e); + } + }); + sendExtMsg(new ProvidePrimaryData(tick, convertedMap)); + } + + /** + * Send information from the external simulation to SIMONA's external primary data service. + * Furthermore, ExtSimAdapter within SIMONA is instructed to activate the ev data service with the + * current tick. + * + * @param msg the data/information that is sent to SIMONA's external primary data service + */ + public void sendExtMsg(PrimaryDataMessageFromExt msg) { + dataService.tell(msg, ActorRef.noSender()); + // we need to schedule data receiver activation with scheduler + extSimAdapter.tell(new ScheduleDataServiceMessage(dataService), ActorRef.noSender()); + } +} diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataSimulation.java b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataSimulation.java new file mode 100644 index 00000000..d2172095 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataSimulation.java @@ -0,0 +1,22 @@ +/* + * © 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.primarydata; + +import edu.ie3.simona.api.data.ExtDataSimulation; + +/** + * An external simulation that provides primary data should implement this interface and handle the + * ExtPrimaryData that is handed over. + */ +public interface ExtPrimaryDataSimulation extends ExtDataSimulation { + + /** Hand over an ExtPrimaryData which enables communication regarding primary data. */ + void setExtPrimaryData(ExtPrimaryData extPrimaryData); + + /** Should implement the convertion of the external format to the PSDM format of primary data. */ + PrimaryDataFactory getPrimaryDataFactory(); +} diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/PrimaryDataFactory.java b/src/main/java/edu/ie3/simona/api/data/primarydata/PrimaryDataFactory.java new file mode 100644 index 00000000..9ff74d5f --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/PrimaryDataFactory.java @@ -0,0 +1,17 @@ +/* + * © 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.primarydata; + +import edu.ie3.datamodel.models.value.Value; +import edu.ie3.simona.api.exceptions.ConvertionException; + +/** Interface that should be implemented by an external simulation. */ +public interface PrimaryDataFactory { + + /** Should convert an object to a primary data value with a check if the object is primary data */ + Value convert(Object entity) throws ConvertionException; +} diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/PrimaryDataMessageFromExt.java b/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/PrimaryDataMessageFromExt.java new file mode 100644 index 00000000..e7eb7ae6 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/PrimaryDataMessageFromExt.java @@ -0,0 +1,12 @@ +/* + * © 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.primarydata.ontology; + +import edu.ie3.simona.api.data.ontology.DataMessageFromExt; + +/** Messages that are sent from an external primary data simulation to SIMONA */ +public interface PrimaryDataMessageFromExt extends DataMessageFromExt {} diff --git a/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/ProvidePrimaryData.java b/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/ProvidePrimaryData.java new file mode 100644 index 00000000..a87d9f7d --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/primarydata/ontology/ProvidePrimaryData.java @@ -0,0 +1,15 @@ +/* + * © 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.primarydata.ontology; + +import edu.ie3.datamodel.models.value.Value; +import java.util.Map; +import java.util.UUID; + +/** Message that provides primary data from an external primary data simulation */ +public record ProvidePrimaryData(long tick, Map primaryData) + implements PrimaryDataMessageFromExt {} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java b/src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java new file mode 100644 index 00000000..99fd1c76 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ExtResultData.java @@ -0,0 +1,113 @@ +/* + * © 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.results; + +import edu.ie3.datamodel.models.result.ResultEntity; +import edu.ie3.simona.api.data.ExtData; +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; +import edu.ie3.simona.api.data.results.ontology.ResultDataMessageFromExt; +import edu.ie3.simona.api.data.results.ontology.ResultDataResponseMessageToExt; +import edu.ie3.simona.api.exceptions.ConvertionException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import org.apache.pekko.actor.ActorRef; + +public class ExtResultData implements ExtData { + + /** 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; + + /** Actor reference to adapter that handles scheduler control flow in SIMONA */ + private final ActorRef extSimAdapter; + + private final ResultDataFactory factory; + + public ExtResultData(ActorRef dataService, ActorRef extSimAdapter, ResultDataFactory factory) { + this.dataService = dataService; + this.extSimAdapter = extSimAdapter; + this.factory = factory; + } + + /** Method that an external simulation can request results from SIMONA as a list. */ + public List requestResults() throws InterruptedException { + sendExtMsg(new RequestResultEntities()); + return receiveWithType(ProvideResultEntities.class).results(); + } + + /** + * Method that an external simulation can request results from SIMONA as a map string to object. + */ + public Map requestResultObjects() + throws ConvertionException, InterruptedException { + return convertResultsList(requestResults()); + } + + protected Map convertResultsList(List results) + throws ConvertionException { + Map resultsMap = new HashMap<>(); + Object convertedResult; + for (ResultEntity res : results) { + convertedResult = factory.convert(res); + resultsMap.put(res.getUuid().toString(), convertedResult); + } + return resultsMap; + } + + /** + * Send information from the external simulation to SIMONA's external data service. Furthermore, + * ExtSimAdapter within SIMONA is instructed to activate the external data service with the + * current tick. + * + * @param msg the data/information that is sent to SIMONA's result data service + */ + public void sendExtMsg(ResultDataMessageFromExt msg) { + dataService.tell(msg, ActorRef.noSender()); + // we need to schedule data receiver activation with scheduler + extSimAdapter.tell(new ScheduleDataServiceMessage(dataService), ActorRef.noSender()); + } + + /** Queues message from SIMONA that should be handled by the external simulation. */ + public void queueExtResponseMsg(ResultDataResponseMessageToExt msg) throws InterruptedException { + receiveTriggerQueue.put(msg); + } + + /** + * Waits until a message of given type is added to the queue. If the message has a different type, + * a RuntimeException is thrown. This method blocks until having received a response from SIMONA. + * + * @param expectedMessageClass the expected class of the message to be received + * @return a message of the expected type once it has been received + * @param the type of the expected message + * @throws InterruptedException if the thread running this has been interrupted during the + * blocking operation + */ + @SuppressWarnings("unchecked") + private T receiveWithType( + Class expectedMessageClass) throws InterruptedException { + + // blocks until actor puts something here + ResultDataResponseMessageToExt msg = receiveTriggerQueue.take(); + + if (msg.getClass().equals(expectedMessageClass)) { + return (T) msg; + } else + throw new RuntimeException( + "Received unexpected message '" + + msg + + "', expected type '" + + expectedMessageClass + + "'"); + } +} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ExtResultDataSimulation.java b/src/main/java/edu/ie3/simona/api/data/results/ExtResultDataSimulation.java new file mode 100644 index 00000000..31ea7ac5 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ExtResultDataSimulation.java @@ -0,0 +1,22 @@ +/* + * © 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.results; + +import edu.ie3.simona.api.data.ExtDataSimulation; + +/** + * An external simulation that needs results from SIMONA should implement this interface and handle + * the ExtResultsData that is handed over. + */ +public interface ExtResultDataSimulation extends ExtDataSimulation { + + /** Hand over an ExtPrimaryData which enables communication regarding primary data. */ + void setExtResultData(ExtResultData extResultData); + + /** Should implement the convertion of the PSDM format to the external format of result data. */ + ResultDataFactory getResultDataFactory(); +} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ResultDataFactory.java b/src/main/java/edu/ie3/simona/api/data/results/ResultDataFactory.java new file mode 100644 index 00000000..16329441 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ResultDataFactory.java @@ -0,0 +1,19 @@ +/* + * © 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.results; + +import edu.ie3.datamodel.models.result.ResultEntity; +import edu.ie3.simona.api.exceptions.ConvertionException; + +public interface ResultDataFactory { + + /** + * Should convert a result entity to an object, that can be read by the external simulation, with + * a check if the object is primary data + */ + Object convert(ResultEntity entity) throws ConvertionException; +} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ontology/ProvideResultEntities.java b/src/main/java/edu/ie3/simona/api/data/results/ontology/ProvideResultEntities.java new file mode 100644 index 00000000..26b4ae55 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ontology/ProvideResultEntities.java @@ -0,0 +1,14 @@ +/* + * © 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.results.ontology; + +import edu.ie3.datamodel.models.result.ResultEntity; +import java.util.List; + +/** Provides a list of results from SIMONA to an external simulation. */ +public record ProvideResultEntities(List results) + implements ResultDataResponseMessageToExt {} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ontology/RequestResultEntities.java b/src/main/java/edu/ie3/simona/api/data/results/ontology/RequestResultEntities.java new file mode 100644 index 00000000..05f8aae9 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ontology/RequestResultEntities.java @@ -0,0 +1,10 @@ +/* + * © 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.results.ontology; + +/** Request calculated results from SIMONA in the current tick */ +public record RequestResultEntities() implements ResultDataMessageFromExt {} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataMessageFromExt.java b/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataMessageFromExt.java new file mode 100644 index 00000000..60ebff47 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataMessageFromExt.java @@ -0,0 +1,10 @@ +/* + * © 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.results.ontology; + +/** Messages that are sent from an external simulation to the SIMONA */ +public interface ResultDataMessageFromExt {} diff --git a/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataResponseMessageToExt.java b/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataResponseMessageToExt.java new file mode 100644 index 00000000..311ff1f4 --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/results/ontology/ResultDataResponseMessageToExt.java @@ -0,0 +1,12 @@ +/* + * © 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.results.ontology; + +import edu.ie3.simona.api.data.ontology.DataResponseMessageToExt; + +/** Messages that are sent from SIMONA to the external simulation that needs results */ +public interface ResultDataResponseMessageToExt extends DataResponseMessageToExt {} diff --git a/src/main/java/edu/ie3/simona/api/exceptions/ConvertionException.java b/src/main/java/edu/ie3/simona/api/exceptions/ConvertionException.java new file mode 100644 index 00000000..2065203a --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/exceptions/ConvertionException.java @@ -0,0 +1,14 @@ +/* + * © 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 ConvertionException extends Exception { + + public ConvertionException(final String message) { + super(message); + } +} 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 new file mode 100644 index 00000000..7163f0bd --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/data/primarydata/ExtPrimaryDataTest.groovy @@ -0,0 +1,62 @@ +package edu.ie3.simona.api.data.primarydata + +import edu.ie3.datamodel.models.StandardUnits +import edu.ie3.datamodel.models.value.PValue +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.exceptions.ConvertionException +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 +import tech.units.indriya.quantity.Quantities + +class ExtPrimaryDataTest extends Specification { + + @Shared + ActorSystem actorSystem + + + class PValuePrimaryDataFactory implements PrimaryDataFactory { + + @Override + Value convert(Object entity) throws ConvertionException { + if (entity.getClass() == PValue) { + return (PValue) entity + } else { + throw new ConvertionException("This factory can convert PValue entities only!") + } + } + } + + 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(dataService.ref(), extSimAdapter.ref(), new PValuePrimaryDataFactory()) + + def primaryData = new HashMap() + def uuid = UUID.randomUUID() + primaryData.put(uuid.toString(), new PValue(Quantities.getQuantity(500.0, StandardUnits.ACTIVE_POWER_IN))) + + def convertedPrimaryData = Map.of(uuid, new PValue(Quantities.getQuantity(500.0, StandardUnits.ACTIVE_POWER_IN))) + + when: + extPrimaryData.providePrimaryData(0, primaryData) + + then: + dataService.expectMsg(new ProvidePrimaryData(0, convertedPrimaryData)) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + } +} diff --git a/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy new file mode 100644 index 00000000..8875b690 --- /dev/null +++ b/src/test/groovy/edu/ie3/simona/api/data/results/ExtResultDataTest.groovy @@ -0,0 +1,131 @@ +package edu.ie3.simona.api.data.results + +import edu.ie3.datamodel.models.StandardUnits +import edu.ie3.datamodel.models.result.ResultEntity +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 +import edu.ie3.simona.api.data.results.ontology.ResultDataResponseMessageToExt +import edu.ie3.simona.api.exceptions.ConvertionException +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 +import tech.units.indriya.quantity.Quantities + +import java.time.ZonedDateTime + +class ExtResultDataTest extends Specification { + + @Shared + ActorSystem actorSystem + + @Shared + UUID loadUuid = UUID.fromString("22bea5fc-2cb2-4c61-beb9-b476e0107f52") + + @Shared + LoadResult loadResult = new LoadResult( + loadUuid, + ZonedDateTime.parse("2020-01-30T17:26:44Z[UTC]"), + UUID.fromString("22bea5fc-2cb2-4c61-beb9-b476e0107f52"), + Quantities.getQuantity(10, StandardUnits.ACTIVE_POWER_IN), + Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN) + ) + + class DefaultResultFactory implements ResultDataFactory { + + @Override + Object convert(ResultEntity entity) throws ConvertionException { + if (entity instanceof LoadResult) { + return "{\"p\":\"" + entity.p.toString() + ",\"q\":\"" + entity.q.toString() + "\"}" + } else { + throw new ConvertionException("This factory can convert LoadResult's only!") + } + } + } + + class WrongResultDataResponseMessageToExt implements ResultDataResponseMessageToExt {} + + def setupSpec() { + actorSystem = ActorSystem.create() + } + + def cleanupSpec() { + TestKit.shutdownActorSystem(actorSystem) + actorSystem = null + } + + def "ExtResultsData should request and receive results correctly as Object"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def resultDataFactory = new DefaultResultFactory() + def extResultData = new ExtResultData(dataService.ref(), extSimAdapter.ref(), resultDataFactory) + + def sentMsg = new ProvideResultEntities([loadResult]) + + when: + // we need to queue the msg beforehand because the receive method is blocking + extResultData.queueExtResponseMsg(sentMsg) + def receivedResults = extResultData.requestResultObjects() + + then: + dataService.expectMsg(new RequestResultEntities()) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + receivedResults.get(loadUuid.toString()) == resultDataFactory.convert(loadResult) + } + + def "ExtResultsData should request and receive results correctly as a list of results entities"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extResultData = new ExtResultData(dataService.ref(), extSimAdapter.ref(), new DefaultResultFactory()) + + def sentMsg = new ProvideResultEntities([loadResult]) + + when: + // we need to queue the msg beforehand because the receive method is blocking + extResultData.queueExtResponseMsg(sentMsg) + def receivedResults = extResultData.requestResults() + + then: + dataService.expectMsg(new RequestResultEntities()) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + receivedResults == sentMsg.results() + } + + def "ExtResultsData should fail if wrong response is sent"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extResultData = new ExtResultData(dataService.ref(), extSimAdapter.ref(), new DefaultResultFactory()) + + def unexpectedMsg = new WrongResultDataResponseMessageToExt() + + when: + // we need to queue the msg beforehand because the receive method is blocking + extResultData.queueExtResponseMsg(unexpectedMsg) + extResultData.requestResults() + + then: + dataService.expectMsg(new RequestResultEntities()) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(dataService.ref())) + thrown RuntimeException + } + + def "ExtResultData should convert a list of result entities correctly to a map of objects"() { + given: + def dataService = new TestProbe(actorSystem) + def extSimAdapter = new TestProbe(actorSystem) + def extResultData = new ExtResultData(dataService.ref(), extSimAdapter.ref(), new DefaultResultFactory()) + + when: + def mapOfResults = extResultData.convertResultsList([loadResult]) + + then: + mapOfResults.size() == 1 + mapOfResults.get(loadUuid.toString()) == "{\"p\":\"10 kW,\"q\":\"10 kvar\"}" + } +}