diff --git a/CHANGELOG.md b/CHANGELOG.md index 97e75c5733..ecb4d3e6aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update RTD references and bibliography [#868](https://github.com/ie3-institute/simona/issues/868) - Add gradle application plugin for command line execution with gradle run [#890](https://github.com/ie3-institute/simona/issues/890) - Additional tests to check flexibility options of thermal house and storage [#729](https://github.com/ie3-institute/simona/issues/729) +- EmAgents should be able to handle initialization [#945](https://github.com/ie3-institute/simona/issues/945) ### Changed - Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435) @@ -78,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `Gradle` to version V8.10 [#829](https://github.com/ie3-institute/simona/issues/829) - Updated AUTHORS.md [#905](https://github.com/ie3-institute/simona/issues/905) - Rewrote BMModelTest from groovy to scala [#646](https://github.com/ie3-institute/simona/issues/646) +- Refactoring EM messages [#947](https://github.com/ie3-institute/simona/issues/947) - Prepare ThermalStorageTestData for Storage without storageVolumeLvlMin [#894](https://github.com/ie3-institute/simona/issues/894) - Refactoring of `ThermalGrid.energyGrid` to distinguish between demand of house and storage [#928](https://github.com/ie3-institute/simona/issues/928) @@ -107,6 +109,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed ThermalStorageResults having multiple entries [#924](https://github.com/ie3-institute/simona/issues/924) - Fix determineState of ThermalHouse [#926](https://github.com/ie3-institute/simona/issues/926) - Fix activation of Hp when not under control of an EM [#922](https://github.com/ie3-institute/simona/issues/922) +- Fix expected secondaryData in baseStateData [#955](https://github.com/ie3-institute/simona/issues/955) - Refactoring of `ThermalGrid.handleInfeed` to fix thermal storage recharge correctly when empty [#930](https://github.com/ie3-institute/simona/issues/930) ## [3.0.0] - 2023-08-07 diff --git a/build.gradle b/build.gradle index b17292a4cb..b47bc21820 100644 --- a/build.gradle +++ b/build.gradle @@ -26,8 +26,8 @@ ext { scalaVersion = '2.13' scalaBinaryVersion = '2.13.14' - pekkoVersion = '1.0.3' - jtsVersion = '1.19.0' + pekkoVersion = '1.1.1' + jtsVersion = '1.20.0' confluentKafkaVersion = '7.4.0' tscfgVersion = '1.1.3' scapegoatVersion = '3.0.0' @@ -98,7 +98,7 @@ dependencies { /* logging */ implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.5" // pekko scala logging - implementation "ch.qos.logback:logback-classic:1.5.7" + implementation "ch.qos.logback:logback-classic:1.5.8" /* testing */ testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' diff --git a/docs/readthedocs/models/em.md b/docs/readthedocs/models/em.md index f59f96a036..ae10cb3ac2 100644 --- a/docs/readthedocs/models/em.md +++ b/docs/readthedocs/models/em.md @@ -6,11 +6,11 @@ Energy Management Agents (EmAgents) control power feed-in and load of system par ## Protocol -During simulation, EmAgents send `RequestFlexOptions` and `IssueFlexControl` messages and receive `ProvideFlexOptions` and `FlexCompletion` messages. -After having been requested to calculate flex options via `RequestFlexOptions`, controllable assets send back their flex options to the controlling unit using `ProvideFlexOptions`. +During simulation, EmAgents send `FlexActivation` and `IssueFlexControl` messages and receive `ProvideFlexOptions` and `FlexCompletion` messages. +After being requested to calculate flex options via `FlexActivation`, controllable assets send back their flex options to the controlling unit using `ProvideFlexOptions`. Eventually the controlling EmAgent responds with some type of `IssueFlexControl` messages, setting a power set point for operation. The asset then tries to realize the set power as best as it can and replies with a `FlexCompletion` messages. -If an EmAgent is itself controlled by another EmAgent, it also behaves like a system participant (sends `RequestFlexOptions` and `IssueFlexControl` messages etc.). +If an EmAgent is itself controlled by another EmAgent, it also behaves like a system participant (sends `FlexActivation` and `IssueFlexControl` messages etc.). Every EmAgent aggregates flex options and power of its connected assets and disaggregates flex control among the connected assets. diff --git a/docs/readthedocs/requirements.txt b/docs/readthedocs/requirements.txt index 34b2f0762a..6c1bfcb1a8 100644 --- a/docs/readthedocs/requirements.txt +++ b/docs/readthedocs/requirements.txt @@ -1,7 +1,7 @@ -Sphinx==7.3.7 +Sphinx==7.4.7 sphinx-rtd-theme==2.0.0 sphinxcontrib-plantuml==0.30 myst-parser==4.0.0 markdown-it-py==3.0.0 -sphinx-hoverxref==1.4.0 -sphinxcontrib-bibtex==2.6.2 +sphinx-hoverxref==1.4.1 +sphinxcontrib-bibtex==2.6.3 diff --git a/docs/uml/protocol/em/ControlledEm.puml b/docs/uml/protocol/em/ControlledEm.puml index 385170e355..7310a5b7c5 100644 --- a/docs/uml/protocol/em/ControlledEm.puml +++ b/docs/uml/protocol/em/ControlledEm.puml @@ -54,13 +54,13 @@ deactivate WeatherService Scheduler -> EmAgent1: Activation(tick=0) activate EmAgent1 -EmAgent1 -> EmAgent2: RequestFlexOptions(tick=0) +EmAgent1 -> EmAgent2: FlexActivation(tick=0) activate EmAgent2 -EmAgent2 -> StorageAgent: RequestFlexOptions(tick=0) +EmAgent2 -> StorageAgent: FlexActivation(tick=0) activate StorageAgent -EmAgent2 -> PvAgent: RequestFlexOptions(tick=0) +EmAgent2 -> PvAgent: FlexActivation(tick=0) activate PvAgent PvAgent -> EmAgent2: ProvideFlexOptions @@ -81,12 +81,15 @@ activate PvAgent EmAgent2 -> StorageAgent: IssuePowerControl(tick=0) activate StorageAgent +PvAgent -> EmAgent2: FlexResult PvAgent -> EmAgent2: FlexCompletion(nextTick=3600) deactivate PvAgent +StorageAgent -> EmAgent2: FlexResult StorageAgent -> EmAgent2: FlexCompletion(nextTick=1805) deactivate StorageAgent +EmAgent2 -> EmAgent1: FlexResult EmAgent2 -> EmAgent1: FlexCompletion(nextTick=1805) deactivate EmAgent2 @@ -98,10 +101,10 @@ deactivate EmAgent1 Scheduler -> EmAgent1: Activation(tick=1805) activate EmAgent1 -EmAgent1 -> EmAgent2: RequestFlexOptions(tick=1805) +EmAgent1 -> EmAgent2: FlexActivation(tick=1805) activate EmAgent2 -EmAgent2 -> StorageAgent: RequestFlexOptions(tick=1805) +EmAgent2 -> StorageAgent: FlexActivation(tick=1805) activate StorageAgent StorageAgent -> EmAgent2: ProvideFlexOptions @@ -116,9 +119,11 @@ activate EmAgent2 EmAgent2 -> StorageAgent: IssuePowerControl(tick=1805) activate StorageAgent +StorageAgent -> EmAgent2: FlexResult StorageAgent -> EmAgent2: FlexCompletion(nextTick=10800) deactivate StorageAgent +EmAgent2 -> EmAgent1: FlexResult EmAgent2 -> EmAgent1: FlexCompletion(nextTick=3600) deactivate EmAgent2 diff --git a/docs/uml/protocol/em/UncontrolledEm.puml b/docs/uml/protocol/em/UncontrolledEm.puml index 16bdc19619..fc3e2dd3ef 100644 --- a/docs/uml/protocol/em/UncontrolledEm.puml +++ b/docs/uml/protocol/em/UncontrolledEm.puml @@ -50,10 +50,10 @@ deactivate WeatherService Scheduler -> EmAgent: Activation(tick=0) activate EmAgent -EmAgent -> StorageAgent: RequestFlexOptions(tick=0) +EmAgent -> StorageAgent: FlexActivation(tick=0) activate StorageAgent -EmAgent -> PvAgent: RequestFlexOptions(tick=0) +EmAgent -> PvAgent: FlexActivation(tick=0) activate PvAgent PvAgent -> EmAgent: ProvideFlexOptions @@ -68,9 +68,11 @@ activate PvAgent EmAgent -> StorageAgent: IssuePowerControl(tick=0) activate StorageAgent +PvAgent -> EmAgent: FlexResult PvAgent -> EmAgent: FlexCompletion(nextTick=3600) deactivate PvAgent +StorageAgent -> EmAgent: FlexResult StorageAgent -> EmAgent: FlexCompletion(nextTick=8400) deactivate StorageAgent diff --git a/src/main/scala/edu/ie3/simona/agent/em/EmAgent.scala b/src/main/scala/edu/ie3/simona/agent/em/EmAgent.scala index 06a7bf598a..fc95473bf8 100644 --- a/src/main/scala/edu/ie3/simona/agent/em/EmAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/em/EmAgent.scala @@ -191,13 +191,16 @@ object EmAgent { val flexOptionsCore = core.activate(msg.tick) msg match { - case Flex(_: RequestFlexOptions) | EmActivation(_) => + case Flex(_: FlexActivation) | EmActivation(_) => val (toActivate, newCore) = flexOptionsCore.takeNewFlexRequests() toActivate.foreach { - _ ! RequestFlexOptions(msg.tick) + _ ! FlexActivation(msg.tick) } - awaitingFlexOptions(emData, modelShell, newCore) + newCore.fold( + awaitingFlexOptions(emData, modelShell, _), + awaitingCompletions(emData, modelShell, _), + ) case Flex(_: IssueFlexControl) => // We got sent a flex control message instead of a flex request, @@ -359,8 +362,16 @@ object EmAgent { modelShell: EmModelShell, core: EmDataCore.AwaitingCompletions, ): Behavior[Request] = Behaviors.receiveMessagePartial { - // Completions and results - case completion: FlexCtrlCompletion => + case result: FlexResult => + val updatedCore = core.handleResult(result) + + awaitingCompletions( + emData, + modelShell, + updatedCore, + ) + + case completion: FlexCompletion => val updatedCore = core.handleCompletion(completion) updatedCore @@ -385,34 +396,38 @@ object EmAgent { } + /** Completions have all been received, possibly send results and report to + * parent + */ private def sendCompletionCommunication( emData: EmData, modelShell: EmModelShell, inactiveCore: EmDataCore.Inactive, lastActiveTick: Long, ): Unit = { - // calc result - val result = inactiveCore.getResults + // Sum up resulting power, if applicable. + // After initialization, there are no results yet. + val maybeResult = inactiveCore.getResults .reduceOption { (power1, power2) => ApparentPower(power1.p + power2.p, power1.q + power2.q) } - .getOrElse( - ApparentPower( - zeroMW, - zeroMVAr, - ) - ) - emData.listener.foreach { - _ ! ParticipantResultEvent( - new EmResult( - lastActiveTick - .toDateTime(emData.simulationStartDate), - modelShell.uuid, - result.p.toMegawatts.asMegaWatt, - result.q.toMegavars.asMegaVar, + maybeResult.foreach { result => + emData.listener.foreach { + _ ! ParticipantResultEvent( + new EmResult( + lastActiveTick + .toDateTime(emData.simulationStartDate), + modelShell.uuid, + result.p.toMegawatts.asMegaWatt, + result.q.toMegavars.asMegaVar, + ) ) - ) + } + + emData.parentData.foreach { + _.emAgent ! FlexResult(modelShell.uuid, result) + } } emData.parentData.fold( @@ -421,9 +436,8 @@ object EmAgent { schedulerData.activationAdapter, inactiveCore.nextActiveTick, ), - _.emAgent ! FlexCtrlCompletion( + _.emAgent ! FlexCompletion( modelShell.uuid, - result, inactiveCore.hasFlexWithNext, inactiveCore.nextActiveTick, ), diff --git a/src/main/scala/edu/ie3/simona/agent/em/EmDataCore.scala b/src/main/scala/edu/ie3/simona/agent/em/EmDataCore.scala index 3b46b176ee..9cd9070de8 100644 --- a/src/main/scala/edu/ie3/simona/agent/em/EmDataCore.scala +++ b/src/main/scala/edu/ie3/simona/agent/em/EmDataCore.scala @@ -6,11 +6,12 @@ package edu.ie3.simona.agent.em -import edu.ie3.simona.exceptions.CriticalFailureException +import edu.ie3.simona.agent.em.EmAgent.Actor +import edu.ie3.simona.agent.em.FlexCorrespondenceStore.WithTime import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower -import EmAgent.Actor -import FlexCorrespondenceStore.WithTime +import edu.ie3.simona.exceptions.CriticalFailureException import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._ +import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.util.scala.collection.mutable.PriorityMultiBiSet import squants.Power @@ -178,8 +179,9 @@ object EmDataCore { * @param correspondences * The data structure storing received and sent flex messages with the * corresponding tick - * @param awaitedFlexOptions - * The set of model uuids, from which flex options are still expected + * @param awaitedConnectedAgents + * The set of model uuids, from which flex options or completions are still + * expected * @param activeTick * The currently active tick */ @@ -187,24 +189,28 @@ object EmDataCore { private val modelToActor: Map[UUID, Actor], private val activationQueue: PriorityMultiBiSet[Long, UUID], private val correspondences: FlexCorrespondenceStore, - private val awaitedFlexOptions: Set[UUID] = Set.empty, + private val awaitedConnectedAgents: Set[UUID] = Set.empty, activeTick: Long, ) { /** Removes and returns flex requests scheduled for the current tick, which - * can be sent out at the current moment. + * can be sent out at the current moment. Depending on the current tick, + * the next state of the [[EmAgent]] is chosen. During initialization, flex + * request, flex control and results are not expected, thus we change to + * [[AwaitingCompletions]] right away. * * @return * A tuple of a collection of agents scheduled for the current tick, and - * the updated [[AwaitingFlexOptions]] core + * either an updated [[AwaitingFlexOptions]] core or an + * [[AwaitingCompletions]] core if we're in initialization * @throws CriticalFailureException * on critical error */ - def takeNewFlexRequests(): (Iterable[Actor], AwaitingFlexOptions) = { + def takeNewFlexRequests(): ( + Iterable[Actor], + Either[AwaitingFlexOptions, AwaitingCompletions], + ) = { val toActivate = activationQueue.getAndRemoveSet(activeTick) - val newFlexOptionsCore = - copy(awaitedFlexOptions = awaitedFlexOptions.concat(toActivate)) - val actors = toActivate.map { modelUuid => modelToActor .getOrElse( @@ -215,7 +221,25 @@ object EmDataCore { ) } - (actors, newFlexOptionsCore) + val newCore = if (activeTick == INIT_SIM_TICK) { + Right( + AwaitingCompletions( + modelToActor, + activationQueue = activationQueue, + correspondences = correspondences, + awaitedCompletions = awaitedConnectedAgents.concat(toActivate), + activeTick = activeTick, + ) + ) + } else { + Left( + copy(awaitedConnectedAgents = + awaitedConnectedAgents.concat(toActivate) + ) + ) + } + + (actors, newCore) } /** Handles the retrieval of flex options sent by some connected agent for @@ -232,7 +256,8 @@ object EmDataCore { copy( correspondences = correspondences.updateFlexOptions(flexOptions, activeTick), - awaitedFlexOptions = awaitedFlexOptions.excl(flexOptions.modelUuid), + awaitedConnectedAgents = + awaitedConnectedAgents.excl(flexOptions.modelUuid), ) /** Checks whether all awaited flex options have been received and we can @@ -241,7 +266,7 @@ object EmDataCore { * @return * true if all awaited flex options have been received */ - def isComplete: Boolean = awaitedFlexOptions.isEmpty + def isComplete: Boolean = awaitedConnectedAgents.isEmpty /** Returns all flex options that are currently relevant, which can include * flex options received at an earlier tick @@ -389,6 +414,26 @@ object EmDataCore { activeTick: Long, ) { + /** Handles a result by some connected agent for the currently active tick. + * + * @param flexResult + * The received result + * @return + * The updated [[AwaitingCompletions]] core + */ + def handleResult(flexResult: FlexResult): AwaitingCompletions = { + val updatedCorrespondence = + correspondences.updateResult( + flexResult.modelUuid, + flexResult.result, + activeTick, + ) + + copy( + correspondences = updatedCorrespondence + ) + } + /** Tries to handle the completion of some connected agent for the currently * active tick. If completion is not valid, a [[CriticalFailureException]] * is thrown. @@ -401,7 +446,7 @@ object EmDataCore { * on critical error */ def handleCompletion( - completion: FlexCtrlCompletion + completion: FlexCompletion ): AwaitingCompletions = { if (!awaitedCompletions.contains(completion.modelUuid)) throw new CriticalFailureException( @@ -412,20 +457,12 @@ object EmDataCore { completion.requestAtTick .foreach { activationQueue.set(_, completion.modelUuid) } - val updatedCorrespondence = - correspondences.updateResult( - completion.modelUuid, - completion.result, - activeTick, - ) - val updatedFlexWithNext = if (completion.requestAtNextActivation) flexWithNext.incl(completion.modelUuid) else flexWithNext copy( - correspondences = updatedCorrespondence, flexWithNext = updatedFlexWithNext, awaitedCompletions = awaitedCompletions.excl(completion.modelUuid), ) diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala index 8bfac96858..52e3a53a59 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala @@ -49,7 +49,7 @@ import edu.ie3.simona.ontology.messages.Activation import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{ FlexResponse, IssueFlexControl, - RequestFlexOptions, + FlexActivation, } import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ @@ -221,7 +221,7 @@ abstract class ParticipantAgent[ finalizeTickAfterPF(baseStateData, tick) case Event( - RequestFlexOptions(tick), + FlexActivation(tick), baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M], ) => val expectedSenders = baseStateData.foreseenDataTicks @@ -336,7 +336,7 @@ abstract class ParticipantAgent[ )(stateData.baseStateData.outputConfig) case Event( - RequestFlexOptions(tick), + FlexActivation(tick), stateData: DataCollectionStateData[PD], ) => checkForExpectedDataAndChangeState( @@ -436,13 +436,17 @@ abstract class ParticipantAgent[ _, ), ) => - val updatedReceivedSecondaryData = ValueStore.updateValueStore( - participantStateData.receivedSecondaryDataStore, - currentTick, - data.map { case (actorRef, Some(data: SecondaryData)) => - actorRef -> data - }, - ) + val updatedReceivedSecondaryData = data match { + case nonEmptyData if nonEmptyData.nonEmpty => + ValueStore.updateValueStore( + participantStateData.receivedSecondaryDataStore, + currentTick, + nonEmptyData.collect { case (actorRef, Some(data: SecondaryData)) => + actorRef -> data + }, + ) + case _ => participantStateData.receivedSecondaryDataStore + } /* At least parts of the needed data has been received or it is an additional activation, that has been triggered. * Anyways, the calculation routine has also to take care of filling up missing data. */ diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala index e9fef1ad20..ee2d4941ff 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -797,9 +797,13 @@ protected trait ParticipantAgentFundamentals[ ) .getOrElse((flexChangeIndicator.changesAtTick, stateDataWithResults)) - flexStateData.emAgent ! FlexCtrlCompletion( + flexStateData.emAgent ! FlexResult( baseStateData.modelUuid, result.primaryData.toApparentPower, + ) + + flexStateData.emAgent ! FlexCompletion( + baseStateData.modelUuid, flexChangeIndicator.changesAtNextActivation, nextActivation, ) diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala index 812eafbfff..9a946de85e 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala @@ -70,18 +70,18 @@ object FlexibilityMessage { scheduleKey: Option[ScheduleKey] = None, ) extends FlexResponse - /** Message that requests flex options from a flex options provider for given - * tick + /** Message that activates a connected agent, usually in order to requests + * flex options for given tick. During initialization, no flex option + * provision is expected. * * @param tick * The tick to request flex options for */ - final case class RequestFlexOptions(override val tick: Long) - extends FlexRequest + final case class FlexActivation(override val tick: Long) extends FlexRequest /** Message that provides flex options to an * [[edu.ie3.simona.agent.em.EmAgent]] after they have been requested via - * [[RequestFlexOptions]] + * [[FlexActivation]] */ trait ProvideFlexOptions extends FlexResponse @@ -115,9 +115,9 @@ object FlexibilityMessage { final case class IssueNoControl(override val tick: Long) extends IssueFlexControl - /** Message sent by flex options providers indicating that the - * [[IssueFlexControl]] message has been handled and the flex communication - * for the current tick is completed. + /** Message sent by flex options providers that transports the result after + * flex control has been handled. Has to be sent before [[FlexCompletion]], + * but is not required during initialization. * * @param modelUuid * The UUID of the flex options provider asset model @@ -125,18 +125,29 @@ object FlexibilityMessage { * The apparent power that is produced/consumed by the flex options * provider, which can deviate from the set point communicated by a * [[IssueFlexControl]] message if it is not feasible. + */ + final case class FlexResult( + override val modelUuid: UUID, + result: ApparentPower, + ) extends FlexResponse + + /** Message sent by flex options providers indicating that the + * [[IssueFlexControl]] message has been handled and the flex communication + * for the current tick is completed. + * + * @param modelUuid + * The UUID of the flex options provider asset model * @param requestAtNextActivation - * Whether or not to request flex options at the very next activation of - * the receiving EM agent. This is the case if flex options change the very + * Whether to request flex options at the very next activation of the + * receiving EM agent. This is the case if flex options change the very * next second after the current tick. * @param requestAtTick * Optionally the tick at which flex options are foreseen to have changed, * i.e. the tick at which the flex options provider would like to be * activated at the latest. */ - final case class FlexCtrlCompletion( + final case class FlexCompletion( override val modelUuid: UUID, - result: ApparentPower, requestAtNextActivation: Boolean = false, requestAtTick: Option[Long] = None, ) extends FlexResponse diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentSpec.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentSpec.scala index fb06c37e8d..fb550a9a77 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentSpec.scala @@ -15,15 +15,16 @@ import edu.ie3.simona.event.ResultEvent.{ ParticipantResultEvent, } import edu.ie3.simona.event.notifier.NotifierConfig -import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._ -import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions import edu.ie3.simona.ontology.messages.SchedulerMessage.{ Completion, ScheduleActivation, } +import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._ +import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage} import edu.ie3.simona.test.common.input.EmInputTestData import edu.ie3.simona.test.matchers.SquantsMatchers +import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.simona.util.TickUtil.TickLong import edu.ie3.util.TimeUtil import edu.ie3.util.quantities.QuantityMatchers.equalWithTolerance @@ -88,26 +89,53 @@ class EmAgentSpec val pvAgent = TestProbe[FlexRequest]("PvAgent") emAgent ! RegisterParticipant(pvInput.getUuid, pvAgent.ref, pvInput) - emAgent ! ScheduleFlexRequest(pvInput.getUuid, 0) + emAgent ! ScheduleFlexRequest(pvInput.getUuid, INIT_SIM_TICK) val sa1 = scheduler.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 0 + sa1.tick shouldBe INIT_SIM_TICK sa1.unlockKey shouldBe None val emAgentActivation = sa1.actor val evcsAgent = TestProbe[FlexRequest]("EvcsAgent") emAgent ! RegisterParticipant(evcsInput.getUuid, evcsAgent.ref, evcsInput) - emAgent ! ScheduleFlexRequest(evcsInput.getUuid, 0) + emAgent ! ScheduleFlexRequest(evcsInput.getUuid, INIT_SIM_TICK) + + // no additional scheduling message, since tick -1 has already been scheduled + scheduler.expectNoMessage() + + /* TICK -1 */ + emAgentActivation ! Activation(INIT_SIM_TICK) + + // expect flex activations + pvAgent.expectMessage(FlexActivation(INIT_SIM_TICK)) + evcsAgent.expectMessage(FlexActivation(INIT_SIM_TICK)) + + // receive flex completions + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, + requestAtTick = Some(0), + ) - // no additional scheduling message, since tick 0 has already been scheduled scheduler.expectNoMessage() + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, + requestAtTick = Some(0), + ) + + // expect no results for init + resultListener.expectNoMessage() + // expect completion from EmAgent + scheduler.expectMessage( + Completion(emAgentActivation, Some(0)) + ) + /* TICK 0 */ emAgentActivation ! Activation(0) - // expect flex requests - pvAgent.expectMessage(RequestFlexOptions(0)) - evcsAgent.expectMessage(RequestFlexOptions(0)) + // expect flex activations + pvAgent.expectMessage(FlexActivation(0)) + evcsAgent.expectMessage(FlexActivation(0)) // send flex options emAgent ! ProvideMinMaxFlexOptions( @@ -129,9 +157,12 @@ class EmAgentSpec // receive flex control messages pvAgent.expectMessage(IssueNoControl(0)) - emAgent ! FlexCtrlCompletion( + emAgent ! FlexResult( modelUuid = pvInput.getUuid, result = ApparentPower(Kilowatts(-5d), Kilovars(-0.5d)), + ) + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, requestAtTick = Some(600), ) @@ -141,9 +172,12 @@ class EmAgentSpec case IssuePowerControl(0, setPower) => setPower should approximate(Kilowatts(5.0)) } - emAgent ! FlexCtrlCompletion( + emAgent ! FlexResult( modelUuid = evcsInput.getUuid, result = ApparentPower(Kilowatts(5d), Kilovars(0.1d)), + ) + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, requestAtTick = Some(300), ) @@ -169,7 +203,7 @@ class EmAgentSpec // thus 1 does not get activated pvAgent.expectNoMessage() - evcsAgent.expectMessage(RequestFlexOptions(300)) + evcsAgent.expectMessage(FlexActivation(300)) // send flex options again, ev is fully charged emAgent ! ProvideMinMaxFlexOptions( @@ -184,11 +218,11 @@ class EmAgentSpec pvAgent.expectNoMessage() - emAgent ! - FlexCtrlCompletion( - evcsInput.getUuid, - ApparentPower(Kilowatts(0d), Kilovars(0d)), - ) + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(0d), Kilovars(0d)), + ) + emAgent ! FlexCompletion(modelUuid = evcsInput.getUuid) // expect correct results resultListener.expectMessageType[ParticipantResultEvent] match { @@ -236,12 +270,14 @@ class EmAgentSpec // no additional scheduling message, since tick 0 has already been scheduled scheduler.expectNoMessage() + // We skip initialization here for simplicity + /* TICK 0 */ emAgentActivation ! Activation(0) - // expect flex requests - pvAgent.expectMessage(RequestFlexOptions(0)) - evcsAgent.expectMessage(RequestFlexOptions(0)) + // expect flex activations + pvAgent.expectMessage(FlexActivation(0)) + evcsAgent.expectMessage(FlexActivation(0)) // send flex options emAgent ! ProvideMinMaxFlexOptions( @@ -269,17 +305,24 @@ class EmAgentSpec } // send completions - emAgent ! FlexCtrlCompletion( + emAgent ! FlexResult( modelUuid = pvInput.getUuid, result = ApparentPower(Kilowatts(-5d), Kilovars(-0.5d)), + ) + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, requestAtTick = Some(300), ) + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(5d), Kilovars(0.1d)), + ) + scheduler.expectNoMessage() - emAgent ! FlexCtrlCompletion( + emAgent ! FlexCompletion( modelUuid = evcsInput.getUuid, - result = ApparentPower(Kilowatts(5d), Kilovars(0.1d)), requestAtTick = Some(600), ) @@ -298,12 +341,12 @@ class EmAgentSpec /* TICK 300 */ emAgentActivation ! Activation(300) - // expect activations and flex requests. + // expect activations and flex activations. // only pv agent has been scheduled for this tick, // thus evcs does not get activated evcsAgent.expectNoMessage() - pvAgent.expectMessage(RequestFlexOptions(300)) + pvAgent.expectMessage(FlexActivation(300)) // send flex options again, now there's a cloud and thus less feed-in emAgent ! ProvideMinMaxFlexOptions( @@ -316,9 +359,13 @@ class EmAgentSpec // receive flex control messages pvAgent.expectMessage(IssueNoControl(300)) - emAgent ! FlexCtrlCompletion( - pvInput.getUuid, - ApparentPower(Kilowatts(-3d), Kilovars(-0.06d)), + emAgent ! FlexResult( + modelUuid = pvInput.getUuid, + result = ApparentPower(Kilowatts(-3d), Kilovars(-0.06d)), + ) + + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid ) // evcs is now sent control too @@ -329,9 +376,12 @@ class EmAgentSpec scheduler.expectNoMessage() - emAgent ! FlexCtrlCompletion( - evcsInput.getUuid, - ApparentPower(Kilowatts(3d), Kilovars(0.06d)), + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(3d), Kilovars(0.06d)), + ) + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, requestAtTick = Some(800), // should overwrite tick 600 ) @@ -382,12 +432,14 @@ class EmAgentSpec // no additional scheduling message, since tick 0 has already been scheduled scheduler.expectNoMessage() + // We skip initialization here for simplicity + /* TICK 0 */ emAgentActivation ! Activation(0) - // expect flex requests - pvAgent.expectMessage(RequestFlexOptions(0)) - evcsAgent.expectMessage(RequestFlexOptions(0)) + // expect flex activations + pvAgent.expectMessage(FlexActivation(0)) + evcsAgent.expectMessage(FlexActivation(0)) // send flex options emAgent ! ProvideMinMaxFlexOptions( @@ -416,17 +468,24 @@ class EmAgentSpec } // send completions - emAgent ! FlexCtrlCompletion( - pvInput.getUuid, - ApparentPower(Kilowatts(-5d), Kilovars(-0.5d)), + emAgent ! FlexResult( + modelUuid = pvInput.getUuid, + result = ApparentPower(Kilowatts(-5d), Kilovars(-0.5d)), + ) + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, requestAtTick = Some(300), ) + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(5d), Kilovars(0.1d)), + ) + scheduler.expectNoMessage() - emAgent ! FlexCtrlCompletion( - evcsInput.getUuid, - ApparentPower(Kilowatts(5d), Kilovars(0.1d)), + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, requestAtNextActivation = true, // sending ChangingFlexOptions indicator requestAtTick = Some(600), ) @@ -447,10 +506,10 @@ class EmAgentSpec // FLEX OPTIONS - // expect activations and flex requests. + // expect activations and flex activations. // pv is scheduled regularly and evcs at any next tick // thus, we expect activations for both - pvAgent.expectMessage(RequestFlexOptions(300)) + pvAgent.expectMessage(FlexActivation(300)) // send flex options again, now there's a cloud and thus less feed-in emAgent ! ProvideMinMaxFlexOptions( @@ -461,7 +520,7 @@ class EmAgentSpec ) // expecting flex options request, since we asked for it last time - evcsAgent.expectMessage(RequestFlexOptions(300)) + evcsAgent.expectMessage(FlexActivation(300)) emAgent ! ProvideMinMaxFlexOptions( evcsInput.getUuid, @@ -473,9 +532,12 @@ class EmAgentSpec // FLEX CONTROL pvAgent.expectMessage(IssueNoControl(300)) - emAgent ! FlexCtrlCompletion( - pvInput.getUuid, - ApparentPower(Kilowatts(-3d), Kilovars(-0.06d)), + emAgent ! FlexResult( + modelUuid = pvInput.getUuid, + result = ApparentPower(Kilowatts(-3d), Kilovars(-0.06d)), + ) + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid ) evcsAgent.expectMessageType[IssuePowerControl] match { @@ -485,9 +547,12 @@ class EmAgentSpec scheduler.expectNoMessage() - emAgent ! FlexCtrlCompletion( - evcsInput.getUuid, // revoking tick 600 - ApparentPower(Kilowatts(3d), Kilovars(0.06d)), + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(3d), Kilovars(0.06d)), + ) + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid // revoking tick 600 ) // expect correct results @@ -525,7 +590,7 @@ class EmAgentSpec val pvAgent = TestProbe[FlexRequest]("PvAgent") emAgent ! RegisterParticipant(pvInput.getUuid, pvAgent.ref, pvInput) - emAgent ! ScheduleFlexRequest(pvInput.getUuid, 0) + emAgent ! ScheduleFlexRequest(pvInput.getUuid, INIT_SIM_TICK) val emAgentFlex = parentEmAgent.expectMessageType[RegisterParticipant] match { @@ -534,21 +599,53 @@ class EmAgentSpec inputModel shouldBe emInput participant } - parentEmAgent.expectMessage(ScheduleFlexRequest(emInput.getUuid, 0)) + parentEmAgent.expectMessage( + ScheduleFlexRequest(emInput.getUuid, INIT_SIM_TICK) + ) val evcsAgent = TestProbe[FlexRequest]("EvcsAgent") emAgent ! RegisterParticipant(evcsInput.getUuid, evcsAgent.ref, evcsInput) - emAgent ! ScheduleFlexRequest(evcsInput.getUuid, 0) + emAgent ! ScheduleFlexRequest(evcsInput.getUuid, INIT_SIM_TICK) - // no additional scheduling message, since tick 0 has already been scheduled + // no additional scheduling message, since tick -1 has already been scheduled parentEmAgent.expectNoMessage() + /* TICK -1 */ + emAgentFlex ! FlexActivation(INIT_SIM_TICK) + + // expect flex activations + pvAgent.expectMessage(FlexActivation(INIT_SIM_TICK)) + evcsAgent.expectMessage(FlexActivation(INIT_SIM_TICK)) + + // receive flex completions + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, + requestAtTick = Some(0), + ) + + parentEmAgent.expectNoMessage() + + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, + requestAtTick = Some(0), + ) + + // expect no results for init + resultListener.expectNoMessage() + // expect completion from EmAgent + parentEmAgent.expectMessage( + FlexCompletion( + modelUuid = emInput.getUuid, + requestAtTick = Some(0), + ) + ) + /* TICK 0 */ - emAgentFlex ! RequestFlexOptions(0) + emAgentFlex ! FlexActivation(0) // expect activations and flex requests - pvAgent.expectMessage(RequestFlexOptions(0)) - evcsAgent.expectMessage(RequestFlexOptions(0)) + pvAgent.expectMessage(FlexActivation(0)) + evcsAgent.expectMessage(FlexActivation(0)) // send flex options emAgent ! ProvideMinMaxFlexOptions( @@ -597,9 +694,12 @@ class EmAgentSpec // expect issue power control pvAgent.expectMessage(IssueNoControl(0)) - emAgent ! FlexCtrlCompletion( - pvInput.getUuid, - ApparentPower(Kilowatts(-5), Kilovars(-0.5)), + emAgent ! FlexResult( + modelUuid = pvInput.getUuid, + result = ApparentPower(Kilowatts(-5), Kilovars(-0.5)), + ) + emAgent ! FlexCompletion( + modelUuid = pvInput.getUuid, requestAtTick = Some(600), ) @@ -610,9 +710,12 @@ class EmAgentSpec parentEmAgent.expectNoMessage() - emAgent ! FlexCtrlCompletion( - evcsInput.getUuid, - ApparentPower(Kilowatts(11), Kilovars(1.1)), + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(11), Kilovars(1.1)), + ) + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, requestAtTick = Some(300), ) @@ -625,20 +728,20 @@ class EmAgentSpec emResult.getQ should equalWithTolerance(0.0006d.asMegaVar) } - parentEmAgent.expectMessageType[FlexCtrlCompletion] match { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => + parentEmAgent.expectMessageType[FlexResult] match { + case FlexResult(modelUuid, result) => modelUuid shouldBe emInput.getUuid result.p should approximate(Kilowatts(6)) result.q should approximate(Kilovars(0.6)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(300) } + parentEmAgent.expectMessage( + FlexCompletion( + modelUuid = emInput.getUuid, + requestAtTick = Some(300), + ) + ) + /* TICK 150 */ // The mock parent EM now acts as if the situation changed before tick 300, // so that the flex control changes before new flex option calculations are due @@ -657,9 +760,12 @@ class EmAgentSpec parentEmAgent.expectNoMessage() - emAgent ! FlexCtrlCompletion( - evcsInput.getUuid, - ApparentPower(Kilowatts(5.0), Kilovars(0.5)), + emAgent ! FlexResult( + modelUuid = evcsInput.getUuid, + result = ApparentPower(Kilowatts(5.0), Kilovars(0.5)), + ) + emAgent ! FlexCompletion( + modelUuid = evcsInput.getUuid, requestAtTick = Some(700), ) @@ -672,19 +778,18 @@ class EmAgentSpec emResult.getQ should equalWithTolerance(0d.asMegaVar) } - parentEmAgent.expectMessageType[FlexCtrlCompletion] match { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => + parentEmAgent.expectMessageType[FlexResult] match { + case FlexResult(modelUuid, result) => modelUuid shouldBe emInput.getUuid result.p should approximate(Kilowatts(0)) result.q should approximate(Kilovars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(600) } + parentEmAgent.expectMessage( + FlexCompletion( + modelUuid = emInput.getUuid, + requestAtTick = Some(600), + ) + ) } } diff --git a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala index 3e3a9c7a2c..06c3e797c0 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala @@ -1277,7 +1277,7 @@ class EvcsAgentModelCalculationSpec - currently no cars */ - emAgent.send(evcsAgent, RequestFlexOptions(0)) + emAgent.send(evcsAgent, FlexActivation(0)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1308,19 +1308,17 @@ class EvcsAgentModelCalculationSpec // next potential activation at fully charged battery: // net power = 12.961kW * 0.92 = 11.92412kW // time to charge fully ~= 16.7727262054h = 60382 ticks (rounded) - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(0)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(900) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(0)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtTick = Some(900), + ) + ) // results arrive after next activation resultListener.expectNoMessage() @@ -1342,7 +1340,7 @@ class EvcsAgentModelCalculationSpec ), ) - emAgent.send(evcsAgent, RequestFlexOptions(900)) + emAgent.send(evcsAgent, FlexActivation(900)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1368,28 +1366,27 @@ class EvcsAgentModelCalculationSpec emAgent.send(evcsAgent, IssueNoControl(900)) // at 4500 ev is departing - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate( - Kilowatts( - ev900 - .unwrap() - .getSRatedAC - .to(PowerSystemUnits.KILOWATT) - .getValue - .doubleValue - ) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate( + Kilowatts( + ev900 + .unwrap() + .getSRatedAC + .to(PowerSystemUnits.KILOWATT) + .getValue + .doubleValue ) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(4500) + ) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(4500), + ) + ) // result of tick 0 resultListener.expectMsgPF() { @@ -1460,7 +1457,7 @@ class EvcsAgentModelCalculationSpec ), ) - emAgent.send(evcsAgent, RequestFlexOptions(4500)) + emAgent.send(evcsAgent, FlexActivation(4500)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1488,19 +1485,18 @@ class EvcsAgentModelCalculationSpec // we currently have an empty battery in ev4500 // time to charge to minimal soc ~= 1.45454545455h = 5236 ticks (rounded) from now // current tick is 4500, thus: 4500 + 5236 = 9736 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(11)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(9736) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(11)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(9736), + ) + ) // already sent out after EV departed resultListener.expectNoMessage() @@ -1511,7 +1507,7 @@ class EvcsAgentModelCalculationSpec */ // sending flex request at very next activated tick - emAgent.send(evcsAgent, RequestFlexOptions(9736)) + emAgent.send(evcsAgent, FlexActivation(9736)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1541,20 +1537,18 @@ class EvcsAgentModelCalculationSpec // ev4500 is now at 16 kWh // time to charge fully = 6.4 h = 23040 ticks from now // current tick is 9736, thus: 9736 + 23040 = 32776 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(10)) - result.q should approximate(Megavars(0)) - // since battery is still below lowest soc, it's still considered empty - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(11700) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(10)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(11700), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: EvResult) => @@ -1593,7 +1587,7 @@ class EvcsAgentModelCalculationSpec ), ) - emAgent.send(evcsAgent, RequestFlexOptions(11700)) + emAgent.send(evcsAgent, FlexActivation(11700)) val combinedChargingPower = ev11700.unwrap().getSRatedAC.add(ev4500.unwrap().getSRatedAC) @@ -1633,20 +1627,18 @@ class EvcsAgentModelCalculationSpec // ev4500: time to charge fully ~= 7.3180556 h = 26345 ticks from now // ev11700: time to charge fully = 5.8 h = 20880 ticks from now // current tick is 11700, thus: 11700 + 20880 = 32580 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(16)) - result.q should approximate(Megavars(0)) - // since battery is still below lowest soc, it's still considered empty - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(32580) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(16)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(32580), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: EvResult) => @@ -1671,7 +1663,7 @@ class EvcsAgentModelCalculationSpec */ // sending flex request at very next activated tick - emAgent.send(evcsAgent, RequestFlexOptions(18000)) + emAgent.send(evcsAgent, FlexActivation(18000)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1707,19 +1699,17 @@ class EvcsAgentModelCalculationSpec // ev4500: time to discharge to lowest soc ~= 1.9455556 h = 7004 ticks from now // ev11700: time to discharge to lowest soc ~= 1.4 h = 5040 ticks from now // current tick is 18000, thus: 18000 + 5040 = 23040 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(-20)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(23040) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(-20)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtTick = Some(23040), + ) + ) Range(0, 2) .map { _ => @@ -1753,7 +1743,7 @@ class EvcsAgentModelCalculationSpec - discharging with 10 kW */ - emAgent.send(evcsAgent, RequestFlexOptions(23040)) + emAgent.send(evcsAgent, FlexActivation(23040)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1791,19 +1781,17 @@ class EvcsAgentModelCalculationSpec // ev4500 is now at 21.455556 kWh, ev11700 at 11.6 kWh (lowest soc) // ev4500: time to discharge to lowest soc = 0.5455556 h = 1964 ticks from now // current tick is 18864, thus: 23040 + 1964 = 25004 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(-10)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(25004) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(-10)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtTick = Some(25004), + ) + ) Range(0, 2) .map { _ => @@ -1837,7 +1825,7 @@ class EvcsAgentModelCalculationSpec - no power */ - emAgent.send(evcsAgent, RequestFlexOptions(25004)) + emAgent.send(evcsAgent, FlexActivation(25004)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1866,19 +1854,16 @@ class EvcsAgentModelCalculationSpec evService.expectNoMessage() // no new activation - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(0)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe None + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(0)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid + ) + ) Range(0, 2) .map { _ => @@ -1955,7 +1940,7 @@ class EvcsAgentModelCalculationSpec } // sending flex request at very next activated tick - emAgent.send(evcsAgent, RequestFlexOptions(36000)) + emAgent.send(evcsAgent, FlexActivation(36000)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -1984,20 +1969,18 @@ class EvcsAgentModelCalculationSpec // ev11700: time to charge fully = 16 h = 57600 ticks from now // current tick is 36000, thus: 36000 + 57600 = 93600 // BUT: departing tick 72000 is earlier - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe evcsInputModelQv.getUuid - result.p should approximate(Kilowatts(4)) - result.q should approximate(Megavars(0)) - // since we're starting from lowest again - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(72000) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe evcsInputModelQv.getUuid + result.p should approximate(Kilowatts(4)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = evcsInputModelQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(72000), + ) + ) // expect no more messages after completion of initialization scheduler.expectNoMessage() diff --git a/src/test/scala/edu/ie3/simona/agent/participant/StorageAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/StorageAgentModelCalculationSpec.scala index d7c8378ce5..06035ed30d 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/StorageAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/StorageAgentModelCalculationSpec.scala @@ -328,7 +328,7 @@ class StorageAgentModelCalculationSpec - expecting changing flex options indicator (charging from empty) */ - emAgent.send(storageAgent, RequestFlexOptions(0)) + emAgent.send(storageAgent, FlexActivation(0)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -362,19 +362,18 @@ class StorageAgentModelCalculationSpec // next potential activation at fully charged battery: // net power = 12.961kW * 0.92 = 11.92412kW // time to charge fully ~= 16.7727262054h = 60382 ticks (rounded) - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(pMax) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(60382) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(pMax) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(60382), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) => @@ -391,7 +390,7 @@ class StorageAgentModelCalculationSpec */ // Re-request flex options, since we've been asked to - emAgent.send(storageAgent, RequestFlexOptions(28800)) + emAgent.send(storageAgent, FlexActivation(28800)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -422,19 +421,17 @@ class StorageAgentModelCalculationSpec // net power = 9kW * 0.92 = 8.28kW // time to charge fully ~= 12.6337004831h = 45481 ticks (rounded) from now // current tick is 28800, thus: 28800 + 45481 = 74281 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(Kilowatts(9)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(74281) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(Kilowatts(9)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid, + requestAtTick = Some(74281), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) => @@ -464,19 +461,17 @@ class StorageAgentModelCalculationSpec // net power = -12.961kW / 0.92 = -14.08804348kW // time to discharge until lowest energy (0 kWh) ~= 7.946664856h = 28608 ticks (rounded) from now // current tick is 36000, thus: 36000 + 28608 = 64608 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(pMax * -1) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(64608) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(pMax * -1) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid, + requestAtTick = Some(64608), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) => @@ -500,19 +495,17 @@ class StorageAgentModelCalculationSpec // net power = 12 * 0.92 = 11.04 kW // time to charge until full ~= 10.52745715h = 37899 ticks (rounded) from now // current tick is 43200, thus: 43200 + 37899 = 81099 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(Kilowatts(12)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe Some(81099) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(Kilowatts(12)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid, + requestAtTick = Some(81099), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) => @@ -529,7 +522,7 @@ class StorageAgentModelCalculationSpec */ // Request flex options - emAgent.send(storageAgent, RequestFlexOptions(81099)) + emAgent.send(storageAgent, FlexActivation(81099)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -560,19 +553,18 @@ class StorageAgentModelCalculationSpec // net power = -12 / 0.92 = -13.04347826 kW // time to discharge until empty ~= 15.33333333h = 55200 ticks from now // current tick is 79688, thus: 81099 + 55200 = 136299 - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(Kilowatts(-12)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(136299) + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(Kilowatts(-12)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid, + requestAtNextActivation = true, + requestAtTick = Some(136299), + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) => @@ -589,7 +581,7 @@ class StorageAgentModelCalculationSpec */ // Request flex options - emAgent.send(storageAgent, RequestFlexOptions(136299)) + emAgent.send(storageAgent, FlexActivation(136299)) emAgent.expectMsgType[ProvideFlexOptions] match { case ProvideMinMaxFlexOptions( @@ -615,19 +607,16 @@ class StorageAgentModelCalculationSpec emAgent.send(storageAgent, IssuePowerControl(136299, Kilowatts(0d))) // we're not charging or discharging, no new expected tick - emAgent.expectMsgPF() { - case FlexCtrlCompletion( - modelUuid, - result, - requestAtNextActivation, - requestAtTick, - ) => - modelUuid shouldBe storageInputQv.getUuid - result.p should approximate(Kilowatts(0)) - result.q should approximate(Megavars(0)) - requestAtNextActivation shouldBe false - requestAtTick shouldBe None + emAgent.expectMsgPF() { case FlexResult(modelUuid, result) => + modelUuid shouldBe storageInputQv.getUuid + result.p should approximate(Kilowatts(0)) + result.q should approximate(Megavars(0)) } + emAgent.expectMsg( + FlexCompletion( + modelUuid = storageInputQv.getUuid + ) + ) resultListener.expectMsgPF() { case ParticipantResultEvent(result: StorageResult) =>