From 13e0da66541a8ff94a52403095738dd5e54a0dda Mon Sep 17 00:00:00 2001 From: Vicky Bung Date: Tue, 9 May 2023 10:48:49 +0200 Subject: [PATCH 01/39] Changed implementation of actor naming for unique name generation --- CHANGELOG.md | 1 + .../scala/edu/ie3/simona/actor/SimonaActorNaming.scala | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cafa9fff6c..61fed7cda9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Speeding up additionalActivationTicks in participant's BaseStateData [#421](https://github.com/ie3-institute/simona/pull/421) - Changed format of example grid `vn_simona` [#216](https://github.com/ie3-institute/simona/issues/216) - Renamed ChpData to ChpRelevantData [#494](https://github.com/ie3-institute/simona/issues/494) +- Changed implementation of actor naming for unique name generation [#103](https://github.com/ie3-institute/simona/issues/103) ### Fixed - Location of `vn_simona` test grid (was partially in Berlin and Dortmund) [#72](https://github.com/ie3-institute/simona/issues/72) diff --git a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala index acf4d2d0a0..6a0d828e52 100644 --- a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala +++ b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala @@ -29,8 +29,12 @@ object SimonaActorNaming { * @return * a shortened uuid string */ - private def simonaActorUuid: String = - UUID.randomUUID().toString.substring(0, 5) + private def simonaActorUuid: String = { + val uuid = UUID.randomUUID().toString.substring(0, 5) + val timestamp = System.currentTimeMillis() + val finalUuid = s"$uuid-$timestamp" + finalUuid.substring(0, 13) + } /** Constructs an actor name based on the simona convention for actor names. * The provided combination of class and id has to be unique for the whole From bc9355fd2ee9f36048a6eea2d23b4bc0c757bd4a Mon Sep 17 00:00:00 2001 From: Vicky Bung Date: Tue, 9 May 2023 10:53:22 +0200 Subject: [PATCH 02/39] Changed implementation of actor naming for unique name generation --- src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala index 6a0d828e52..6070cd6510 100644 --- a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala +++ b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala @@ -31,9 +31,9 @@ object SimonaActorNaming { */ private def simonaActorUuid: String = { val uuid = UUID.randomUUID().toString.substring(0, 5) - val timestamp = System.currentTimeMillis() + val timestamp = System.nanoTime() val finalUuid = s"$uuid-$timestamp" - finalUuid.substring(0, 13) + finalUuid.substring(0, 15) } /** Constructs an actor name based on the simona convention for actor names. From 8b973adb2c4cd81503e146f76f08300d207cc733 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Mar 2024 16:47:31 +0100 Subject: [PATCH 03/39] Providing detailed information about next tick per agent --- CHANGELOG.md | 1 + .../agent/participant/ParticipantAgent.scala | 93 +++++++++++++----- .../ParticipantAgentFundamentals.scala | 30 ------ .../flex/MinMaxFlexibilityMessage.scala | 6 ++ .../messages/services/EvMessage.scala | 4 +- .../messages/services/ServiceMessage.scala | 17 +++- .../simona/service/ev/ExtEvDataService.scala | 94 +++++++++++++------ 7 files changed, 162 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33813e38c3..42efb5c00b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Converting `SimonaSim` to pekko typed/terminating SimonSim when initialization fails [#210](https://github.com/ie3-institute/simona/issues/210) - Converting the `GridAgent` and the `DBFSAlgorithm` to `pekko typed` [#666](https://github.com/ie3-institute/simona/issues/666) - Validation of grid will throw exception instead of just logging errors [#463](https://github.com/ie3-institute/simona/issues/463) +- External simulation should provide detailed information about next tick per agent [#776](https://github.com/ie3-institute/simona/issues/776) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) 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 a01c5677a1..e573ac7949 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala @@ -50,12 +50,14 @@ import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{ FlexResponse, IssueFlexControl, RequestFlexOptions, + ScheduleFlexRequest, } import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ PrimaryServiceRegistrationMessage, ProvisionMessage, RegistrationResponseMessage, + ScheduleProvisionMessage, } import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.util.scala.quantities.ReactivePower @@ -193,6 +195,14 @@ abstract class ParticipantAgent[ fromOutsideBaseStateData, ) + case Event( + msg: ScheduleProvisionMessage, + baseStateData: BaseStateData[PD], + ) => + val updatedStateData = handleProvisionScheduling(msg, baseStateData) + + stay() using updatedStateData + case Event( msg: ProvisionMessage[Data], baseStateData: BaseStateData[PD], @@ -347,6 +357,18 @@ abstract class ParticipantAgent[ scheduler, )(stateData.baseStateData.outputConfig) + case Event( + msg: ScheduleProvisionMessage, + stateData @ DataCollectionStateData( + baseStateData: BaseStateData[PD], + _, + _, + ), + ) => + val updatedStateData = handleProvisionScheduling(msg, baseStateData) + + stay() using stateData.copy(baseStateData = updatedStateData) + case Event( msg: ProvisionMessage[_], stateData @ DataCollectionStateData( @@ -355,28 +377,13 @@ abstract class ParticipantAgent[ isYetTriggered, ), ) => - /* We yet have received at least one data provision message. Handle all messages, that follow up for this tick, by - * adding the received data to the collection state data and checking, if everything is at its place */ - val unexpectedSender = baseStateData.foreseenDataTicks.exists { - case (ref, None) => msg.serviceRef == ref - case _ => false - } - - if (data.contains(msg.serviceRef) || unexpectedSender) { + if (data.contains(msg.serviceRef)) { /* Update the yet received information */ val updatedData = data + (msg.serviceRef -> Some(msg.data)) - /* If we have received unexpected data, we also have not been scheduled before */ - if (unexpectedSender) - scheduler ! ScheduleActivation( - self.toTyped, - msg.tick, - msg.unlockKey, - ) - /* Depending on if a next data tick can be foreseen, either update the entry in the base state data or remove * it */ - val foreSeenDataTicks = + val foreseenDataTicks = baseStateData.foreseenDataTicks + (msg.serviceRef -> msg.nextDataTick) val updatedBaseStateData = BaseStateData.updateBaseStateData( baseStateData, @@ -384,7 +391,7 @@ abstract class ParticipantAgent[ baseStateData.requestValueStore, baseStateData.voltageValueStore, baseStateData.additionalActivationTicks, - foreSeenDataTicks, + foreseenDataTicks, ) val updatedStateData: DataCollectionStateData[PD] = stateData .copy( @@ -611,16 +618,13 @@ abstract class ParticipantAgent[ actorRef -> None } - val unforeseenPossible = - baseStateData.foreseenDataTicks.exists(_._2.isEmpty) - val nextStateData = DataCollectionStateData( baseStateData, expectedSenders, yetTriggered = true, ) - if (expectedSenders.nonEmpty || unforeseenPossible) { + if (expectedSenders.nonEmpty) { /* Do await provision messages in HandleInformation */ goto(HandleInformation) using nextStateData } else { @@ -646,6 +650,51 @@ abstract class ParticipantAgent[ .getOrElse(createInitialState(baseStateData)) } + private def handleProvisionScheduling( + msg: ScheduleProvisionMessage, + baseStateData: BaseStateData[PD], + ): BaseStateData[PD] = { + val foreseenDataTicks = + baseStateData.foreseenDataTicks + (msg.serviceRef -> Some(msg.tick)) + + val updatedStateData = BaseStateData.updateBaseStateData( + baseStateData, + baseStateData.resultValueStore, + baseStateData.requestValueStore, + baseStateData.voltageValueStore, + baseStateData.additionalActivationTicks, + foreseenDataTicks, + ) + + updatedStateData match { + case modelStateData: ParticipantModelBaseStateData[_, _, _, _] => + val maybeEmAgent = modelStateData.flexStateData.map(_.emAgent) + + maybeEmAgent match { + case Some(emAgent) => + emAgent ! ScheduleFlexRequest( + modelStateData.model.getUuid, + msg.tick, + Some(msg.unlockKey), + ) + case None => + scheduler ! ScheduleActivation( + self.toTyped, + msg.tick, + Some(msg.unlockKey), + ) + } + case _ => + scheduler ! ScheduleActivation( + self.toTyped, + msg.tick, + Some(msg.unlockKey), + ) + } + + updatedStateData + } + /** Handle an incoming data provision message in Idle, try to figure out who's * about to send information in this tick as well. Map together all senders * with their yet apparent information. 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 2ec46f6450..277b455939 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -514,36 +514,6 @@ protected trait ParticipantAgentFundamentals[ } } - val unexpectedSender = baseStateData.foreseenDataTicks.exists { - case (ref, None) => msg.serviceRef == ref - case _ => false - } - - /* If we have received unexpected data, we also have not been scheduled before */ - if (unexpectedSender) { - baseStateData match { - case modelStateData: ParticipantModelBaseStateData[_, _, _, _] => - val maybeEmAgent = modelStateData.flexStateData.map(_.emAgent) - - maybeEmAgent match { - case Some(emAgent) => - emAgent ! ScheduleFlexRequest( - modelStateData.model.getUuid, - msg.tick, - msg.unlockKey, - ) - case None => - scheduler ! ScheduleActivation( - self.toTyped, - msg.tick, - msg.unlockKey, - ) - } - case _ => - false - } - } - /* If the sender announces a new next tick, add it to the list of expected ticks, else remove the current entry */ val foreseenDataTicks = baseStateData.foreseenDataTicks + (msg.serviceRef -> msg.nextDataTick) diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala index af72bf52c6..986cc3afe4 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala @@ -114,4 +114,10 @@ object MinMaxFlexibilityMessage { ): ProvideMinMaxFlexOptions = ProvideMinMaxFlexOptions(modelUuid, power, power, power) } + + /** Indicates that flex options have not changed since last provision + * @param modelUuid + */ + case class FlexOptionsUnchanged(override val modelUuid: UUID) + extends ProvideFlexOptions } diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala index 05a5be6886..181d58d8d7 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala @@ -48,7 +48,7 @@ object EvMessage { override val tick: Long, override val serviceRef: ActorRef, override val data: EvData, - override val nextDataTick: Option[Long] = None, + override val nextDataTick: Option[Long], override val unlockKey: Option[ScheduleKey] = None, ) extends EvMessage with ProvisionMessage[EvData] @@ -72,7 +72,7 @@ object EvMessage { /** Holds arrivals for one charging station * * @param arrivals - * EVs arriving at the charging station + * EVs arriving at the charging station, which might be empty */ final case class ArrivingEvsData( arrivals: Seq[EvModelWrapper] diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala index 33946b669f..6cf02c0f2d 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala @@ -17,7 +17,7 @@ import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey */ sealed trait ServiceMessage -case object ServiceMessage { +object ServiceMessage { /** Message used to register for a service */ @@ -75,7 +75,22 @@ case object ServiceMessage { val tick: Long val serviceRef: ActorRef val data: D + + /** Next tick at which data could arrive. If None, no data is expected for + * the rest of the simulation + */ val nextDataTick: Option[Long] val unlockKey: Option[ScheduleKey] } + + /** @param serviceRef + * @param tick + * @param unlockKey + */ + case class ScheduleProvisionMessage( + serviceRef: ActorRef, + tick: Long, + unlockKey: ScheduleKey, + ) extends ServiceMessage + } diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index c827cb52fb..a0c5cb219f 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -6,18 +6,23 @@ package edu.ie3.simona.service.ev -import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps -import org.apache.pekko.actor.{ActorContext, ActorRef, Props} import edu.ie3.simona.api.data.ev.ExtEvData import edu.ie3.simona.api.data.ev.model.EvModel import edu.ie3.simona.api.data.ev.ontology._ import edu.ie3.simona.api.data.ontology.DataMessageFromExt import edu.ie3.simona.exceptions.WeatherServiceException.InvalidRegistrationRequestException -import edu.ie3.simona.exceptions.{InitializationException, ServiceException} +import edu.ie3.simona.exceptions.{ + CriticalFailureException, + InitializationException, + ServiceException, +} import edu.ie3.simona.model.participant.evcs.EvModelWrapper import edu.ie3.simona.ontology.messages.services.EvMessage._ import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage -import edu.ie3.simona.ontology.messages.services.ServiceMessage.ServiceRegistrationMessage +import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ + ScheduleProvisionMessage, + ServiceRegistrationMessage, +} import edu.ie3.simona.scheduler.ScheduleLock import edu.ie3.simona.service.ServiceStateData.{ InitializeServiceStateData, @@ -29,6 +34,9 @@ import edu.ie3.simona.service.ev.ExtEvDataService.{ } import edu.ie3.simona.service.{ExtDataSupport, ServiceStateData, SimonaService} import edu.ie3.simona.util.ReceiveDataMap +import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK +import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps +import org.apache.pekko.actor.{ActorContext, ActorRef, Props} import java.util.UUID import scala.jdk.CollectionConverters._ @@ -142,8 +150,6 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData.uuidToActorRef.get(evcs) match { case None => // Actor is not registered yet - agentToBeRegistered ! RegistrationSuccessfulMessage(self, None) - serviceStateData.copy( uuidToActorRef = serviceStateData.uuidToActorRef + (evcs -> agentToBeRegistered) @@ -185,7 +191,10 @@ class ExtEvDataService(override val scheduler: ActorRef) case departingEvsRequest: RequestDepartingEvs => requestDepartingEvs(tick, departingEvsRequest.departures) case arrivingEvsProvision: ProvideArrivingEvs => - handleArrivingEvs(tick, arrivingEvsProvision.arrivals)( + handleArrivingEvs( + tick, + arrivingEvsProvision.arrivals, + )( serviceStateData, ctx, ) @@ -263,33 +272,62 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData: ExtEvStateData, ctx: ActorContext, ): (ExtEvStateData, Option[Long]) = { - val actorToEvs = allArrivingEvs.asScala.flatMap { - case (evcs, arrivingEvs) => - serviceStateData.uuidToActorRef - .get(evcs) - .map((_, arrivingEvs.asScala.map(EvModelWrapper.apply).toSeq)) - .orElse { - log.warning( - "A corresponding actor ref for UUID {} could not be found", - evcs, - ) - None - } - } - if (actorToEvs.nonEmpty) { - val keys = - ScheduleLock.multiKey(ctx, scheduler.toTyped, tick, actorToEvs.size) + val arrivingEvs = allArrivingEvs.asScala - actorToEvs.zip(keys).foreach { case ((actor, arrivingEvs), key) => - actor ! ProvideEvDataMessage( - tick, + val keys = ScheduleLock.multiKey( + ctx, + scheduler.toTyped, + tick, + serviceStateData.uuidToActorRef.size, + ) + + if (tick == INIT_SIM_TICK) { + serviceStateData.uuidToActorRef.foreach { case (uuid, actor) => + + val firstTick: Option[Long] = arrivingEvs + .getOrElse( + uuid, + throw new CriticalFailureException( + s"No initial message found for EVCS $actor($uuid)" + ), + ) + .nextTick + + actor ! RegistrationSuccessfulMessage( self, - ArrivingEvsData(arrivingEvs), - unlockKey = Some(key), + firstTick, ) } + } else { + val actorToEvs = serviceStateData.uuidToActorRef.map { + case (uuid, actor) => + actor -> arrivingEvs + .get(uuid) + .map(_.asScala.map(EvModelWrapper.apply).toSeq) + .getOrElse(Seq.empty) + } + if (actorToEvs.nonEmpty) { + + actorToEvs.zip(keys).foreach { case ((actor, arrivingEvs), key) => + if (arrivingEvs.nonEmpty) + actor ! ProvideEvDataMessage( + tick, + self, + ArrivingEvsData(arrivingEvs), + Some(arrivingEvs.nextTick), + unlockKey = Some(key), + ) + else + actor ! ScheduleProvisionMessage( + self, + tick, + unlockKey = key, + ) + } + + } } ( From 7c482ea75965482f6700c015160fe875ce336bae Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Mar 2024 16:52:11 +0100 Subject: [PATCH 04/39] Removing unused scheduleKey --- .../agent/participant/ParticipantAgentFundamentals.scala | 5 +---- .../ie3/simona/ontology/messages/services/EvMessage.scala | 2 -- .../ontology/messages/services/PrimaryDataMessage.scala | 2 -- .../simona/ontology/messages/services/ServiceMessage.scala | 1 - .../simona/ontology/messages/services/WeatherMessage.scala | 2 -- .../scala/edu/ie3/simona/service/ev/ExtEvDataService.scala | 2 -- .../ie3/simona/service/primary/PrimaryServiceWorker.scala | 2 -- 7 files changed, 1 insertion(+), 15 deletions(-) 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 277b455939..ff060dd980 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -72,10 +72,7 @@ import edu.ie3.simona.ontology.messages.PowerMessage.{ AssetPowerChangedMessage, AssetPowerUnchangedMessage, } -import edu.ie3.simona.ontology.messages.SchedulerMessage.{ - Completion, - ScheduleActivation, -} +import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._ import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala index 181d58d8d7..1b7a714514 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala @@ -12,7 +12,6 @@ import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ ProvisionMessage, ServiceRegistrationMessage, } -import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey import org.apache.pekko.actor.ActorRef import java.util.UUID @@ -49,7 +48,6 @@ object EvMessage { override val serviceRef: ActorRef, override val data: EvData, override val nextDataTick: Option[Long], - override val unlockKey: Option[ScheduleKey] = None, ) extends EvMessage with ProvisionMessage[EvData] diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala index 73aa482b64..4721af5f2f 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala @@ -8,7 +8,6 @@ package edu.ie3.simona.ontology.messages.services import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower import edu.ie3.simona.ontology.messages.services.ServiceMessage.ProvisionMessage -import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey import org.apache.pekko.actor.ActorRef sealed trait PrimaryDataMessage @@ -30,7 +29,6 @@ object PrimaryDataMessage { override val serviceRef: ActorRef, override val data: ApparentPower, override val nextDataTick: Option[Long], - override val unlockKey: Option[ScheduleKey] = None, ) extends ProvisionMessage[ApparentPower] with PrimaryDataMessage } diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala index 6cf02c0f2d..eed3832f63 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala @@ -80,7 +80,6 @@ object ServiceMessage { * the rest of the simulation */ val nextDataTick: Option[Long] - val unlockKey: Option[ScheduleKey] } /** @param serviceRef diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala index 9d17284d63..cb9e8349ba 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala @@ -11,7 +11,6 @@ import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ ProvisionMessage, ServiceRegistrationMessage, } -import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey import edu.ie3.util.scala.quantities.Irradiance import org.apache.pekko.actor.ActorRef import squants.{Temperature, Velocity} @@ -54,7 +53,6 @@ object WeatherMessage { override val serviceRef: ActorRef, override val data: WeatherData, override val nextDataTick: Option[Long], - override val unlockKey: Option[ScheduleKey] = None, ) extends WeatherMessage with ProvisionMessage[WeatherData] diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index a0c5cb219f..0cfe0c47b2 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -284,7 +284,6 @@ class ExtEvDataService(override val scheduler: ActorRef) if (tick == INIT_SIM_TICK) { serviceStateData.uuidToActorRef.foreach { case (uuid, actor) => - val firstTick: Option[Long] = arrivingEvs .getOrElse( uuid, @@ -317,7 +316,6 @@ class ExtEvDataService(override val scheduler: ActorRef) self, ArrivingEvsData(arrivingEvs), Some(arrivingEvs.nextTick), - unlockKey = Some(key), ) else actor ! ScheduleProvisionMessage( diff --git a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala index b37c061d39..7ff05ccf1a 100644 --- a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala +++ b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala @@ -22,7 +22,6 @@ import edu.ie3.simona.exceptions.InitializationException import edu.ie3.simona.exceptions.WeatherServiceException.InvalidRegistrationRequestException import edu.ie3.simona.ontology.messages.services.ServiceMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage -import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey import edu.ie3.simona.service.ServiceStateData.{ InitializeServiceStateData, ServiceActivationBaseStateData, @@ -437,6 +436,5 @@ object PrimaryServiceWorker { override val serviceRef: ActorRef, override val data: PrimaryData, override val nextDataTick: Option[Long], - override val unlockKey: Option[ScheduleKey] = None, ) extends ServiceMessage.ProvisionMessage[PrimaryData] } From 3cd05a83de2741cf4f16c2bcabfc5cbadf4c83a8 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Mar 2024 17:08:09 +0100 Subject: [PATCH 05/39] Towards a functioning ExtEvDataService... --- .../simona/service/ev/ExtEvDataService.scala | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index 0cfe0c47b2..42fade723a 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -273,25 +273,25 @@ class ExtEvDataService(override val scheduler: ActorRef) ctx: ActorContext, ): (ExtEvStateData, Option[Long]) = { - val arrivingEvs = allArrivingEvs.asScala - - val keys = ScheduleLock.multiKey( - ctx, - scheduler.toTyped, - tick, - serviceStateData.uuidToActorRef.size, - ) + val allArrivingEvsData = allArrivingEvs.asScala if (tick == INIT_SIM_TICK) { + val keys = ScheduleLock.multiKey( + ctx, + scheduler.toTyped, + tick, + serviceStateData.uuidToActorRef.size, + ) + serviceStateData.uuidToActorRef.foreach { case (uuid, actor) => - val firstTick: Option[Long] = arrivingEvs + val firstTick: Option[Long] = allArrivingEvsData .getOrElse( uuid, throw new CriticalFailureException( s"No initial message found for EVCS $actor($uuid)" ), ) - .nextTick + .maybeNextTick actor ! RegistrationSuccessfulMessage( self, @@ -299,33 +299,38 @@ class ExtEvDataService(override val scheduler: ActorRef) ) } } else { - val actorToEvs = serviceStateData.uuidToActorRef.map { - case (uuid, actor) => - actor -> arrivingEvs - .get(uuid) - .map(_.asScala.map(EvModelWrapper.apply).toSeq) - .getOrElse(Seq.empty) - } - - if (actorToEvs.nonEmpty) { - actorToEvs.zip(keys).foreach { case ((actor, arrivingEvs), key) => - if (arrivingEvs.nonEmpty) - actor ! ProvideEvDataMessage( - tick, - self, - ArrivingEvsData(arrivingEvs), - Some(arrivingEvs.nextTick), - ) - else - actor ! ScheduleProvisionMessage( - self, - tick, - unlockKey = key, - ) + val actorToEvsData = allArrivingEvsData + .flatMap { case (evcs, arrivingEvsData) => + serviceStateData.uuidToActorRef + .get(evcs) + .map((_, arrivingEvsData)) + .orElse { + log.warning( + "A corresponding actor ref for UUID {} could not be found", + evcs, + ) + None + } } - + .partitionMap() // TODO + + actorToEvsData.zip(keys).foreach { case ((actor, arrivingEvsData), key) => + if (arrivingEvsData.arrivals.nonEmpty) + actor ! ProvideEvDataMessage( + tick, + self, + arrivingEvsData.arrivals, + Some(arrivingEvsData.maybeNextTick), + ) + else + actor ! ScheduleProvisionMessage( + self, + tick, + unlockKey = key, + ) } + } ( From 6fc8187572f85858770739b4d5082776879e9bb0 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Mar 2024 19:49:58 +0100 Subject: [PATCH 06/39] Removing PhaseSwitch implementation and utilization --- .../scheduler/core/PhaseSwitchCore.scala | 160 ------------- .../scala/edu/ie3/simona/sim/SimonaSim.scala | 26 +-- .../simona/sim/setup/ExtSimSetupData.scala | 3 - .../ie3/simona/sim/setup/SimonaSetup.scala | 6 +- .../sim/setup/SimonaStandaloneSetup.scala | 14 +- .../immutable/PrioritySwitchBiSet.scala | 213 ------------------ .../edu/ie3/simona/sim/SimonaSimSpec.scala | 4 +- .../simona/sim/setup/SimonaSetupSpec.scala | 2 +- .../immutable/PrioritySwitchBiSetSpec.scala | 174 -------------- 9 files changed, 20 insertions(+), 582 deletions(-) delete mode 100644 src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala delete mode 100644 src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala delete mode 100644 src/test/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSetSpec.scala diff --git a/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala b/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala deleted file mode 100644 index eff9128068..0000000000 --- a/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala +++ /dev/null @@ -1,160 +0,0 @@ -/* - * © 2023. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.scheduler.core - -import edu.ie3.simona.exceptions.CriticalFailureException -import edu.ie3.simona.scheduler.core.Core.{ - ActiveCore, - Actor, - CoreFactory, - InactiveCore, -} -import edu.ie3.util.scala.collection.immutable.PrioritySwitchBiSet - -/** A scheduler core that activates actors in phases when active. This means - * that only one actor at any given time is activated and the completion of - * said actor is necessary before activating other actors scheduled for the - * same tick. - * - * When multiple actors are scheduled for the same tick, they are always - * activated in the same order, which is given by the initial scheduling of - * said actors. Thus, if e.g. actor 1 has been scheduled for initialization - * before actor 2, actor 1 will be activated before actor 2 for the - * initialization tick and all consecutive ticks. - */ -object PhaseSwitchCore extends CoreFactory { - - override def create(): PhaseSwitchInactive = - PhaseSwitchInactive(PrioritySwitchBiSet.empty, None) - - final case class PhaseSwitchInactive private ( - private val activationQueue: PrioritySwitchBiSet[Long, Actor], - private val lastActiveTick: Option[Long], - ) extends InactiveCore { - override def activate(newTick: Long): ActiveCore = { - val nextScheduledTick = activationQueue.headKeyOption.getOrElse( - throw new CriticalFailureException( - "No activation scheduled, cannot activate." - ) - ) - - if (nextScheduledTick != newTick) - throw new CriticalFailureException( - s"Cannot activate with new tick $newTick because $nextScheduledTick is the next scheduled tick." - ) - - PhaseSwitchActive(activationQueue, activeTick = newTick) - } - - override def handleSchedule( - actor: Actor, - newTick: Long, - ): (Option[Long], InactiveCore) = { - lastActiveTick.filter(newTick < _).foreach { lastActive => - throw new CriticalFailureException( - s"Cannot schedule an activation for $actor at tick $newTick because the last active tick is $lastActive" - ) - } - - val oldEarliestTick = activationQueue.headKeyOption - - val updatedQueue = activationQueue.set(newTick, actor) - val newEarliestTick = updatedQueue.headKeyOption - - val maybeScheduleTick = - Option - .when(newEarliestTick != oldEarliestTick)(newEarliestTick) - .flatten - - (maybeScheduleTick, copy(activationQueue = updatedQueue)) - } - - } - - private final case class PhaseSwitchActive( - private val activationQueue: PrioritySwitchBiSet[Long, Actor], - activeTick: Long, - private val phase: Int = 0, - private val activeActors: Set[Actor] = Set.empty, - ) extends ActiveCore { - - override def handleCompletion(actor: Actor): ActiveCore = { - if (!activeActors.contains(actor)) - throw new CriticalFailureException( - s"Actor $actor is not part of the expected completing actors" - ) - - copy(activeActors = activeActors.excl(actor)) - } - - override def maybeComplete(): Option[(Option[Long], InactiveCore)] = { - Option.when( - activeActors.isEmpty && activationQueue.headKeyOption.forall( - _ > activeTick - ) - ) { - ( - activationQueue.headKeyOption, - PhaseSwitchInactive(activationQueue, Some(activeTick)), - ) - } - } - - override def handleSchedule( - actor: Actor, - newTick: Long, - ): ActiveCore = { - if (newTick == activeTick) { - // what's done, is done: old phases are completed, - // thus they cannot handle new activation schedulings - if (activationQueue.indexOf(actor).exists(_ < phase)) { - val activeActor = activationQueue.values(phase) - throw new CriticalFailureException( - s"Cannot schedule an activation at tick $newTick for $actor while actor $activeActor is active now" - ) - } - } else { - if (newTick < activeTick) - throw new CriticalFailureException( - s"Cannot schedule an activation at tick $newTick for $actor while tick $activeTick is currently active" - ) - } - - copy(activationQueue = activationQueue.set(newTick, actor)) - } - - override def takeNewActivations(): (Iterable[Actor], ActiveCore) = { - Option - .when(activeActors.isEmpty) { - // only one actor can be active at a time, and only - // if the last actor of the current tick has completed - activationQueue.takeNextValueFor(activeTick) - } - .flatten - .map { case (actor, updatedQueue) => - val newPhase = activationQueue - .indexOf(actor) - .getOrElse( - throw new RuntimeException( - "Actor not scheduled, should not happen" - ) - ) - ( - Iterable.single(actor), - copy( - activationQueue = updatedQueue, - phase = newPhase, - activeActors = activeActors.incl(actor), - ), - ) - } - .getOrElse((Iterable.empty, this)) - } - - } - -} diff --git a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala index 9126926147..c8196aaf30 100644 --- a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala +++ b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala @@ -12,8 +12,7 @@ import edu.ie3.simona.event.RuntimeEvent import edu.ie3.simona.event.listener.{DelayedStopHelper, RuntimeEventListener} import edu.ie3.simona.main.RunSimona.SimonaEnded import edu.ie3.simona.scheduler.TimeAdvancer -import edu.ie3.simona.scheduler.core.PhaseSwitchCore -import edu.ie3.simona.sim.setup.{ExtSimSetupData, SimonaSetup} +import edu.ie3.simona.sim.setup.SimonaSetup import edu.ie3.util.scala.Scope import org.apache.pekko.actor.typed.scaladsl.adapter._ import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors} @@ -77,30 +76,23 @@ object SimonaSim { val timeAdvancer = simonaSetup.timeAdvancer(ctx, ctx.self, runtimeEventListener) - val rootPhaseSwitch = - simonaSetup.scheduler(ctx, timeAdvancer, PhaseSwitchCore) + val scheduler = simonaSetup.scheduler(ctx, timeAdvancer) // External simulations have to be scheduled for initialization first, // so that the phase switch permanently activates them first - val extSimulationData: ExtSimSetupData = - simonaSetup.extSimulations(ctx, rootPhaseSwitch) - - // scheduler for all actors besides external simulation, - // which come second in line with phase switch - val simScheduler = - simonaSetup.scheduler(ctx, rootPhaseSwitch) + val extSimulationData = simonaSetup.extSimulations(ctx, scheduler) /* start services */ // primary service proxy val primaryServiceProxy = - simonaSetup.primaryServiceProxy(ctx, simScheduler) + simonaSetup.primaryServiceProxy(ctx, scheduler) // weather service val weatherService = - simonaSetup.weatherService(ctx, simScheduler) + simonaSetup.weatherService(ctx, scheduler) val environmentRefs = EnvironmentRefs( - simScheduler, + scheduler, runtimeEventListener.toClassic, primaryServiceProxy, weatherService, @@ -116,14 +108,12 @@ object SimonaSim { val otherActors = Iterable[ActorRef[_]]( timeAdvancer, - rootPhaseSwitch, - simScheduler, + scheduler, primaryServiceProxy.toTyped, weatherService.toTyped, ) ++ gridAgents ++ - extSimulationData.extDataServices.values.map(_.toTyped) ++ - extSimulationData.extScheduler.toSeq + extSimulationData.extDataServices.values.map(_.toTyped) /* watch all actors */ resultEventListeners.foreach(ctx.watch) diff --git a/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala b/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala index 40443cad5f..3209fb706d 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala @@ -6,15 +6,12 @@ package edu.ie3.simona.sim.setup -import edu.ie3.simona.ontology.messages.SchedulerMessage import org.apache.pekko.actor.{ActorRef => ClassicRef} import edu.ie3.simona.service.ev.ExtEvDataService -import org.apache.pekko.actor.typed.ActorRef final case class ExtSimSetupData( extSimAdapters: Iterable[ClassicRef], extDataServices: Map[Class[_], ClassicRef], - extScheduler: Option[ActorRef[SchedulerMessage]], ) { def evDataService: Option[ClassicRef] = diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala index f96a144a36..ef2e6f533b 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala @@ -95,14 +95,14 @@ trait SimonaSetup { * * @param context * Actor context to use - * @param rootScheduler - * Actor reference to it's according scheduler to use + * @param scheduler + * Actor reference to the scheduler to use * @return * External simulations and their init data */ def extSimulations( context: ActorContext[_], - rootScheduler: ActorRef[SchedulerMessage], + scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData /** Creates the time advancer diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala index 6788a652f2..1cb3da574e 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala @@ -190,20 +190,19 @@ class SimonaStandaloneSetup( override def extSimulations( context: ActorContext[_], - rootScheduler: ActorRef[SchedulerMessage], + scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData = { val jars = ExtSimLoader.scanInputFolder() val extLinks = jars.flatMap(ExtSimLoader.loadExtLink).toSeq if (extLinks.nonEmpty) { - val extScheduler = scheduler(context, parent = rootScheduler) val (extSimAdapters, extDataServices) = extLinks.zipWithIndex.map { case (extLink, index) => // external simulation always needs at least an ExtSimAdapter val extSimAdapter = context.toClassic.simonaActorOf( - ExtSimAdapter.props(extScheduler.toClassic), + ExtSimAdapter.props(scheduler.toClassic), s"$index", ) val extSimAdapterData = new ExtSimAdapterData(extSimAdapter, args) @@ -211,7 +210,7 @@ class SimonaStandaloneSetup( // send init data right away, init activation is scheduled extSimAdapter ! ExtSimAdapter.Create( extSimAdapterData, - ScheduleLock.singleKey(context, extScheduler, INIT_SIM_TICK), + ScheduleLock.singleKey(context, scheduler, INIT_SIM_TICK), ) // setup data services that belong to this external simulation @@ -222,7 +221,7 @@ class SimonaStandaloneSetup( extLink.getExtDataSimulations.asScala.zipWithIndex.map { case (_: ExtEvSimulation, dIndex) => val extEvDataService = context.toClassic.simonaActorOf( - ExtEvDataService.props(extScheduler.toClassic), + ExtEvDataService.props(scheduler.toClassic), s"$index-$dIndex", ) val extEvData = new ExtEvData(extEvDataService, extSimAdapter) @@ -231,7 +230,7 @@ class SimonaStandaloneSetup( InitExtEvData(extEvData), ScheduleLock.singleKey( context, - extScheduler, + scheduler, INIT_SIM_TICK, ), ) @@ -254,10 +253,9 @@ class SimonaStandaloneSetup( ExtSimSetupData( extSimAdapters, extDataServices.flatten.toMap, - Some(extScheduler), ) } else { - ExtSimSetupData(Iterable.empty, Map.empty, None) + ExtSimSetupData(Iterable.empty, Map.empty) } } diff --git a/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala b/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala deleted file mode 100644 index 5aeaf9591a..0000000000 --- a/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala +++ /dev/null @@ -1,213 +0,0 @@ -/* - * © 2023. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.util.scala.collection.immutable - -import scala.collection.immutable - -/** Queue that is specialized at holding many values of type [[V]] for the same - * key of type [[K]], while only allowing each value to be linked to one key. - * Mathematically, the relation between keys and values is thus not univalent - * (right-unique), but injective. - * - * In contrast to the [[PrioritySwitchBiSet]], this data structure always - * returns values stored at the same key in a fixed order that is determined by - * the first storage of a value for any key. Also, only one value can be - * returned at a time for some key, namely the value that is in order after the - * last returned value for the same key. - * - * @param orderedValues - * Vector that holds values in a fixed order, in which they are always - * returned for all keys. This vector is never cleared, thus there should be - * a limited number of values stored in this data structure only. - * @param valueToIndex - * A reverse map to [[orderedValues]], linking every value to the index it - * has been stored at. - * @param queue - * A sorted map that holds the keys and the indices to values stored in - * [[orderedValues]]. Value indices are also ordered numerically within - * sorted sets. - * @param back - * A map that links values back to keys. Used to fastly ensure every value is - * only stored once. - * @tparam K - * Type of the key - * @tparam V - * Type of the value - */ -final case class PrioritySwitchBiSet[K, V]( - private val orderedValues: immutable.Vector[V], - private val valueToIndex: Map[V, Int], - private val queue: immutable.SortedMap[K, immutable.SortedSet[Int]], - private val back: Map[V, K], -) { - - /** Get the first key of the queue, if the queue is not empty. - * - * @return - * The first key - */ - def headKeyOption: Option[K] = - queue.headOption.map { case (key, _) => key } - - /** Get the first index of the first key of the queue, if the queue is not - * empty. - * - * @return - * The first index of the first key - */ - def headKeyIndexOption: Option[Int] = - queue.headOption.flatMap { case (_, set) => set.headOption } - - /** Get the index that given value is stored at, if it exists. - * - * @param value - * Value to retrieve the index for - * @return - * The index - */ - def indexOf(value: V): Option[Int] = valueToIndex.get(value) - - /** Set given value to given key - * - * @param key - * The key to add the value for - * @param value - * The value to add - * @return - * The altered data structure - */ - def set(key: K, value: V): PrioritySwitchBiSet[K, V] = - dequeue(value).add(key, value) - - private def dequeue(value: V): PrioritySwitchBiSet[K, V] = - back - .get(value) - .map { key => - val updatedBack = back.removed(value) - - val updatedQueue = valueToIndex - .get(value) - .map { i => - val newSet = - queue - .get(key) - .map(_.excl(i)) - .getOrElse(immutable.SortedSet.empty[Int]) - - if (newSet.isEmpty) - queue.removed(key) - else - queue.updated(key, newSet) - } - .getOrElse(queue) - - copy( - queue = updatedQueue, - back = updatedBack, - ) - } - .getOrElse(this) - - private def add(key: K, value: V): PrioritySwitchBiSet[K, V] = { - // add value to orderedValues and valueToIndex, if not present already - val (updatedStruct, i) = orderedValues.indexOf(value) match { - case -1 => - val newIndex = orderedValues.size - ( - copy( - orderedValues = orderedValues.appended(value), - valueToIndex = valueToIndex.updated(value, newIndex), - ), - newIndex, - ) - case i => - (this, i) - } - - // add value to key - val updatedSet = - queue.getOrElse(key, immutable.SortedSet.empty[Int]).incl(i) - - updatedStruct.copy( - queue = queue.updated(key, updatedSet), - back = back.updated(value, key), - ) - } - - /** Retrieves the first element in the list of given key. The returned element - * is also removed the queue here. - * - * If the list of values for given key is empty, the list is removed: There - * are no empty lists in the queue, thus also keys only exist for non-empty - * lists. - * - * @return - * The first element in the list of the first key and the changed data - * structure, if it is not empty. - */ - def takeNextValueFor(key: K): Option[(V, PrioritySwitchBiSet[K, V])] = { - queue - .get(key) - .flatMap { set => - set.headOption.map(orderedValues).map((set, _)) - } - .map { case (set, firstValue) => - val updatedQueue = - if (set.size == 1) - queue.removed(key) - else - // drop first value - queue.updated(key, set.drop(1)) - - ( - firstValue, - copy(queue = updatedQueue, back = back.removed(firstValue)), - ) - } - } - - /** Tests whether there is no value for any key in the queue. - * - * @return - * True if the queue is empty - */ - def isEmpty: Boolean = queue.isEmpty - - /** Tests whether there is any value for any key in the queue. - * - * @return - * True if the queue is non-empty - */ - def nonEmpty: Boolean = queue.nonEmpty - - /** Returns all values in order - * - * @return - * The value vector - */ - def values: Vector[V] = orderedValues - -} - -object PrioritySwitchBiSet { - - /** Creates and returns an empty PrioritySwitchBiSet for given types. - * - * @tparam K - * Type of the key, which needs to be sortable by means of [[Ordering]] - * @tparam V - * Type of the value - * @return - * An empty PrioritySwitchBiSet - */ - def empty[K: Ordering, V]: PrioritySwitchBiSet[K, V] = PrioritySwitchBiSet( - Vector.empty, - Map.empty, - immutable.SortedMap.empty, - Map.empty, - ) -} diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala index c6ffda8fab..8a88b2163f 100644 --- a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala @@ -60,7 +60,7 @@ class SimonaSimSpec extends ScalaTestWithActorTestKit with UnitSpec { ) { override def extSimulations( context: ActorContext[_], - rootScheduler: ActorRef[SchedulerMessage], + scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData = { // We cannot return a TestProbe ref here, // needs to be a proper actor created by context @@ -451,7 +451,7 @@ object SimonaSimSpec { override def extSimulations( context: ActorContext[_], - rootScheduler: ActorRef[SchedulerMessage], + scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData = ExtSimSetupData(Iterable.empty, Map.empty, None) } diff --git a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala index d8cc467dd9..12c7681835 100644 --- a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala @@ -56,7 +56,7 @@ class SimonaSetupSpec extends UnitSpec with SimonaSetup with SubGridGateMokka { override def extSimulations( context: ActorContext[_], - rootScheduler: ActorRef[SchedulerMessage], + scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData = throw new NotImplementedException( "This is a dummy setup" ) diff --git a/src/test/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSetSpec.scala b/src/test/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSetSpec.scala deleted file mode 100644 index 125cc24c5f..0000000000 --- a/src/test/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSetSpec.scala +++ /dev/null @@ -1,174 +0,0 @@ -/* - * © 2023. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.util.scala.collection.immutable - -import edu.ie3.simona.test.common.UnitSpec - -class PrioritySwitchBiSetSpec extends UnitSpec { - private type Key = Int - private type Value = String - - private val item1: Value = "test1" - private val item2: Value = "test2" - private val item3: Value = "test3" - private val item4: Value = "test4" - private val item5: Value = "test5" - private val item6: Value = "test6" - - "A PriorityMultiBiSet" should { - "be created correctly emptily" in { - val prioSet = PrioritySwitchBiSet.empty[Key, Value] - - prioSet.isEmpty shouldBe true - prioSet.nonEmpty shouldBe false - prioSet.headKeyOption shouldBe None - prioSet.headKeyIndexOption shouldBe None - prioSet.values shouldBe Vector.empty - prioSet.takeNextValueFor(0) shouldBe None - } - - "behave correctly when adding to an empty map" in { - val prioSet00 = PrioritySwitchBiSet.empty[Key, Value] - - val prioSet01 = prioSet00.set(1, item1) - prioSet01.isEmpty shouldBe false - prioSet01.nonEmpty shouldBe true - prioSet01.headKeyOption shouldBe Some(1) - prioSet01.headKeyIndexOption shouldBe Some(0) - prioSet01.values shouldBe Vector(item1) - prioSet01.indexOf(item1) shouldBe Some(0) - prioSet01.indexOf(item2) shouldBe None - prioSet01.takeNextValueFor(0) shouldBe None - - val (val02, prioSet02) = prioSet01.takeNextValueFor(1).value - val02 shouldBe item1 - prioSet02.isEmpty shouldBe true - prioSet02.headKeyOption shouldBe None - prioSet02.headKeyIndexOption shouldBe None - prioSet02.values shouldBe Vector(item1) - } - - "behave correctly when adding and retrieving multiple values" in { - val prioSet00 = PrioritySwitchBiSet.empty[Key, Value] - - val prioSet01 = prioSet00.set(10, item1) - prioSet01.nonEmpty shouldBe true - prioSet01.headKeyOption shouldBe Some(10) - prioSet01.headKeyIndexOption shouldBe Some(0) - prioSet01.values shouldBe Vector(item1) - prioSet01.indexOf(item1) shouldBe Some(0) - - val prioSet02 = prioSet01.set(2, item2) - prioSet02.nonEmpty shouldBe true - prioSet02.headKeyOption shouldBe Some(2) - prioSet02.headKeyIndexOption shouldBe Some(1) - prioSet02.values shouldBe Vector(item1, item2) - prioSet02.indexOf(item2) shouldBe Some(1) - - // moving item2 to 5 - val prioSet03 = prioSet02.set(5, item2) - prioSet03.nonEmpty shouldBe true - prioSet03.headKeyOption shouldBe Some(5) - prioSet03.headKeyIndexOption shouldBe Some(1) - prioSet03.values shouldBe Vector(item1, item2) - prioSet03.indexOf(item2) shouldBe Some(1) - prioSet03.takeNextValueFor(2) shouldBe None - - val prioSet04 = prioSet03.set(5, item4) - prioSet04.nonEmpty shouldBe true - prioSet04.headKeyOption shouldBe Some(5) - prioSet04.headKeyIndexOption shouldBe Some(1) - prioSet04.values shouldBe Vector(item1, item2, item4) - prioSet04.indexOf(item4) shouldBe Some(2) - - val prioSet05 = prioSet04.set(5, item3) - prioSet05.headKeyOption shouldBe Some(5) - prioSet05.headKeyIndexOption shouldBe Some(1) - prioSet05.values shouldBe Vector(item1, item2, item4, item3) - prioSet05.indexOf(item3) shouldBe Some(3) - - val prioSet06 = prioSet05.set(15, item5) - prioSet06.headKeyOption shouldBe Some(5) - prioSet06.headKeyIndexOption shouldBe Some(1) - prioSet06.values shouldBe Vector(item1, item2, item4, item3, item5) - prioSet06.indexOf(item5) shouldBe Some(4) - - // moving item4 to 15 - val prioSet07 = prioSet06.set(15, item4) - prioSet07.headKeyOption shouldBe Some(5) - prioSet07.headKeyIndexOption shouldBe Some(1) - prioSet07.values shouldBe Vector(item1, item2, item4, item3, item5) - - // moving item1 to 15 - val prioSet08 = prioSet07.set(15, item1) - prioSet08.headKeyOption shouldBe Some(5) - prioSet08.headKeyIndexOption shouldBe Some(1) - prioSet08.values shouldBe Vector(item1, item2, item4, item3, item5) - - // priority indices should not have changed - prioSet08.indexOf(item1) shouldBe Some(0) - prioSet08.indexOf(item2) shouldBe Some(1) - prioSet08.indexOf(item3) shouldBe Some(3) - prioSet08.indexOf(item4) shouldBe Some(2) - prioSet08.indexOf(item5) shouldBe Some(4) - prioSet08.indexOf(item6) shouldBe None - - // retrieving values now. They should come in order: - // 5 -> (item2, item3), 15 -> (item1, item4, item5) - - val (val09, prioSet09) = prioSet08.takeNextValueFor(5).value - val09 shouldBe item2 - prioSet09.isEmpty shouldBe false - prioSet09.headKeyOption shouldBe Some(5) - prioSet09.headKeyIndexOption shouldBe Some(3) - - val (val10, prioSet10) = prioSet09.takeNextValueFor(5).value - val10 shouldBe item3 - prioSet10.isEmpty shouldBe false - prioSet10.headKeyOption shouldBe Some(15) - prioSet10.headKeyIndexOption shouldBe Some(0) - - val (val11, prioSet11) = prioSet10.takeNextValueFor(15).value - val11 shouldBe item1 - prioSet11.isEmpty shouldBe false - prioSet11.headKeyOption shouldBe Some(15) - prioSet11.headKeyIndexOption shouldBe Some(2) - - val (val12, prioSet12) = prioSet11.takeNextValueFor(15).value - val12 shouldBe item4 - prioSet12.isEmpty shouldBe false - prioSet12.headKeyOption shouldBe Some(15) - prioSet12.headKeyIndexOption shouldBe Some(4) - - // moving item5 to 10 - val prioSet13 = prioSet12.set(10, item5) - prioSet13.isEmpty shouldBe false - prioSet13.headKeyOption shouldBe Some(10) - prioSet13.headKeyIndexOption shouldBe Some(4) - prioSet13.takeNextValueFor(15) shouldBe None - - val (val14, prioSet14) = prioSet13.takeNextValueFor(10).value - val14 shouldBe item5 - prioSet14.isEmpty shouldBe true - prioSet14.nonEmpty shouldBe false - prioSet14.headKeyOption shouldBe None - prioSet14.headKeyIndexOption shouldBe None - - // priority indices should not have changed - prioSet14.indexOf(item1) shouldBe Some(0) - prioSet14.indexOf(item2) shouldBe Some(1) - prioSet14.indexOf(item3) shouldBe Some(3) - prioSet14.indexOf(item4) shouldBe Some(2) - prioSet14.indexOf(item5) shouldBe Some(4) - prioSet14.indexOf(item6) shouldBe None - - prioSet14.values shouldBe Vector(item1, item2, item4, item3, item5) - } - - } - -} From 19bed20afa51db2f4069c3e06ca1949ed6677d05 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 2 Apr 2024 15:04:47 +0200 Subject: [PATCH 07/39] More implementation & cleanup --- .../flex/MinMaxFlexibilityMessage.scala | 6 --- .../messages/services/EvMessage.scala | 2 +- .../messages/services/ServiceMessage.scala | 7 ++- .../simona/service/ev/ExtEvDataService.scala | 52 +++++++++++++------ 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala index 986cc3afe4..af72bf52c6 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala @@ -114,10 +114,4 @@ object MinMaxFlexibilityMessage { ): ProvideMinMaxFlexOptions = ProvideMinMaxFlexOptions(modelUuid, power, power, power) } - - /** Indicates that flex options have not changed since last provision - * @param modelUuid - */ - case class FlexOptionsUnchanged(override val modelUuid: UUID) - extends ProvideFlexOptions } diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala index 1b7a714514..fa83e37602 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala @@ -70,7 +70,7 @@ object EvMessage { /** Holds arrivals for one charging station * * @param arrivals - * EVs arriving at the charging station, which might be empty + * EVs arriving at the charging station */ final case class ArrivingEvsData( arrivals: Seq[EvModelWrapper] diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala index eed3832f63..603ac38ace 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala @@ -82,9 +82,14 @@ object ServiceMessage { val nextDataTick: Option[Long] } - /** @param serviceRef + /** Scheduling a new data provision for given tick with the system participant + * + * @param serviceRef + * The service to schedule * @param tick + * The tick at which the data provision is to be scheduled * @param unlockKey + * The unlock key for possibly scheduling an activation */ case class ScheduleProvisionMessage( serviceRef: ActorRef, diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index 42fade723a..44c96e213c 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -300,7 +300,7 @@ class ExtEvDataService(override val scheduler: ActorRef) } } else { - val actorToEvsData = allArrivingEvsData + val (actorsToSchedule, actorsWithArrivals) = allArrivingEvsData .flatMap { case (evcs, arrivingEvsData) => serviceStateData.uuidToActorRef .get(evcs) @@ -313,22 +313,40 @@ class ExtEvDataService(override val scheduler: ActorRef) None } } - .partitionMap() // TODO - - actorToEvsData.zip(keys).foreach { case ((actor, arrivingEvsData), key) => - if (arrivingEvsData.arrivals.nonEmpty) - actor ! ProvideEvDataMessage( - tick, - self, - arrivingEvsData.arrivals, - Some(arrivingEvsData.maybeNextTick), - ) - else - actor ! ScheduleProvisionMessage( - self, - tick, - unlockKey = key, - ) + .partitionMap { + case (actor, arrivingEvsData) if arrivingEvsData.arrivals.isEmpty => + Left(actor, arrivingEvsData.maybeNextTick) + case (actor, arrivingEvsData) => + Right((actor, arrivingEvsData)) + } + + if (actorsToSchedule.toSeq.nonEmpty) { + val keys = ScheduleLock.multiKey( + ctx, + scheduler.toTyped, + tick, + actorsToSchedule.size, + ) + + actorsToSchedule.zip(keys).foreach { + case ((actor, maybeNextTick), key) => + maybeNextTick.foreach { nextTick => + actor ! ScheduleProvisionMessage( + self, + nextTick, + unlockKey = key, + ) + } + } + } + + actorsWithArrivals.foreach { case (actor, arrivingEvsData) => + actor ! ProvideEvDataMessage( + tick, + self, + arrivingEvsData.arrivals, + Some(arrivingEvsData.maybeNextTick), + ) } } From e2236b4f25700908f3afc9f41c896fe821a05c03 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 3 Jun 2024 22:53:48 +0200 Subject: [PATCH 08/39] change actor naming to actorId plus random number --- .../ie3/simona/actor/SimonaActorNaming.scala | 24 ++++++++----------- .../agent/grid/GridAgentController.scala | 3 ++- .../sim/setup/SimonaStandaloneSetup.scala | 6 +++-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala index 7e29146003..ce2669b300 100644 --- a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala +++ b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala @@ -10,7 +10,7 @@ import org.apache.pekko.actor.typed.ActorRef import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps import org.apache.pekko.actor.{ActorRefFactory, Props, ActorRef => ClassicRef} -import java.util.UUID +import scala.util.Random object SimonaActorNaming { @@ -18,24 +18,20 @@ object SimonaActorNaming { extends AnyVal { def simonaActorOf(props: Props, actorId: String): ClassicRef = - refFactory.actorOf(props, actorName(props, actorId)) - - def simonaActorOf(props: Props): ClassicRef = - refFactory.actorOf(props, actorName(props, simonaActorUuid)) + refFactory.actorOf(props, actorName(props, simonaActorId(actorId))) } - /** Constructs a uuid and cuts it down to 6 digits for convenience. Although - * this is dangerous as duplicates might be possible, it should be sufficient - * in our case as the uniqueness is only required in one actor system + /** Constructs an Id for convenience actor naming. Although this is dangerous + * as duplicates might be possible, it should be sufficient in our case as + * the uniqueness is only required in one actor system * * @return - * a shortened uuid string + * an Id string */ - private def simonaActorUuid: String = { - val uuid = UUID.randomUUID().toString.substring(0, 5) - val timestamp = System.nanoTime() - val finalUuid = s"$uuid-$timestamp" - finalUuid.substring(0, 15) + private def simonaActorId(actorId: String): String = { + val randomNumber = Random.nextInt(1000).toString + val finalId = s"$actorId-$randomNumber" + finalId } /** Constructs an actor name based on the simona convention for actor names. diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala index 7ba4abef77..33482204f6 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala @@ -652,7 +652,8 @@ class GridAgentController( maybeControllingEm, ), listener.map(_.toClassic), - ) + ), + evcsInput.getId, ) .toTyped diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala index 1f0d894316..951e4cd714 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala @@ -158,7 +158,8 @@ class SimonaStandaloneSetup( simulationStart, ), simulationStart, - ) + ), + "primaryServiceProxyAgent", ) scheduler ! ScheduleActivation(primaryServiceProxy.toTyped, INIT_SIM_TICK) @@ -176,7 +177,8 @@ class SimonaStandaloneSetup( .toZonedDateTime(simonaConfig.simona.time.startDateTime), TimeUtil.withDefaults .toZonedDateTime(simonaConfig.simona.time.endDateTime), - ) + ), + "weatherAgent", ) weatherService ! SimonaService.Create( InitWeatherServiceStateData( From 2ac0ceba4da373dc528626b3f0dc4564a192c596 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 9 Apr 2024 13:01:54 +0200 Subject: [PATCH 09/39] Adapting to changes in simonaAPI interface Signed-off-by: Sebastian Peter --- build.gradle | 2 +- .../agent/participant/ParticipantAgent.scala | 69 --- .../ParticipantAgentFundamentals.scala | 6 +- .../evcs/EvcsAgentFundamentals.scala | 54 +- .../messages/services/EvMessage.scala | 2 +- .../messages/services/ServiceMessage.scala | 16 - .../simona/service/ev/ExtEvDataService.scala | 108 ++-- .../EvcsAgentModelCalculationSpec.scala | 125 ++--- .../scheduler/PhaseSwitchSchedulerSpec.scala | 502 ------------------ .../service/ev/ExtEvDataServiceSpec.scala | 242 ++++++--- .../primary/PrimaryServiceWorkerSpec.scala | 4 - .../edu/ie3/simona/sim/SimonaSimSpec.scala | 4 +- 12 files changed, 283 insertions(+), 851 deletions(-) delete mode 100644 src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala diff --git a/build.gradle b/build.gradle index 161bc59d69..ed46bbe283 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ dependencies { exclude group: 'edu.ie3' } - implementation('com.github.ie3-institute:simonaAPI:0.4.0') { + implementation('com.github.ie3-institute:simonaAPI:0.5-SNAPSHOT') { exclude group: 'org.apache.logging.log4j' exclude group: 'org.slf4j' /* Exclude our own nested dependencies */ 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 e573ac7949..fb9620ddf7 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala @@ -45,23 +45,19 @@ import edu.ie3.simona.model.participant.{ } import edu.ie3.simona.ontology.messages.Activation import edu.ie3.simona.ontology.messages.PowerMessage.RequestAssetPowerMessage -import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleActivation import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{ FlexResponse, IssueFlexControl, RequestFlexOptions, - ScheduleFlexRequest, } import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ PrimaryServiceRegistrationMessage, ProvisionMessage, RegistrationResponseMessage, - ScheduleProvisionMessage, } import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.util.scala.quantities.ReactivePower -import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef} import org.apache.pekko.actor.{ActorRef, FSM} import squants.{Dimensionless, Power} @@ -195,14 +191,6 @@ abstract class ParticipantAgent[ fromOutsideBaseStateData, ) - case Event( - msg: ScheduleProvisionMessage, - baseStateData: BaseStateData[PD], - ) => - val updatedStateData = handleProvisionScheduling(msg, baseStateData) - - stay() using updatedStateData - case Event( msg: ProvisionMessage[Data], baseStateData: BaseStateData[PD], @@ -357,18 +345,6 @@ abstract class ParticipantAgent[ scheduler, )(stateData.baseStateData.outputConfig) - case Event( - msg: ScheduleProvisionMessage, - stateData @ DataCollectionStateData( - baseStateData: BaseStateData[PD], - _, - _, - ), - ) => - val updatedStateData = handleProvisionScheduling(msg, baseStateData) - - stay() using stateData.copy(baseStateData = updatedStateData) - case Event( msg: ProvisionMessage[_], stateData @ DataCollectionStateData( @@ -650,51 +626,6 @@ abstract class ParticipantAgent[ .getOrElse(createInitialState(baseStateData)) } - private def handleProvisionScheduling( - msg: ScheduleProvisionMessage, - baseStateData: BaseStateData[PD], - ): BaseStateData[PD] = { - val foreseenDataTicks = - baseStateData.foreseenDataTicks + (msg.serviceRef -> Some(msg.tick)) - - val updatedStateData = BaseStateData.updateBaseStateData( - baseStateData, - baseStateData.resultValueStore, - baseStateData.requestValueStore, - baseStateData.voltageValueStore, - baseStateData.additionalActivationTicks, - foreseenDataTicks, - ) - - updatedStateData match { - case modelStateData: ParticipantModelBaseStateData[_, _, _, _] => - val maybeEmAgent = modelStateData.flexStateData.map(_.emAgent) - - maybeEmAgent match { - case Some(emAgent) => - emAgent ! ScheduleFlexRequest( - modelStateData.model.getUuid, - msg.tick, - Some(msg.unlockKey), - ) - case None => - scheduler ! ScheduleActivation( - self.toTyped, - msg.tick, - Some(msg.unlockKey), - ) - } - case _ => - scheduler ! ScheduleActivation( - self.toTyped, - msg.tick, - Some(msg.unlockKey), - ) - } - - updatedStateData - } - /** Handle an incoming data provision message in Idle, try to figure out who's * about to send information in this tick as well. Map together all senders * with their yet apparent information. 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 ff060dd980..be3060c236 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -1065,9 +1065,9 @@ protected trait ParticipantAgentFundamentals[ false } - // Only for completing initialization: - // if we are EM-managed, there is no new tick for the - // scheduler, since we are activated by the EmAgent from now on + // If we're completing initialization and we're EM-managed: + // There is no new tick for the scheduler, + // since we are activated by the EmAgent from now on scheduler ! Completion( self.toTyped, maybeNextTick.filterNot(_ => emManaged), diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala index 9bf4dde900..0d6e3f8638 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala @@ -210,7 +210,7 @@ protected trait EvcsAgentFundamentals .getOrElse(tick, Map.empty) .collectFirst { // filter secondary data for arriving EVs data - case (_, arrivingEvsData: ArrivingEvsData) => + case (_, arrivingEvsData: ArrivingEvs) => arrivingEvsData.arrivals } .getOrElse(Seq.empty) @@ -318,7 +318,7 @@ protected trait EvcsAgentFundamentals .values .collectFirst { // filter secondary data for arriving EVs data - case _: ArrivingEvsData => + case _: ArrivingEvs => handleArrivingEvsAndGoIdle( tick, scheduler, @@ -485,32 +485,40 @@ protected trait EvcsAgentFundamentals val relevantData = createCalcRelevantData(modelBaseStateData, tick) - val lastState = getLastOrInitialStateData(modelBaseStateData, tick) + // TODO also adapt for em-controlled and test + val updatedBaseStateData = { + if (relevantData.arrivals.nonEmpty) { + val lastState = getLastOrInitialStateData(modelBaseStateData, tick) - val currentEvs = modelBaseStateData.model.determineCurrentEvs( - relevantData, - lastState, - ) + val currentEvs = modelBaseStateData.model.determineCurrentEvs( + relevantData, + lastState, + ) - // if new EVs arrived, a new scheduling must be calculated. - val newSchedule = modelBaseStateData.model.calculateNewScheduling( - relevantData, - currentEvs, - ) + // if new EVs arrived, a new scheduling must be calculated. + val newSchedule = modelBaseStateData.model.calculateNewScheduling( + relevantData, + currentEvs, + ) - // create new current state - val newState = EvcsState(currentEvs, newSchedule, tick) + // create new current state + val newState = EvcsState(currentEvs, newSchedule, tick) - val updatedStateDataStore = ValueStore.updateValueStore( - modelBaseStateData.stateDataStore, - tick, - newState, - ) + val updatedStateDataStore = ValueStore.updateValueStore( + modelBaseStateData.stateDataStore, + tick, + newState, + ) - /* Update the base state data with the updated result value store and relevant data store */ - val updatedBaseStateData = modelBaseStateData.copy( - stateDataStore = updatedStateDataStore - ) + /* Update the base state data with the updated result value store and relevant data store */ + modelBaseStateData.copy( + stateDataStore = updatedStateDataStore + ) + } else + // Empty arrivals means that there is no data for this EVCS at the current tick, + // thus we just return and wait for the next activation + modelBaseStateData + } // We're only here if we're not flex-controlled, thus sending a Completion is always right goToIdleReplyCompletionAndScheduleTriggerForNextAction( diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala index fa83e37602..1e8ae341a9 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala @@ -72,7 +72,7 @@ object EvMessage { * @param arrivals * EVs arriving at the charging station */ - final case class ArrivingEvsData( + final case class ArrivingEvs( arrivals: Seq[EvModelWrapper] ) extends EvData {} diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala index 603ac38ace..d7444454fd 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala @@ -81,20 +81,4 @@ object ServiceMessage { */ val nextDataTick: Option[Long] } - - /** Scheduling a new data provision for given tick with the system participant - * - * @param serviceRef - * The service to schedule - * @param tick - * The tick at which the data provision is to be scheduled - * @param unlockKey - * The unlock key for possibly scheduling an activation - */ - case class ScheduleProvisionMessage( - serviceRef: ActorRef, - tick: Long, - unlockKey: ScheduleKey, - ) extends ServiceMessage - } diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index 44c96e213c..10b4329ac8 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -19,11 +19,7 @@ import edu.ie3.simona.exceptions.{ import edu.ie3.simona.model.participant.evcs.EvModelWrapper import edu.ie3.simona.ontology.messages.services.EvMessage._ import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage -import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ - ScheduleProvisionMessage, - ServiceRegistrationMessage, -} -import edu.ie3.simona.scheduler.ScheduleLock +import edu.ie3.simona.ontology.messages.services.ServiceMessage.ServiceRegistrationMessage import edu.ie3.simona.service.ServiceStateData.{ InitializeServiceStateData, ServiceBaseStateData, @@ -35,11 +31,11 @@ import edu.ie3.simona.service.ev.ExtEvDataService.{ import edu.ie3.simona.service.{ExtDataSupport, ServiceStateData, SimonaService} import edu.ie3.simona.util.ReceiveDataMap import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK -import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps import org.apache.pekko.actor.{ActorContext, ActorRef, Props} import java.util.UUID import scala.jdk.CollectionConverters._ +import scala.jdk.OptionConverters._ import scala.util.{Failure, Success, Try} object ExtEvDataService { @@ -181,6 +177,10 @@ class ExtEvDataService(override val scheduler: ActorRef) ExtEvStateData, Option[Long], ) = { + def asScala[E] + : java.util.Map[UUID, java.util.List[E]] => Map[UUID, Seq[E]] = map => + map.asScala.view.mapValues(_.asScala.toSeq).toMap + serviceStateData.extEvMessage.getOrElse( throw ServiceException( "ExtEvDataService was triggered without ExtEvMessage available" @@ -189,14 +189,14 @@ class ExtEvDataService(override val scheduler: ActorRef) case _: RequestEvcsFreeLots => requestFreeLots(tick) case departingEvsRequest: RequestDepartingEvs => - requestDepartingEvs(tick, departingEvsRequest.departures) + requestDepartingEvs(tick, asScala(departingEvsRequest.departures)) case arrivingEvsProvision: ProvideArrivingEvs => handleArrivingEvs( tick, - arrivingEvsProvision.arrivals, + asScala(arrivingEvsProvision.arrivals), + arrivingEvsProvision.maybeNextTick.toScala.map(Long2long), )( - serviceStateData, - ctx, + serviceStateData ) } } @@ -228,16 +228,16 @@ class ExtEvDataService(override val scheduler: ActorRef) private def requestDepartingEvs( tick: Long, - requestedDepartingEvs: java.util.Map[UUID, java.util.List[UUID]], + requestedDepartingEvs: Map[UUID, Seq[UUID]], )(implicit serviceStateData: ExtEvStateData ): (ExtEvStateData, Option[Long]) = { val departingEvResponses = - requestedDepartingEvs.asScala.flatMap { case (evcs, departingEvs) => + requestedDepartingEvs.flatMap { case (evcs, departingEvs) => serviceStateData.uuidToActorRef.get(evcs) match { case Some(evcsActor) => - evcsActor ! DepartingEvsRequest(tick, departingEvs.asScala.toSeq) + evcsActor ! DepartingEvsRequest(tick, departingEvs) Some(evcs) @@ -267,94 +267,46 @@ class ExtEvDataService(override val scheduler: ActorRef) private def handleArrivingEvs( tick: Long, - allArrivingEvs: java.util.Map[UUID, java.util.List[EvModel]], + allArrivingEvs: Map[UUID, Seq[EvModel]], + maybeNextTick: Option[Long], )(implicit - serviceStateData: ExtEvStateData, - ctx: ActorContext, + serviceStateData: ExtEvStateData ): (ExtEvStateData, Option[Long]) = { - val allArrivingEvsData = allArrivingEvs.asScala - if (tick == INIT_SIM_TICK) { - val keys = ScheduleLock.multiKey( - ctx, - scheduler.toTyped, - tick, - serviceStateData.uuidToActorRef.size, - ) - serviceStateData.uuidToActorRef.foreach { case (uuid, actor) => - val firstTick: Option[Long] = allArrivingEvsData - .getOrElse( - uuid, - throw new CriticalFailureException( - s"No initial message found for EVCS $actor($uuid)" - ), - ) - .maybeNextTick + maybeNextTick.getOrElse( + throw new CriticalFailureException( + s"After initialization, a first simulation tick needs to be provided by the external mobility simulation." + ) + ) + serviceStateData.uuidToActorRef.foreach { case (_, actor) => actor ! RegistrationSuccessfulMessage( self, - firstTick, + maybeNextTick, ) } - } else { - val (actorsToSchedule, actorsWithArrivals) = allArrivingEvsData - .flatMap { case (evcs, arrivingEvsData) => - serviceStateData.uuidToActorRef - .get(evcs) - .map((_, arrivingEvsData)) - .orElse { - log.warning( - "A corresponding actor ref for UUID {} could not be found", - evcs, - ) - None - } - } - .partitionMap { - case (actor, arrivingEvsData) if arrivingEvsData.arrivals.isEmpty => - Left(actor, arrivingEvsData.maybeNextTick) - case (actor, arrivingEvsData) => - Right((actor, arrivingEvsData)) - } - - if (actorsToSchedule.toSeq.nonEmpty) { - val keys = ScheduleLock.multiKey( - ctx, - scheduler.toTyped, - tick, - actorsToSchedule.size, - ) - - actorsToSchedule.zip(keys).foreach { - case ((actor, maybeNextTick), key) => - maybeNextTick.foreach { nextTick => - actor ! ScheduleProvisionMessage( - self, - nextTick, - unlockKey = key, - ) - } - } - } + } else { + serviceStateData.uuidToActorRef.foreach { case (evcs, actor) => + val evs = + allArrivingEvs.getOrElse(evcs, Seq.empty) - actorsWithArrivals.foreach { case (actor, arrivingEvsData) => actor ! ProvideEvDataMessage( tick, self, - arrivingEvsData.arrivals, - Some(arrivingEvsData.maybeNextTick), + ArrivingEvs(evs.map(EvModelWrapper.apply)), + maybeNextTick, ) } - } ( serviceStateData.copy( extEvMessage = None ), + // We still don't return the next tick because departures might come earlier None, ) } 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 52114eb5be..93858b0b4b 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala @@ -36,10 +36,7 @@ import edu.ie3.simona.ontology.messages.PowerMessage.{ AssetPowerUnchangedMessage, RequestAssetPowerMessage, } -import edu.ie3.simona.ontology.messages.SchedulerMessage.{ - Completion, - ScheduleActivation, -} +import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._ import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions import edu.ie3.simona.ontology.messages.services.EvMessage._ @@ -48,7 +45,6 @@ import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResp RegistrationFailedMessage, RegistrationSuccessfulMessage, } -import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey import edu.ie3.simona.test.ParticipantAgentSpec import edu.ie3.simona.test.common.input.EvcsInputTestData import edu.ie3.simona.test.common.{EvTestData, TestSpawnerClassic} @@ -65,7 +61,6 @@ import squants.energy._ import squants.{Each, Energy, Power} import java.time.ZonedDateTime -import java.util.UUID import scala.collection.immutable.{SortedMap, SortedSet} class EvcsAgentModelCalculationSpec @@ -412,8 +407,6 @@ class EvcsAgentModelCalculationSpec } "do correct transitions faced with new data in Idle" in { - val lock = TestProbe("lock") - val evcsAgent = TestFSMRef( new EvcsAgent( scheduler = scheduler.ref, @@ -438,7 +431,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsgType[RegisterForEvDataMessage] evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(0)), ) /* I'm not interested in the content of the CompletionMessage */ @@ -448,19 +441,17 @@ class EvcsAgentModelCalculationSpec /* Send out new data */ val arrivingEvsData = - ArrivingEvsData(Seq(EvModelWrapper(evA), EvModelWrapper(evB))) + ArrivingEvs(Seq(EvModelWrapper(evA), EvModelWrapper(evB))) - val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( 0L, evService.ref, arrivingEvsData, - unlockKey = key1, + Some(900), ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1)) /* Find yourself in corresponding state and state data */ evcsAgent.stateName shouldBe HandleInformation @@ -471,7 +462,9 @@ class EvcsAgentModelCalculationSpec isYetTriggered, ) => /* The next data tick is already registered */ - baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> None) + baseStateData.foreseenDataTicks shouldBe Map( + evService.ref -> Some(900) + ) /* The yet sent data is also registered */ expectedSenders shouldBe Map( @@ -495,7 +488,7 @@ class EvcsAgentModelCalculationSpec /* The agent will notice, that all expected information are apparent, switch to Calculate and trigger itself * for starting the calculation */ scheduler.expectMsg( - Completion(evcsAgent.toTyped, None) + Completion(evcsAgent.toTyped, Some(900)) ) evcsAgent.stateName shouldBe Idle evcsAgent.stateData match { @@ -548,8 +541,6 @@ class EvcsAgentModelCalculationSpec } "do correct transitions triggered for activation in idle" in { - val lock = TestProbe("lock") - val evcsAgent = TestFSMRef( new EvcsAgent( scheduler = scheduler.ref, @@ -574,7 +565,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsgType[RegisterForEvDataMessage] evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(0)), ) /* I'm not interested in the content of the CompletionMessage */ @@ -597,10 +588,10 @@ class EvcsAgentModelCalculationSpec isYetTriggered, ) => /* The next data tick is already registered */ - baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> None) + baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> Some(0)) /* The yet sent data is also registered */ - expectedSenders shouldBe Map.empty + expectedSenders shouldBe Map(evService.ref -> None) /* It is not yet triggered */ isYetTriggered shouldBe true @@ -612,24 +603,22 @@ class EvcsAgentModelCalculationSpec /* Send out new data */ val arrivingEvsData = - ArrivingEvsData(Seq(EvModelWrapper(evA), EvModelWrapper(evB))) + ArrivingEvs(Seq(EvModelWrapper(evA), EvModelWrapper(evB))) - val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( 0L, evService.ref, arrivingEvsData, - unlockKey = key1, + Some(900), ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1)) /* The agent will notice, that all expected information are apparent, switch to Calculate and trigger itself * for starting the calculation */ scheduler.expectMsg( - Completion(evcsAgent.toTyped, None) + Completion(evcsAgent.toTyped, Some(900)) ) evcsAgent.stateName shouldBe Idle evcsAgent.stateData match { @@ -707,7 +696,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsgType[RegisterForEvDataMessage] evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(10800)), ) /* I'm not interested in the content of the CompletionMessage */ @@ -715,7 +704,7 @@ class EvcsAgentModelCalculationSpec awaitAssert(evcsAgent.stateName shouldBe Idle) evcsAgent ! RequestAssetPowerMessage( - 7200L, + 7200, Each(1.0), Each(0.0), ) @@ -728,8 +717,6 @@ class EvcsAgentModelCalculationSpec } "provide number of free lots when asked to" in { - val lock = TestProbe("lock") - val evcsAgent = TestFSMRef( new EvcsAgent( scheduler = scheduler.ref, @@ -754,7 +741,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsgType[RegisterForEvDataMessage] evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(0)), ) /* I'm not interested in the content of the CompletionMessage */ @@ -777,28 +764,26 @@ class EvcsAgentModelCalculationSpec scheduler.expectNoMessage() /* Send ev for this tick */ - val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( 0L, evService.ref, - ArrivingEvsData(Seq(EvModelWrapper(evA))), - unlockKey = key1, + ArrivingEvs(Seq(EvModelWrapper(evA))), + Some(900), ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1)) scheduler.send( evcsAgent, Activation(0), ) - scheduler.expectMsg(Completion(evcsAgent.toTyped)) + scheduler.expectMsg(Completion(evcsAgent.toTyped, Some(900))) /* Ask for public evcs lot count again with a later tick */ evService.send( evcsAgent, - EvFreeLotsRequest(3600L), + EvFreeLotsRequest(3600), ) // this time, only one is still free @@ -833,8 +818,6 @@ class EvcsAgentModelCalculationSpec ) "provide correct average power after three data ticks are available" in { - val lock = TestProbe("lock") - scheduler.send(evcsAgent, Activation(INIT_SIM_TICK)) /* Refuse registration with primary service */ @@ -848,7 +831,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsgType[RegisterForEvDataMessage] evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(0)), ) /* I'm not interested in the content of the CompletionMessage */ @@ -857,26 +840,26 @@ class EvcsAgentModelCalculationSpec /* Send out the three data points */ /* ... for tick 0 */ - val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( - 0L, + 0, evService.ref, - ArrivingEvsData(Seq(EvModelWrapper(evA.copyWithDeparture(3600L)))), - unlockKey = key1, + ArrivingEvs( + Seq(EvModelWrapper(evA.copyWithDeparture(3600))) + ), + Some(3600), ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1)) scheduler.send(evcsAgent, Activation(0)) - scheduler.expectMsg(Completion(evcsAgent.toTyped)) + scheduler.expectMsg(Completion(evcsAgent.toTyped, Some(3600))) /* ... for tick 3600 */ // departures first evService.send( evcsAgent, - DepartingEvsRequest(3600L, Seq(evA.getUuid)), + DepartingEvsRequest(3600, Seq(evA.getUuid)), ) evService.expectMsgType[DepartingEvsResponse] match { case DepartingEvsResponse(evcs, evModels) => @@ -891,27 +874,27 @@ class EvcsAgentModelCalculationSpec } // arrivals second - val key2 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( - 3600L, + 3600, evService.ref, - ArrivingEvsData(Seq(EvModelWrapper(evB.copyWithDeparture(7200L)))), - unlockKey = key2, + ArrivingEvs( + Seq(EvModelWrapper(evB.copyWithDeparture(7200))) + ), + Some(7200), ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 3600, key2)) scheduler.send(evcsAgent, Activation(3600)) - scheduler.expectMsg(Completion(evcsAgent.toTyped)) + scheduler.expectMsg(Completion(evcsAgent.toTyped, Some(7200))) /* ... for tick 7200 */ // departures first evService.send( evcsAgent, - DepartingEvsRequest(7200L, Seq(evB.getUuid)), + DepartingEvsRequest(7200, Seq(evB.getUuid)), ) evService.expectMsgType[DepartingEvsResponse] match { case DepartingEvsResponse(evcs, evModels) => @@ -925,21 +908,21 @@ class EvcsAgentModelCalculationSpec } } - val key3 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID())) evService.send( evcsAgent, ProvideEvDataMessage( - 7200L, + 7200, evService.ref, - ArrivingEvsData(Seq(EvModelWrapper(evA.copyWithDeparture(10800L)))), - unlockKey = key3, + ArrivingEvs( + Seq(EvModelWrapper(evA.copyWithDeparture(10800))) + ), + None, ), ) - scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 7200, key3)) scheduler.send(evcsAgent, Activation(7200)) - scheduler.expectMsg(Completion(evcsAgent.toTyped)) + scheduler.expectMsg(Completion(evcsAgent.toTyped, None)) /* Ask the agent for average power in tick 7500 */ evcsAgent ! RequestAssetPowerMessage( @@ -1071,7 +1054,7 @@ class EvcsAgentModelCalculationSpec evService.expectMsg(RegisterForEvDataMessage(evcsInputModelQv.getUuid)) evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(0)), ) emAgent.expectMsg( @@ -1107,7 +1090,7 @@ class EvcsAgentModelCalculationSpec ) outputConfig shouldBe defaultOutputConfig additionalActivationTicks shouldBe empty - foreseenDataTicks shouldBe Map(evService.ref -> None) + foreseenDataTicks shouldBe Map(evService.ref -> Some(0)) voltageValueStore shouldBe ValueStore( resolution, SortedMap(0L -> Each(1.0)), @@ -1212,11 +1195,11 @@ class EvcsAgentModelCalculationSpec evService.expectMsg(RegisterForEvDataMessage(evcsInputModelQv.getUuid)) evService.send( evcsAgent, - RegistrationSuccessfulMessage(evService.ref, None), + RegistrationSuccessfulMessage(evService.ref, Some(900)), ) emAgent.expectMsg( - ScheduleFlexRequest(evcsInputModelQv.getUuid, 0) + ScheduleFlexRequest(evcsInputModelQv.getUuid, 900) ) scheduler.expectMsg(Completion(evcsAgent.toTyped)) @@ -1267,7 +1250,7 @@ class EvcsAgentModelCalculationSpec result.p should approximate(Kilowatts(0)) result.q should approximate(Megavars(0)) requestAtNextActivation shouldBe false - requestAtTick shouldBe None + requestAtTick shouldBe Some(900) } // results arrive after next activation @@ -1285,11 +1268,11 @@ class EvcsAgentModelCalculationSpec ProvideEvDataMessage( 900, evService.ref, - ArrivingEvsData(Seq(ev900)), + ArrivingEvs(Seq(ev900)), + Some(4500), ), ) - emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 900)) emAgent.send(evcsAgent, RequestFlexOptions(900)) emAgent.expectMsgType[ProvideFlexOptions] match { @@ -1403,11 +1386,11 @@ class EvcsAgentModelCalculationSpec ProvideEvDataMessage( 4500, evService.ref, - ArrivingEvsData(Seq(ev4500)), + ArrivingEvs(Seq(ev4500)), + Some(11700), ), ) - emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 4500)) emAgent.send(evcsAgent, RequestFlexOptions(4500)) emAgent.expectMsgType[ProvideFlexOptions] match { @@ -1501,7 +1484,7 @@ class EvcsAgentModelCalculationSpec result.q should approximate(Megavars(0)) // since battery is still below lowest soc, it's still considered empty requestAtNextActivation shouldBe true - requestAtTick shouldBe Some(32776) + requestAtTick shouldBe Some(11700) } resultListener.expectMsgPF() { @@ -1536,11 +1519,11 @@ class EvcsAgentModelCalculationSpec ProvideEvDataMessage( 11700, evService.ref, - ArrivingEvsData(Seq(ev11700)), + ArrivingEvs(Seq(ev11700)), + None, ), ) - emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 11700)) emAgent.send(evcsAgent, RequestFlexOptions(11700)) val combinedChargingPower = diff --git a/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala b/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala deleted file mode 100644 index 447a2d2a44..0000000000 --- a/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala +++ /dev/null @@ -1,502 +0,0 @@ -/* - * © 2023. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.scheduler - -import edu.ie3.simona.ontology.messages.SchedulerMessage.{ - Completion, - ScheduleActivation, -} -import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage} -import edu.ie3.simona.scheduler.core.PhaseSwitchCore -import edu.ie3.simona.util.ActorUtils.RichActivatedActor -import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK -import org.apache.pekko.actor.testkit.typed.scaladsl.{ - ScalaTestWithActorTestKit, - TestProbe, -} -import org.scalatest.matchers.should -import org.scalatest.wordspec.AnyWordSpecLike - -/** A lot of functions are already tested in [[SchedulerSpec]] and don't need to - * be repeated here - */ -class PhaseSwitchSchedulerSpec - extends ScalaTestWithActorTestKit - with AnyWordSpecLike - with should.Matchers { - - "The Scheduler with PhaseSwitchCore should work correctly" when { - - "receiving initial activation scheduling before activation" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - scheduler ! ScheduleActivation(agent2.ref, INIT_SIM_TICK) - - agent1.expectNoMessage() - agent2.expectNoMessage() - parent.expectNoMessage() - - // TICK -1, phase 0 - schedulerActivation ! Activation(INIT_SIM_TICK) - - agent1.expectMessage(Activation(INIT_SIM_TICK)) - agent2.expectNoMessage() - - // TICK -1, phase 1 - scheduler ! Completion(agent1.ref) - - agent1.expectNoMessage() - agent2.expectMessage(Activation(INIT_SIM_TICK)) - - parent.expectNoMessage() - - scheduler ! Completion(agent2.ref) - - parent.expectMessage(Completion(schedulerActivation)) - } - - "receiving activation scheduling in a different order than initially" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - scheduler ! ScheduleActivation(agent2.ref, INIT_SIM_TICK) - - // TICK -1, phase 0 - schedulerActivation ! Activation(INIT_SIM_TICK) - agent1.expectMessage(Activation(INIT_SIM_TICK)) - - // TICK -1, phase 1 - scheduler ! Completion(agent1.ref) - agent2.expectMessage(Activation(INIT_SIM_TICK)) - - scheduler ! Completion(agent2.ref, Some(0)) - parent.expectMessage(Completion(schedulerActivation, Some(0))) - - // scheduling first actor after the second actor for the same tick - scheduler ! ScheduleActivation(agent1.ref, 0) - parent.expectNoMessage() - - // TICK 0, phase 0 - schedulerActivation ! Activation(0) - - agent1.expectMessage(Activation(0)) - agent2.expectNoMessage() - - // TICK -1, phase 1 - scheduler ! Completion(agent1.ref) - - agent1.expectNoMessage() - agent2.expectMessage(Activation(0)) - - scheduler ! Completion(agent2.ref) - - parent.expectMessage(Completion(schedulerActivation)) - } - - "receiving activation scheduling after init activation" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - agent1.expectNoMessage() - - // TICK -1, phase 0 - schedulerActivation ! Activation(INIT_SIM_TICK) - - agent1.expectMessage(Activation(INIT_SIM_TICK)) - agent2.expectNoMessage() - - scheduler ! ScheduleActivation( - agent2.ref, - INIT_SIM_TICK, - ) - - // waiting for next phase - agent2.expectNoMessage() - parent.expectNoMessage() - - // TICK -1, phase 1 - scheduler ! Completion(agent1.ref) - - agent2.expectMessage(Activation(INIT_SIM_TICK)) - - scheduler ! Completion(agent2.ref) - - parent.expectMessage(Completion(schedulerActivation)) - } - - "scheduling with parent when earliest tick changes" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation(agent1.ref, 10) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 10 - val schedulerActivation = sa1.actor - - scheduler ! ScheduleActivation(agent2.ref, INIT_SIM_TICK) - parent.expectMessage( - ScheduleActivation(schedulerActivation, INIT_SIM_TICK) - ) - - scheduler ! ScheduleActivation(agent2.ref, 5) - parent.expectMessage(ScheduleActivation(schedulerActivation, 5)) - - scheduler ! ScheduleActivation(agent2.ref, 11) - // expect activation for earliest tick (of agent 1) - parent.expectMessage(ScheduleActivation(schedulerActivation, 10)) - - scheduler ! ScheduleActivation(agent2.ref, 20) - // no update, 10 is still earliest - parent.expectNoMessage() - - scheduler ! ScheduleActivation(agent2.ref, 10) - parent.expectNoMessage() - - agent1.expectNoMessage() - - // TICK 10, phase 0 - schedulerActivation ! Activation(10) - - agent1.expectMessage(Activation(10)) - agent2.expectNoMessage() - - // TICK 10, phase 1 - scheduler ! Completion(agent1.ref) - - agent2.expectMessage(Activation(10)) - } - - "scheduling two actors for different ticks" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation( - agent1.ref, - INIT_SIM_TICK, - ) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - scheduler ! ScheduleActivation( - agent2.ref, - INIT_SIM_TICK, - ) - - // TICK -1 - schedulerActivation ! Activation(INIT_SIM_TICK) - - agent2.expectNoMessage() - agent1.expectActivationAndComplete( - scheduler, - INIT_SIM_TICK, - Some(0), - ) - - parent.expectNoMessage() - - agent2.expectActivationAndComplete( - scheduler, - INIT_SIM_TICK, - Some(0), - ) - - parent.expectMessage(Completion(schedulerActivation, Some(0))) - - agent1.expectNoMessage() - agent2.expectNoMessage() - - // TICK 0 - schedulerActivation ! Activation(0) - - agent1.expectActivationAndComplete( - scheduler, - 0, - Some(900), - ) - - parent.expectNoMessage() - - agent2.expectActivationAndComplete( - scheduler, - 0, - Some(300), - ) - - parent.expectMessage(Completion(schedulerActivation, Some(300))) - - // TICK 300 - schedulerActivation ! Activation(300) - - agent2.expectActivationAndComplete( - scheduler, - 300, - Some(900), - ) - - parent.expectMessage(Completion(schedulerActivation, Some(900))) - - agent1.expectNoMessage() - agent2.expectNoMessage() - - // TICK 900 - schedulerActivation ! Activation(900) - - agent1.expectActivationAndComplete( - scheduler, - 900, - Some(3600), - ) - - parent.expectNoMessage() - - agent2.expectActivationAndComplete( - scheduler, - 900, - Some(1800), - ) - - parent.expectMessage(Completion(schedulerActivation, Some(1800))) - - parent.expectNoMessage() - agent1.expectNoMessage() - agent2.expectNoMessage() - } - - } - - "The Scheduler with PhaseSwitchCore should fail and stop" when { - - "activated if no activation is scheduled" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - // TICK -1 - schedulerActivation ! Activation(INIT_SIM_TICK) - agent1.expectActivationAndComplete(scheduler, INIT_SIM_TICK, None) - parent.expectMessage(Completion(schedulerActivation, None)) - - // No activation scheduled, this should fail now - schedulerActivation ! Activation(0) - - // scheduler stopped - parent.expectTerminated(scheduler) - } - - "activated with wrong tick" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe INIT_SIM_TICK - val schedulerActivation = sa1.actor - - // TICK 0 - schedulerActivation ! Activation(0) - - // agent does not receive activation - agent1.expectNoMessage() - parent.expectNoMessage() - - // scheduler stopped - parent.expectTerminated(scheduler) - } - - "asked to schedule activation for a past tick while inactive" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - - scheduler ! ScheduleActivation(agent1.ref, 900) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 900 - val schedulerActivation = sa1.actor - - // TICK 900 - schedulerActivation ! Activation(900) - - agent1.expectActivationAndComplete( - scheduler, - 900, - Some(1800), - ) - - parent.expectMessage(Completion(schedulerActivation, Some(1800))) - - // now inactive again - // can't schedule activation with earlier tick than last tick (900) -> error - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - // agent does not receive activation - agent1.expectNoMessage() - parent.expectNoMessage() - - // scheduler stopped - parent.expectTerminated(scheduler) - } - - "asked to schedule activation for a past tick while active" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - - scheduler ! ScheduleActivation(agent1.ref, 0) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 0 - val schedulerActivation = sa1.actor - - // TICK 0 - schedulerActivation ! Activation(0) - agent1.expectMessage(Activation(0)) - - // can't schedule activation for earlier tick than current active -> error - scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK) - - // agent does not receive activation - agent1.expectNoMessage() - parent.expectNoMessage() - - // scheduler stopped - parent.expectTerminated(scheduler) - } - - "asked to schedule activation for a past phase in the current tick" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_2") - - scheduler ! ScheduleActivation(agent1.ref, 0) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 0 - val schedulerActivation = sa1.actor - - scheduler ! ScheduleActivation(agent2.ref, 0) - - // TICK 0, phase 0 - schedulerActivation ! Activation(0) - agent1.expectMessage(Activation(0)) - - // TICK 0, phase 1 - scheduler ! Completion(agent1.ref) - agent2.expectMessage(Activation(0)) - - // schedule first actor again, this is not allowed - scheduler ! ScheduleActivation(agent1.ref, 0) - - // agent does not receive activation - agent1.expectNoMessage() - parent.expectNoMessage() - - // scheduler stopped - parent.expectTerminated(scheduler) - } - - "receiving unexpected completion message" in { - val parent = TestProbe[SchedulerMessage]("parent") - val scheduler = spawn( - Scheduler(parent.ref, PhaseSwitchCore) - ) - - val agent1 = TestProbe[Activation]("agent_1") - val agent2 = TestProbe[Activation]("agent_1") - - scheduler ! ScheduleActivation(agent1.ref, 0) - - val sa1 = parent.expectMessageType[ScheduleActivation] - sa1.tick shouldBe 0 - val schedulerActivation = sa1.actor - - // TICK 0 - schedulerActivation ! Activation(0) - agent1.expectMessage(Activation(0)) - - // receiving completion for wrong actor - scheduler ! Completion(agent2.ref) - - // parent does not receive completion - parent.expectNoMessage() - - // scheduler stopped - parent.expectTerminated(scheduler) - } - } -} diff --git a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala index 5eed45eb46..019a871763 100644 --- a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala +++ b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala @@ -6,9 +6,6 @@ package edu.ie3.simona.service.ev -import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps -import org.apache.pekko.actor.{ActorRef, ActorSystem} -import org.apache.pekko.testkit.{TestActorRef, TestProbe} import com.typesafe.config.ConfigFactory import edu.ie3.simona.api.data.ev.ExtEvData import edu.ie3.simona.api.data.ev.model.EvModel @@ -33,12 +30,16 @@ import edu.ie3.simona.test.common.{ } import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.util.quantities.PowerSystemUnits +import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.testkit.{TestActorRef, TestProbe} import org.scalatest.wordspec.AnyWordSpecLike import tech.units.indriya.quantity.Quantities import java.util.UUID import scala.concurrent.duration.DurationInt import scala.jdk.CollectionConverters._ +import scala.jdk.OptionConverters._ class ExtEvDataServiceSpec extends TestKitWithShutdown( @@ -55,15 +56,6 @@ class ExtEvDataServiceSpec with EvTestData with TestSpawnerClassic { - private val scheduler = TestProbe("scheduler") - private val extSimAdapter = TestProbe("extSimAdapter") - - private val extEvData = (dataService: ActorRef) => - new ExtEvData( - dataService, - extSimAdapter.ref, - ) - private val evcs1UUID = UUID.fromString("06a14909-366e-4e94-a593-1016e1455b30") private val evcs2UUID = @@ -71,7 +63,11 @@ class ExtEvDataServiceSpec "An uninitialized ev movement service" must { "send correct completion message after initialisation" in { + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -79,7 +75,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extEvData(evService)), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsg( ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key)) @@ -90,7 +86,11 @@ class ExtEvDataServiceSpec } "stash registration request and handle it correctly once initialized" in { + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val evcs1 = TestProbe("evcs1") @@ -106,7 +106,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extEvData(evService)), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsg( ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key)) @@ -114,15 +114,17 @@ class ExtEvDataServiceSpec scheduler.send(evService, Activation(INIT_SIM_TICK)) scheduler.expectMsg(Completion(evService.toTyped)) - - evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, None)) } } "An idle ev movements service" must { "handle duplicate registrations correctly" in { + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -130,7 +132,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extEvData(evService)), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -141,19 +143,35 @@ class ExtEvDataServiceSpec val evcs2 = TestProbe("evcs2") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, None)) + evcs1.expectNoMessage() evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID)) - evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, None)) + evcs2.expectNoMessage() // register first one again evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) evcs1.expectNoMessage() - evcs2.expectNoMessage() + + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) + + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) } "fail when activated without having received ExtEvMessage" in { + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -161,7 +179,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extEvData(evService)), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsg( ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key)) @@ -182,9 +200,11 @@ class ExtEvDataServiceSpec } "handle free lots requests correctly and forward them to the correct evcs" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -192,7 +212,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -203,12 +223,25 @@ class ExtEvDataServiceSpec val evcs2 = TestProbe("evcs2") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsgType[RegistrationSuccessfulMessage] + evcs1.expectNoMessage() evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID)) - evcs2.expectMsgType[RegistrationSuccessfulMessage] + evcs2.expectNoMessage() + + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) - extData.sendExtMsg( + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + + extEvData.sendExtMsg( new RequestEvcsFreeLots() ) @@ -231,7 +264,7 @@ class ExtEvDataServiceSpec scheduler.expectMsg(Completion(evService.toTyped)) - extData.receiveTriggerQueue shouldBe empty + extEvData.receiveTriggerQueue shouldBe empty // return free lots to ev service evcs1.send( @@ -243,7 +276,7 @@ class ExtEvDataServiceSpec ) // nothing should happen yet, waiting for second departed ev - extData.receiveTriggerQueue shouldBe empty + extEvData.receiveTriggerQueue shouldBe empty evcs2.send( evService, @@ -256,21 +289,23 @@ class ExtEvDataServiceSpec // ev service should recognize that all evcs that are expected are returned, // thus should send ProvideEvcsFreeLots awaitCond( - !extData.receiveTriggerQueue.isEmpty, + !extEvData.receiveTriggerQueue.isEmpty, max = 3.seconds, message = "No message received", ) - extData.receiveTriggerQueue.size() shouldBe 1 + extEvData.receiveTriggerQueue.size() shouldBe 1 // only evcs 1 should be included, the other one is full - extData.receiveTriggerQueue.take() shouldBe new ProvideEvcsFreeLots( + extEvData.receiveTriggerQueue.take() shouldBe new ProvideEvcsFreeLots( Map(evcs1UUID -> Integer.valueOf(2)).asJava ) } "return free lots requests right away if there are no evcs registered" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -278,14 +313,14 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] scheduler.send(evService, Activation(INIT_SIM_TICK)) scheduler.expectMsg(Completion(evService.toTyped)) - extData.sendExtMsg(new RequestEvcsFreeLots()) + extEvData.sendExtMsg(new RequestEvcsFreeLots()) // ev service should receive movements msg at this moment // scheduler receives schedule msg @@ -300,18 +335,20 @@ class ExtEvDataServiceSpec // ev service should send ProvideEvcsFreeLots right away awaitCond( - !extData.receiveTriggerQueue.isEmpty, + !extEvData.receiveTriggerQueue.isEmpty, max = 3.seconds, message = "No message received", ) - extData.receiveTriggerQueue.size() shouldBe 1 - extData.receiveTriggerQueue.take() shouldBe new ProvideEvcsFreeLots() + extEvData.receiveTriggerQueue.size() shouldBe 1 + extEvData.receiveTriggerQueue.take() shouldBe new ProvideEvcsFreeLots() } "handle ev departure requests correctly and return departed evs" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -319,7 +356,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -330,17 +367,30 @@ class ExtEvDataServiceSpec val evcs2 = TestProbe("evcs1") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsgType[RegistrationSuccessfulMessage] + evcs1.expectNoMessage() evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID)) - evcs2.expectMsgType[RegistrationSuccessfulMessage] + evcs2.expectNoMessage() + + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) + + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) val departures = Map( evcs1UUID -> List(evA.getUuid).asJava, evcs2UUID -> List(evB.getUuid).asJava, ).asJava - extData.sendExtMsg( + extEvData.sendExtMsg( new RequestDepartingEvs(departures) ) @@ -373,7 +423,7 @@ class ExtEvDataServiceSpec ) // nothing should happen yet, waiting for second departed ev - extData.receiveTriggerQueue shouldBe empty + extEvData.receiveTriggerQueue shouldBe empty val updatedEvB = evB.copyWith( Quantities.getQuantity(4.0, PowerSystemUnits.KILOWATTHOUR) @@ -387,20 +437,22 @@ class ExtEvDataServiceSpec // ev service should recognize that all evs that are expected are returned, // thus should send ProvideDepartingEvs awaitCond( - !extData.receiveTriggerQueue.isEmpty, + !extEvData.receiveTriggerQueue.isEmpty, max = 3.seconds, message = "No message received", ) - extData.receiveTriggerQueue.size() shouldBe 1 - extData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs( + extEvData.receiveTriggerQueue.size() shouldBe 1 + extEvData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs( List[EvModel](updatedEvA, updatedEvB).asJava ) } "return ev departure requests right away if request list is empty" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -408,14 +460,14 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] scheduler.send(evService, Activation(INIT_SIM_TICK)) scheduler.expectMsg(Completion(evService.toTyped)) - extData.sendExtMsg( + extEvData.sendExtMsg( new RequestDepartingEvs(Map.empty[UUID, java.util.List[UUID]].asJava) ) @@ -432,20 +484,22 @@ class ExtEvDataServiceSpec // ev service should send ProvideDepartingEvs right away awaitCond( - !extData.receiveTriggerQueue.isEmpty, + !extEvData.receiveTriggerQueue.isEmpty, max = 3.seconds, message = "No message received", ) - extData.receiveTriggerQueue.size() shouldBe 1 - extData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs( + extEvData.receiveTriggerQueue.size() shouldBe 1 + extEvData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs( List.empty[EvModel].asJava ) } "handle ev arrivals correctly and forward them to the correct evcs" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -453,7 +507,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -464,18 +518,31 @@ class ExtEvDataServiceSpec val evcs2 = TestProbe("evcs2") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsgType[RegistrationSuccessfulMessage] + evcs1.expectNoMessage() evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID)) - evcs2.expectMsgType[RegistrationSuccessfulMessage] + evcs2.expectNoMessage() + + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) + + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) val arrivals = Map( evcs1UUID -> List[EvModel](evA).asJava, evcs2UUID -> List[EvModel](evB).asJava, ).asJava - extData.sendExtMsg( - new ProvideArrivingEvs(arrivals) + extEvData.sendExtMsg( + new ProvideArrivingEvs(arrivals, None.toJava) ) // ev service should receive movements msg at this moment @@ -486,29 +553,31 @@ class ExtEvDataServiceSpec // we trigger ev service scheduler.send(evService, Activation(tick)) - // schedule lock is scheduled - scheduler.expectMsgType[ScheduleActivation].tick shouldBe tick val evsMessage1 = evcs1.expectMsgType[ProvideEvDataMessage] evsMessage1.tick shouldBe tick - evsMessage1.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evA))) - evsMessage1.unlockKey should not be empty + evsMessage1.data shouldBe ArrivingEvs( + Seq(EvModelWrapper(evA)) + ) val evsMessage2 = evcs2.expectMsgType[ProvideEvDataMessage] evsMessage2.tick shouldBe tick - evsMessage2.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evB))) - evsMessage2.unlockKey should not be empty + evsMessage2.data shouldBe ArrivingEvs( + Seq(EvModelWrapper(evB)) + ) scheduler.expectMsg(Completion(evService.toTyped)) // no response expected - extData.receiveTriggerQueue shouldBe empty + extEvData.receiveTriggerQueue shouldBe empty } "skip a movements provision from an evcs that is not registered" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -516,7 +585,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -526,15 +595,27 @@ class ExtEvDataServiceSpec val evcs1 = TestProbe("evcs1") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsgType[RegistrationSuccessfulMessage] + evcs1.expectNoMessage() + + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) + + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) val arrivals = Map( evcs1UUID -> List[EvModel](evA).asJava, evcs2UUID -> List[EvModel](evB).asJava, ).asJava - extData.sendExtMsg( - new ProvideArrivingEvs(arrivals) + extEvData.sendExtMsg( + new ProvideArrivingEvs(arrivals, None.toJava) ) // ev service should receive movements msg at this moment @@ -545,18 +626,17 @@ class ExtEvDataServiceSpec // we trigger ev service scheduler.send(evService, Activation(tick)) - // schedule lock is scheduled - scheduler.expectMsgType[ScheduleActivation].tick shouldBe tick val evsMessage1 = evcs1.expectMsgType[ProvideEvDataMessage] evsMessage1.tick shouldBe tick - evsMessage1.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evA))) - evsMessage1.unlockKey should not be empty + evsMessage1.data shouldBe ArrivingEvs( + Seq(EvModelWrapper(evA)) + ) scheduler.expectMsg(Completion(evService.toTyped)) // no response expected - extData.receiveTriggerQueue shouldBe empty + extEvData.receiveTriggerQueue shouldBe empty } } } diff --git a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala index 1ae6457b8e..db7e9abdea 100644 --- a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala +++ b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala @@ -251,13 +251,11 @@ class PrimaryServiceWorkerSpec actualServiceRef, actualData, actualNextDataTick, - unlockKey, ) => actualTick shouldBe 0L actualServiceRef shouldBe serviceRef actualData shouldBe primaryData actualNextDataTick shouldBe Some(900L) - unlockKey shouldBe None } } @@ -356,7 +354,6 @@ class PrimaryServiceWorkerSpec actualServiceRef, data, nextDataTick, - unlockKey, ) => tick shouldBe 900L actualServiceRef shouldBe serviceRef @@ -366,7 +363,6 @@ class PrimaryServiceWorkerSpec case _ => fail("Expected to get active power only.") } nextDataTick shouldBe None - unlockKey shouldBe None } } } diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala index 8a88b2163f..59aaaea924 100644 --- a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala @@ -68,7 +68,7 @@ class SimonaSimSpec extends ScalaTestWithActorTestKit with UnitSpec { forwardMessage(Some(extSimAdapter.ref)), uniqueName("extSimAdapterForwarder"), ) - ExtSimSetupData(Iterable(extSim.toClassic), Map.empty, None) + ExtSimSetupData(Iterable(extSim.toClassic), Map.empty) } } ), @@ -453,6 +453,6 @@ object SimonaSimSpec { context: ActorContext[_], scheduler: ActorRef[SchedulerMessage], ): ExtSimSetupData = - ExtSimSetupData(Iterable.empty, Map.empty, None) + ExtSimSetupData(Iterable.empty, Map.empty) } } From b8620355f2699d6829b4b96f7615efcce3c0e36a Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 21 Jun 2024 14:07:58 +0200 Subject: [PATCH 10/39] Adapting changelog to changed issue Signed-off-by: Sebastian Peter --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1873b9d44..6a5ca87d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactoring of `GridAgent` messages [#736](https://github.com/ie3-institute/simona/issues/736) - Rewrote PVModelTest from groovy to scala [#646](https://github.com/ie3-institute/simona/issues/646) - Making configuration of `RefSystem` via config optional [#769](https://github.com/ie3-institute/simona/issues/769) -- External simulation should provide detailed information about next tick per agent [#776](https://github.com/ie3-institute/simona/issues/776) +- External simulation should provide information about next tick of MobSim [#776](https://github.com/ie3-institute/simona/issues/776) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) From 8164faf6599c851f6deef2752cc8d3ab28d5e38c Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 9 Jul 2024 14:47:57 +0200 Subject: [PATCH 11/39] Update gitignore --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7cf28aed43..4e7c9b7103 100644 --- a/.gitignore +++ b/.gitignore @@ -201,7 +201,13 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.gitignore.io/api/java,macos,windows,eclipse +# Metals +.metals/ +.bloop/ +project/**/metals.sbt + + +# End of https://www.gitignore.io/api/java,macos,windows,eclipse, metals /target/ From cba5bb565fddfef0c42696903e2c4aa062af7346 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 9 Jul 2024 14:49:56 +0200 Subject: [PATCH 12/39] Handle multiple closed switches at node --- .../edu/ie3/simona/model/grid/GridModel.scala | 102 ++++++++++-------- .../edu/ie3/simona/model/grid/GridSpec.scala | 29 +++-- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala index f2eab5ff1d..6f96da8fff 100644 --- a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala +++ b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala @@ -29,7 +29,6 @@ import org.jgrapht.graph.{DefaultEdge, SimpleGraph} import java.time.ZonedDateTime import java.util.UUID -import scala.collection.immutable.ListSet import scala.jdk.CollectionConverters._ /** Representation of one physical electrical grid. It holds the references to @@ -440,19 +439,6 @@ object GridModel { throw new InvalidGridException( s"The grid model for subnet ${gridModel.subnetNo} has multiple nodes with the same name!" ) - - // multiple switches @ one node -> not supported yet! - val switchVector = gridModel.gridComponents.switches.foldLeft( - Vector.empty[UUID] - )((vector, switch) => (vector :+ switch.nodeAUuid) :+ switch.nodeBUuid) - val uniqueSwitchNodeIds = switchVector.toSet.toList - if (switchVector.diff(uniqueSwitchNodeIds).nonEmpty) { - throw new InvalidGridException( - s"The grid model for subnet ${gridModel.subnetNo} has nodes with multiple switches. This is not supported yet! Duplicates are located @ nodes: ${switchVector - .diff(uniqueSwitchNodeIds)}" - ) - } - } /** Checks all ControlGroups if a) Transformer of ControlGroup and Measurement @@ -646,40 +632,66 @@ object GridModel { def updateUuidToIndexMap(gridModel: GridModel): Unit = { val switches = gridModel.gridComponents.switches - val nodes = gridModel.gridComponents.nodes - - val nodesAndSwitches: ListSet[SystemComponent] = ListSet - .empty[SystemComponent] ++ switches ++ nodes - - val updatedNodeToUuidMap = nodesAndSwitches - .filter(_.isInOperation) - .filter { - case switch: SwitchModel => switch.isClosed - case _: NodeModel => true + val nodes = gridModel.gridComponents.nodes.distinct + + val nodeConnections: Map[UUID, Set[UUID]] = + switches.filter(_.isClosed).foldLeft(Map.empty[UUID, Set[UUID]]) { + (acc, switch) => + acc + .updated( + switch.nodeAUuid, + acc.getOrElse(switch.nodeAUuid, Set()) + switch.nodeBUuid, + ) + .updated( + switch.nodeBUuid, + acc.getOrElse(switch.nodeBUuid, Set()) + switch.nodeAUuid, + ) } - .zipWithIndex - .foldLeft(Map.empty[UUID, Int]) { - case (map, (gridComponent, componentId)) => - gridComponent match { - case switchModel: SwitchModel => - map ++ Map( - switchModel.nodeAUuid -> componentId, - switchModel.nodeBUuid -> componentId, - ) - - case nodeModel: NodeModel => - if (!map.contains(nodeModel.uuid)) { - val idx = map.values.toList.sorted.lastOption - .getOrElse( - -1 - ) + 1 // if we didn't found anything in the list, we don't have switches and want to start @ 0 - map + (nodeModel.uuid -> idx) - } else { - map - } - } + val switchConnectedNodes = + findConnectedNodes(nodeConnections).zipWithIndex.flatMap { + case (nodes, int) => nodes.map(n => n -> int) + }.toMap + var offset = switchConnectedNodes.values.maxOption.getOrElse(-1) + val updatedNodeToUuidMap = nodes + .filter(_.isInOperation) + .foldLeft(Map.empty[UUID, Int]) { case (map, nodeModel) => + switchConnectedNodes.get(nodeModel.uuid) match { + case Some(idx) => map + (nodeModel.uuid -> idx) + case None => + offset += 1 + map + (nodeModel.uuid -> offset) + } } gridModel._nodeUuidToIndexMap = updatedNodeToUuidMap } + + private def findConnectedNodes( + nodeConnections: Map[UUID, Set[UUID]] + ): Seq[Seq[UUID]] = { + val visited = scala.collection.mutable.Set[UUID]() + var components = Seq[Seq[UUID]]() + + def dfs( + node: UUID, + component: scala.collection.mutable.ListBuffer[UUID], + ): Unit = { + visited += node + component += node + for (neighbor <- nodeConnections.getOrElse(node, Set())) { + if (!visited.contains(neighbor)) { + dfs(neighbor, component) + } + } + } + + for (node <- nodeConnections.keys) { + if (!visited.contains(node)) { + val component = scala.collection.mutable.ListBuffer[UUID]() + dfs(node, component) + components = components :+ component.toList + } + } + components + } } diff --git a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala index dc4fab6ef2..e78d0def7f 100644 --- a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala @@ -9,12 +9,14 @@ package edu.ie3.simona.model.grid import breeze.linalg.DenseMatrix import breeze.math.Complex import breeze.numerics.abs -import edu.ie3.datamodel.exceptions.InvalidGridException import edu.ie3.datamodel.models.input.MeasurementUnitInput import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils import edu.ie3.simona.exceptions.GridInconsistencyException import edu.ie3.simona.model.control.{GridControls, TransformerControlGroupModel} -import edu.ie3.simona.model.grid.GridModel.GridComponents +import edu.ie3.simona.model.grid.GridModel.{ + GridComponents, + updateUuidToIndexMap, +} import edu.ie3.simona.test.common.input.{GridInputTestData, LineInputTestData} import edu.ie3.simona.test.common.model.grid.{ BasicGrid, @@ -265,7 +267,7 @@ class GridSpec } - "throw an InvalidGridException if two switches are connected @ the same node" in new BasicGridWithSwitches { + "Correctly handle multiple closed switches at node" in new BasicGridWithSwitches { // enable nodes override val nodes: Seq[NodeModel] = super.nodes nodes.foreach(_.enable()) @@ -282,7 +284,7 @@ class GridSpec override val switches: Set[SwitchModel] = super.switches + secondSwitch switches.foreach(_.enable()) // open the switches - switches.foreach(_.open()) + switches.foreach(_.close()) // get the grid from the raw data val gridModel = new GridModel( @@ -298,18 +300,15 @@ class GridSpec GridControls.empty, ) - // get the private method for validation - val validateConsistency: PrivateMethod[Unit] = - PrivateMethod[Unit](Symbol("validateConsistency")) - - // call the validation method - val exception: InvalidGridException = intercept[InvalidGridException] { - GridModel invokePrivate validateConsistency(gridModel) - } - - // expect an exception for node 13 - exception.getMessage shouldBe s"The grid model for subnet 1 has nodes with multiple switches. This is not supported yet! Duplicates are located @ nodes: Vector(${node13.uuid})" + updateUuidToIndexMap(gridModel) + // nodes 1, 13 and 14 should map to the same node + gridModel.nodeUuidToIndexMap + .get(node1.uuid) + .value shouldBe gridModel.nodeUuidToIndexMap.get(node13.uuid).value + gridModel.nodeUuidToIndexMap + .get(node1.uuid) + .value shouldBe gridModel.nodeUuidToIndexMap.get(node14.uuid).value } "update the nodeUuidToIndexMap correctly, when the given grid" must { From a259a7beb37af2b7596a6e3afae77248fe221df3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 10 Jul 2024 15:31:10 +0200 Subject: [PATCH 13/39] Address SonarQube hints --- input/samples/vn_simona/vn_simona.conf | 8 ++++---- .../scala/edu/ie3/simona/model/grid/GridModel.scala | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf index 55c14f26a2..ef18c3b589 100644 --- a/input/samples/vn_simona/vn_simona.conf +++ b/input/samples/vn_simona/vn_simona.conf @@ -203,12 +203,12 @@ simona.control.transformer = [ { transformers = ["31a2b9bf-e785-4475-aa44-1c34646e8c79"], measurements = ["923f2d69-3093-4198-86e4-13d2d1c220f8"], - vMin = 0.98, - vMax = 1.02 + vmin = 0.98, + vmax = 1.02 }, { transformers = ["1132dbf4-e8a1-44ae-8415-f42d4497aa1d"], measurements = ["7686b818-a0ba-465c-8e4e-f7d3c4e171fc"], - vMin = 0.98, - vMax = 1.02 + vmin = 0.98, + vmax = 1.02 } ] diff --git a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala index 6f96da8fff..0591b30823 100644 --- a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala +++ b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala @@ -640,11 +640,11 @@ object GridModel { acc .updated( switch.nodeAUuid, - acc.getOrElse(switch.nodeAUuid, Set()) + switch.nodeBUuid, + acc.getOrElse(switch.nodeAUuid, Set.empty) + switch.nodeBUuid, ) .updated( switch.nodeBUuid, - acc.getOrElse(switch.nodeBUuid, Set()) + switch.nodeAUuid, + acc.getOrElse(switch.nodeBUuid, Set.empty) + switch.nodeAUuid, ) } val switchConnectedNodes = @@ -670,7 +670,7 @@ object GridModel { nodeConnections: Map[UUID, Set[UUID]] ): Seq[Seq[UUID]] = { val visited = scala.collection.mutable.Set[UUID]() - var components = Seq[Seq[UUID]]() + var components = Seq.empty[Seq[UUID]] def dfs( node: UUID, @@ -678,7 +678,7 @@ object GridModel { ): Unit = { visited += node component += node - for (neighbor <- nodeConnections.getOrElse(node, Set())) { + for (neighbor <- nodeConnections.getOrElse(node, Set.empty)) { if (!visited.contains(neighbor)) { dfs(neighbor, component) } @@ -689,7 +689,7 @@ object GridModel { if (!visited.contains(node)) { val component = scala.collection.mutable.ListBuffer[UUID]() dfs(node, component) - components = components :+ component.toList + components = components :+ component.toSeq } } components From b2b77427e4accc8bb61b375e9f7fe92306ba3cd1 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 11 Jul 2024 14:08:31 +0200 Subject: [PATCH 14/39] Roll back conf changes --- input/samples/vn_simona/vn_simona.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf index ef18c3b589..55c14f26a2 100644 --- a/input/samples/vn_simona/vn_simona.conf +++ b/input/samples/vn_simona/vn_simona.conf @@ -203,12 +203,12 @@ simona.control.transformer = [ { transformers = ["31a2b9bf-e785-4475-aa44-1c34646e8c79"], measurements = ["923f2d69-3093-4198-86e4-13d2d1c220f8"], - vmin = 0.98, - vmax = 1.02 + vMin = 0.98, + vMax = 1.02 }, { transformers = ["1132dbf4-e8a1-44ae-8415-f42d4497aa1d"], measurements = ["7686b818-a0ba-465c-8e4e-f7d3c4e171fc"], - vmin = 0.98, - vmax = 1.02 + vMin = 0.98, + vMax = 1.02 } ] From 500bc4d3edadedb9b99de7320b82aecf2fe803dc Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 24 Jul 2024 11:48:28 +0200 Subject: [PATCH 15/39] Providing test for empty arrivals Signed-off-by: Sebastian Peter --- .../evcs/EvcsAgentFundamentals.scala | 3 +- .../EvcsAgentModelCalculationSpec.scala | 71 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala index 6e7ea8f3d4..a023227083 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala @@ -485,7 +485,6 @@ protected trait EvcsAgentFundamentals val relevantData = createCalcRelevantData(modelBaseStateData, tick) - // TODO also adapt for em-controlled and test val updatedBaseStateData = { if (relevantData.arrivals.nonEmpty) { val lastState = getLastOrInitialStateData(modelBaseStateData, tick) @@ -510,7 +509,7 @@ protected trait EvcsAgentFundamentals newState, ) - /* Update the base state data with the updated result value store and relevant data store */ + /* Update the base state data with the updated state data store */ modelBaseStateData.copy( stateDataStore = updatedStateDataStore ) 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 e6154e8db9..8a81d51d5a 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala @@ -767,7 +767,7 @@ class EvcsAgentModelCalculationSpec evService.send( evcsAgent, ProvideEvDataMessage( - 0L, + 0, evService.ref, ArrivingEvs(Seq(EvModelWrapper(evA))), Some(900), @@ -797,6 +797,75 @@ class EvcsAgentModelCalculationSpec scheduler.expectNoMessage() } + "handle empty arrivals" in { + val evcsAgent = TestFSMRef( + new EvcsAgent( + scheduler = scheduler.ref, + initStateData = initStateData, + listener = Iterable.empty, + ) + ) + + scheduler.send( + evcsAgent, + Activation(INIT_SIM_TICK), + ) + + /* Refuse registration with primary service */ + primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage] + primaryServiceProxy.send( + evcsAgent, + RegistrationFailedMessage(primaryServiceProxy.ref), + ) + + /* I'm not interested in the content of the RegistrationMessage */ + evService.expectMsgType[RegisterForEvDataMessage] + evService.send( + evcsAgent, + RegistrationSuccessfulMessage(evService.ref, Some(0)), + ) + + /* I'm not interested in the content of the CompletionMessage */ + scheduler.expectMsgType[Completion] + awaitAssert(evcsAgent.stateName shouldBe Idle) + + /* Send ev for this tick */ + evService.send( + evcsAgent, + ProvideEvDataMessage( + 0, + evService.ref, + ArrivingEvs(Seq(EvModelWrapper(evA))), + Some(900), + ), + ) + + scheduler.send( + evcsAgent, + Activation(0), + ) + scheduler.expectMsg(Completion(evcsAgent.toTyped, Some(900))) + + /* Send empty EV list for this tick */ + evService.send( + evcsAgent, + ProvideEvDataMessage( + 900, + evService.ref, + ArrivingEvs(Seq.empty), + Some(1800), + ), + ) + + scheduler.send( + evcsAgent, + Activation(900), + ) + scheduler.expectMsg(Completion(evcsAgent.toTyped, Some(1800))) + + scheduler.expectNoMessage() + } + val evcsAgent = TestFSMRef( new EvcsAgent( scheduler = scheduler.ref, From 2d32cf3ade1854d721cb74b4de0238dae964d145 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Thu, 8 Aug 2024 13:13:51 +0200 Subject: [PATCH 16/39] Adapt to changed ev protocol Signed-off-by: Sebastian Peter --- .../service/ev/ExtEvDataServiceSpec.scala | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala index ff8310e679..8e6cf9e07d 100644 --- a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala +++ b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala @@ -301,9 +301,11 @@ class ExtEvDataServiceSpec } "handle price requests correctly by returning dummy values" in { - val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val scheduler = TestProbe("scheduler") + val extSimAdapter = TestProbe("extSimAdapter") - val extData = extEvData(evService) + val evService = TestActorRef(new ExtEvDataService(scheduler.ref)) + val extEvData = new ExtEvData(evService, extSimAdapter.ref) val key = ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) @@ -311,7 +313,7 @@ class ExtEvDataServiceSpec scheduler.send( evService, - SimonaService.Create(InitExtEvData(extData), key), + SimonaService.Create(InitExtEvData(extEvData), key), ) scheduler.expectMsgType[ScheduleActivation] @@ -322,12 +324,25 @@ class ExtEvDataServiceSpec val evcs2 = TestProbe("evcs2") evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID)) - evcs1.expectMsgType[RegistrationSuccessfulMessage] + evcs1.expectNoMessage() evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID)) - evcs2.expectMsgType[RegistrationSuccessfulMessage] + evcs2.expectNoMessage() - extData.sendExtMsg(new RequestCurrentPrices()) + extEvData.sendExtMsg( + new ProvideArrivingEvs( + Map.empty[UUID, java.util.List[EvModel]].asJava, + Some(long2Long(0L)).toJava, + ) + ) + extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService)) + scheduler.send(evService, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(evService.toTyped)) + + evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, Some(0L))) + + extEvData.sendExtMsg(new RequestCurrentPrices()) // ev service should receive request at this moment // scheduler should receive schedule msg @@ -344,13 +359,13 @@ class ExtEvDataServiceSpec // ev service should recognize that all evcs that are expected are returned, // thus should send ProvideEvcsFreeLots awaitCond( - !extData.receiveTriggerQueue.isEmpty, + !extEvData.receiveTriggerQueue.isEmpty, max = 3.seconds, message = "No message received", ) - extData.receiveTriggerQueue.size() shouldBe 1 + extEvData.receiveTriggerQueue.size() shouldBe 1 // only evcs 1 should be included, the other one is full - extData.receiveTriggerQueue.take() shouldBe new ProvideCurrentPrices( + extEvData.receiveTriggerQueue.take() shouldBe new ProvideCurrentPrices( Map( evcs1UUID -> double2Double(0d), evcs2UUID -> double2Double(0d), From a5753d57db499885ee07fc109d5650ef98a93c61 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 9 Aug 2024 11:19:29 +0200 Subject: [PATCH 17/39] Handling requests also in case activation has already arrived Signed-off-by: Sebastian Peter --- .../agent/participant/evcs/EvcsAgent.scala | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala index b2c428242d..c00b76bbf1 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala @@ -14,13 +14,18 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{ import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData -import edu.ie3.simona.agent.participant.statedata.ParticipantStateData +import edu.ie3.simona.agent.participant.statedata.{ + BaseStateData, + DataCollectionStateData, + ParticipantStateData, +} import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData import edu.ie3.simona.agent.participant.{ ParticipantAgent, ParticipantAgentFundamentals, } import edu.ie3.simona.agent.state.AgentState.Idle +import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation import edu.ie3.simona.config.SimonaConfig.EvcsRuntimeConfig import edu.ie3.simona.model.participant.evcs.EvcsModel import edu.ie3.simona.model.participant.evcs.EvcsModel.{ @@ -105,6 +110,50 @@ class EvcsAgent( stay() using updatedStateData } + when(HandleInformation) { + // FreeLotsRequest and DepartingEvsRequest also need to be handled + // in case the activation has arrived first + + case Event( + EvFreeLotsRequest(tick), + stateData: DataCollectionStateData[ApparentPower], + ) => + stateData.baseStateData match { + case modelStateData: BaseStateData.ParticipantModelBaseStateData[ + ApparentPower, + EvcsRelevantData, + EvcsState, + EvcsModel, + ] => + handleFreeLotsRequest(tick, modelStateData) + stay() + case x => + throw new IllegalStateException( + s"Unsupported base state data '$x' when receiving FreeLotsRequest" + ) + } + + case Event( + DepartingEvsRequest(tick, departingEvs), + stateData: DataCollectionStateData[ApparentPower], + ) => + stateData.baseStateData match { + case modelStateData: BaseStateData.ParticipantModelBaseStateData[ + ApparentPower, + EvcsRelevantData, + EvcsState, + EvcsModel, + ] => + val updatedStateData = + handleDepartingEvsRequest(tick, departingEvs, modelStateData) + stay() using stateData.copy(baseStateData = updatedStateData) + case x => + throw new IllegalStateException( + s"Unsupported base state data '$x' when receiving DepartingEvsRequest" + ) + } + } + /** Determine the average result within the given tick window * * @param tickToResults From 1da5700dbd11f04fe2d5f740e380551cceea07fc Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 9 Aug 2024 15:51:45 +0200 Subject: [PATCH 18/39] Explanatory commentary Signed-off-by: Sebastian Peter --- src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala index aa0cc5d217..66cfaa96f1 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -146,6 +146,8 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData.uuidToActorRef.get(evcs) match { case None => // Actor is not registered yet + // (not sending confirmation message yet, because we're waiting + // for MobSim to tell us what the first tick is going to be) serviceStateData.copy( uuidToActorRef = serviceStateData.uuidToActorRef + (evcs -> agentToBeRegistered) From 2b40f2eee3051a472a7b6678bc513653c86e0cc2 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 9 Aug 2024 17:18:17 +0200 Subject: [PATCH 19/39] Updating simonaAPI to release version 0.5 Signed-off-by: Sebastian Peter --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d173660f3b..88e8998076 100644 --- a/build.gradle +++ b/build.gradle @@ -89,7 +89,7 @@ dependencies { exclude group: 'edu.ie3' } - implementation('com.github.ie3-institute:simonaAPI:0.5-SNAPSHOT') { + implementation('com.github.ie3-institute:simonaAPI:0.5.0') { exclude group: 'org.apache.logging.log4j' exclude group: 'org.slf4j' /* Exclude our own nested dependencies */ From 1f0a8857923d0aeccb4e0721466861becf228597 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Mon, 12 Aug 2024 14:00:29 +0200 Subject: [PATCH 20/39] Refactoring old EvMovement classes Signed-off-by: Sebastian Peter --- .../agent/grid/GridAgentController.scala | 4 ++-- .../participant/ServiceRegistration.scala | 8 ++++---- .../data/secondary/SecondaryDataService.scala | 2 +- .../agent/participant/evcs/EvcsAgent.scala | 4 ++-- .../evcs/EvcsAgentFundamentals.scala | 6 +++--- .../EvcsAgentModelCalculationSpec.scala | 20 +++++++++---------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala index 393e7417e7..f953165ede 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala @@ -14,7 +14,7 @@ import edu.ie3.simona.agent.EnvironmentRefs import edu.ie3.simona.agent.em.EmAgent import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{ - ActorEvMovementsService, + ActorExtEvDataService, ActorWeatherService, } import edu.ie3.simona.agent.participant.evcs.EvcsAgent @@ -666,7 +666,7 @@ class GridAgentController( modelConfiguration, primaryServiceProxy, Iterable( - ActorEvMovementsService( + ActorExtEvDataService( evMovementsService ) ), diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala index b8b2ef33ad..5308b4e768 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala @@ -12,7 +12,7 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.PrimaryDataWithApp import edu.ie3.simona.agent.participant.data.Data.SecondaryData import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{ - ActorEvMovementsService, + ActorExtEvDataService, ActorPriceService, ActorWeatherService, } @@ -83,8 +83,8 @@ trait ServiceRegistration[ case ActorWeatherService(serviceRef) => registerForWeather(serviceRef, inputModel) Some(serviceRef) - case ActorEvMovementsService(serviceRef) => - registerForEvMovements(serviceRef, inputModel) + case ActorExtEvDataService(serviceRef) => + registerForEvData(serviceRef, inputModel) Some(serviceRef) } @@ -124,7 +124,7 @@ trait ServiceRegistration[ * Input model of the simulation mode * @return */ - private def registerForEvMovements( + private def registerForEvData( serviceRef: ActorRef, inputModel: I, ): Unit = { diff --git a/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala b/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala index 7d2646b87b..ebac2ad136 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala @@ -24,6 +24,6 @@ object SecondaryDataService { final case class ActorWeatherService(override val actorRef: ActorRef) extends SecondaryDataService[WeatherData] - final case class ActorEvMovementsService(override val actorRef: ActorRef) + final case class ActorExtEvDataService(override val actorRef: ActorRef) extends SecondaryDataService[EvData] } diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala index c00b76bbf1..cdab067fa7 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala @@ -12,7 +12,7 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{ ZERO_POWER, } import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService -import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorExtEvDataService import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData import edu.ie3.simona.agent.participant.statedata.{ BaseStateData, @@ -59,7 +59,7 @@ object EvcsAgent { ) val neededServices: Vector[Class[_ <: SecondaryDataService[_]]] = Vector( - classOf[ActorEvMovementsService] + classOf[ActorExtEvDataService] ) } diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala index 3421b7d9e8..d98e837e48 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala @@ -19,7 +19,7 @@ import edu.ie3.simona.agent.participant.ParticipantAgentFundamentals import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower import edu.ie3.simona.agent.participant.data.Data.SecondaryData import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService -import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorExtEvDataService import edu.ie3.simona.agent.participant.evcs.EvcsAgent.neededServices import edu.ie3.simona.agent.participant.statedata.BaseStateData.{ FlexControlledData, @@ -359,7 +359,7 @@ protected trait EvcsAgentFundamentals EvcsModel, ], ): Unit = { - val evServiceRef = getService[ActorEvMovementsService]( + val evServiceRef = getService[ActorExtEvDataService]( modelBaseStateData.services ) @@ -398,7 +398,7 @@ protected trait EvcsAgentFundamentals EvcsState, EvcsModel, ] = { - val evServiceRef = getService[ActorEvMovementsService]( + val evServiceRef = getService[ActorExtEvDataService]( baseStateData.services ) 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 8a81d51d5a..3e3a9c7a2c 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala @@ -17,7 +17,7 @@ import edu.ie3.simona.agent.grid.GridAgentMessages.{ } import edu.ie3.simona.agent.participant.ParticipantAgent.RequestAssetPowerMessage import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower -import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorExtEvDataService import edu.ie3.simona.agent.participant.evcs.EvcsAgent import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData import edu.ie3.simona.agent.participant.statedata.DataCollectionStateData @@ -186,7 +186,7 @@ class EvcsAgentModelCalculationSpec inputModel = evcsInputModel, modelConfig = modelConfig, secondaryDataServices = Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ), simulationStartDate = simulationStartDate, simulationEndDate = simulationEndDate, @@ -248,7 +248,7 @@ class EvcsAgentModelCalculationSpec inputModel shouldBe SimpleInputContainer(evcsInputModel) modelConfig shouldBe modelConfig secondaryDataServices shouldBe Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ) simulationStartDate shouldBe simulationStartDate simulationEndDate shouldBe simulationEndDate @@ -296,7 +296,7 @@ class EvcsAgentModelCalculationSpec startDate shouldBe simulationStartDate endDate shouldBe simulationEndDate services shouldBe Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ) outputConfig shouldBe NotifierConfig( simulationResultInfo = false, @@ -873,7 +873,7 @@ class EvcsAgentModelCalculationSpec inputModel = evcsInputModelQv, modelConfig = modelConfig, secondaryDataServices = Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ), simulationStartDate = simulationStartDate, simulationEndDate = simulationEndDate, @@ -1055,7 +1055,7 @@ class EvcsAgentModelCalculationSpec inputModel = evcsInputModelQv, modelConfig = modelConfig, secondaryDataServices = Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ), simulationStartDate = simulationStartDate, simulationEndDate = simulationEndDate, @@ -1092,7 +1092,7 @@ class EvcsAgentModelCalculationSpec inputModel shouldBe SimpleInputContainer(evcsInputModelQv) modelConfig shouldBe modelConfig secondaryDataServices shouldBe Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ) simulationStartDate shouldBe simulationStartDate simulationEndDate shouldBe simulationEndDate @@ -1155,7 +1155,7 @@ class EvcsAgentModelCalculationSpec startDate shouldBe simulationStartDate endDate shouldBe simulationEndDate services shouldBe Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ) outputConfig shouldBe defaultOutputConfig additionalActivationTicks shouldBe empty @@ -1189,7 +1189,7 @@ class EvcsAgentModelCalculationSpec inputModel = SimpleInputContainer(evcsInputModelQv), modelConfig = modelConfig, secondaryDataServices = Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ), simulationStartDate = simulationStartDate, simulationEndDate = simulationEndDate, @@ -1230,7 +1230,7 @@ class EvcsAgentModelCalculationSpec inputModel shouldBe SimpleInputContainer(evcsInputModelQv) modelConfig shouldBe modelConfig secondaryDataServices shouldBe Iterable( - ActorEvMovementsService(evService.ref) + ActorExtEvDataService(evService.ref) ) simulationStartDate shouldBe simulationStartDate simulationEndDate shouldBe simulationEndDate From 22450d0782cd3899a6ece14d33003f6d9edbc3e9 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Mon, 12 Aug 2024 14:59:38 +0200 Subject: [PATCH 21/39] Updating UML documentation related to mobility simulation (class diagram needs some more work) Signed-off-by: Sebastian Peter --- docs/uml/main/ExtEvSimulationClasses.puml | 34 +-- .../uml/protocol/ExtEvSimulationSequence.puml | 217 +++++++++++++----- 2 files changed, 178 insertions(+), 73 deletions(-) diff --git a/docs/uml/main/ExtEvSimulationClasses.puml b/docs/uml/main/ExtEvSimulationClasses.puml index e35c8e130a..9543697f03 100644 --- a/docs/uml/main/ExtEvSimulationClasses.puml +++ b/docs/uml/main/ExtEvSimulationClasses.puml @@ -27,7 +27,7 @@ package ev-simulation { package simona-api { ' MIDDLE PART class ExtEvData { - ~ LinkedBlockingQueue receiveTriggerQueue + ~ LinkedBlockingQueue receiveTriggerQueue - ActorRef dataService - ActorRef extSimAdapter + List requestAvailablePublicEvCs() @@ -119,33 +119,33 @@ package simona-api { EvModelImpl --|> EvModel - interface ExtEvMessage + interface EvDataMessageFromExt class EvMovementsMessage { - Map> movements } class RequestEvcsFreeLots - RequestEvcsFreeLots --|> ExtEvMessage - EvMovementsMessage --|> ExtEvMessage + RequestEvcsFreeLots --|> EvDataMessageFromExt + EvMovementsMessage --|> EvDataMessageFromExt RequestEvcsFreeLots -[hidden]> EvMovementsMessage - interface ExtEvResponseMessage + interface EvDataResponseMessageToExt class AllDepartedEvsRepsonse { - Map> movements } class ProvideEvcsFreeLots - ProvideEvcsFreeLots --|> ExtEvResponseMessage - AllDepartedEvsRepsonse --|> ExtEvResponseMessage - ExtEvData -> ExtEvMessage - ExtEvData -> ExtEvResponseMessage + ProvideEvcsFreeLots --|> EvDataResponseMessageToExt + AllDepartedEvsRepsonse --|> EvDataResponseMessageToExt + ExtEvData -> EvDataMessageFromExt + ExtEvData -> EvDataResponseMessageToExt EvMovement -[hidden]-> RequestEvcsFreeLots - ExtEvMessage -[hidden]> ExtEvResponseMessage + EvDataMessageFromExt -[hidden]> EvDataResponseMessageToExt EvMovementsMessage -[hidden]> ProvideEvcsFreeLots ProvideEvcsFreeLots -[hidden]> AllDepartedEvsRepsonse class ScheduleDataServiceMessage { - ExtEvDataService dataService } - ExtEvResponseMessage -[hidden]> ScheduleDataServiceMessage + EvDataResponseMessageToExt -[hidden]> ScheduleDataServiceMessage ExtEvData -> ScheduleDataServiceMessage @@ -158,10 +158,10 @@ package simona-api { ExtLink --|> ExtLinkInterface interface ExtTrigger - class ActivityStartTrigger { + class ActivationMessage { - Long tick } - ActivityStartTrigger --|> ExtTrigger + ActivationMessage --|> ExtTrigger interface ExtTriggerResponse class CompletionMessage { @@ -188,7 +188,7 @@ package simona { class SimonaSim - class SimScheduler + class Scheduler class SimonaStandaloneSetup @@ -198,12 +198,12 @@ package simona { class ExtEvDataService - SimScheduler -- SimonaSim + Scheduler -- SimonaSim SimonaSim *- SimonaStandaloneSetup SimonaStandaloneSetup *- ExtSimLoader - ExtSimAdapter -- SimScheduler - ExtEvDataService -- SimScheduler + ExtSimAdapter -- Scheduler + ExtEvDataService -- Scheduler SecondaryData <|-- EvMovementData diff --git a/docs/uml/protocol/ExtEvSimulationSequence.puml b/docs/uml/protocol/ExtEvSimulationSequence.puml index 470d49981e..954da645af 100644 --- a/docs/uml/protocol/ExtEvSimulationSequence.puml +++ b/docs/uml/protocol/ExtEvSimulationSequence.puml @@ -2,97 +2,202 @@ !theme plain +participant Scheduler +participant ExtSimAdapter +participant ExtSimulation +participant ExtEvDataService +participant EvcsAgent1 +participant EvcsAgent2 + ==Init== -SimScheduler -> ExtSimAdapter: ! ActivityStartTrigger(-1L) +Scheduler -> EvcsAgent1: ! Activation(-1) +activate EvcsAgent1 + +EvcsAgent1 -> ExtEvDataService: ! RegisterForEvDataMessage +deactivate EvcsAgent1 + +Scheduler -> EvcsAgent2: ! Activation(-1) +activate EvcsAgent2 + +EvcsAgent2 -> ExtEvDataService: ! RegisterForEvDataMessage +deactivate EvcsAgent2 + +Scheduler -> ExtSimAdapter: ! Activation(-1) activate ExtSimAdapter -ExtSimAdapter -> ExtSimulation: queue(ActivityStartTrigger(-1L)) +ExtSimAdapter -> ExtSimulation: queue(ActivationMessage(-1)) deactivate ExtSimAdapter activate ExtSimulation + ... Initialize external mobility simulation ... +ExtSimulation -> ExtEvDataService: ProvideArrivingEvs(_, t1) -ExtSimulation -> ExtSimAdapter: ! CompletionMessage(newTriggers) -deactivate ExtSimulation +ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) activate ExtSimAdapter -ExtSimAdapter -> SimScheduler: ! CompletionMessage(newTriggers) +ExtSimAdapter -> Scheduler: ! ScheduleActivation(\n\t_, dataServiceRef) deactivate ExtSimAdapter +activate Scheduler + +Scheduler -> ExtEvDataService: ! Activation(t1) +deactivate Scheduler +activate ExtEvDataService -==Sim== -SimScheduler -> ExtSimAdapter: ! ActivityStartTrigger(tick) +ExtSimulation -> ExtSimAdapter: ! CompletionMessage(newTriggers) +deactivate ExtSimulation activate ExtSimAdapter -ExtSimAdapter -> ExtSimulation: queue(ActivityStartTrigger(tick)) +ExtSimAdapter -> Scheduler: ! Completion(newTriggers) deactivate ExtSimAdapter -activate ExtSimulation -ExtSimulation -> ExtEvDataService: ! RequestEvcsFreeLots -ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) - -activate ExtSimAdapter -ExtSimAdapter -> SimScheduler: ! ScheduleTriggerMessage(\n\t_, dataServiceRef) -deactivate ExtSimAdapter +ExtEvDataService -> EvcsAgent1: ! RegistrationSuccessfulMessage(t1) +activate EvcsAgent1 -activate SimScheduler -SimScheduler -> ExtEvDataService: ! ActivityStartTrigger(tick) -deactivate SimScheduler +EvcsAgent1 -> Scheduler: ! Completion(t1) +deactivate EvcsAgent1 -activate ExtEvDataService -ExtEvDataService -> EvcsAgent1: ! EvFreeLotsRequest(tick) -activate EvcsAgent1 -ExtEvDataService -> EvcsAgent2: ! EvFreeLotsRequest(tick) +ExtEvDataService -> EvcsAgent2: ! RegistrationSuccessfulMessage(t1) activate EvcsAgent2 -ExtEvDataService -> SimScheduler: ! CompletionMessage(None) - -EvcsAgent2 -> ExtEvDataService: ! FreeLotsResponse(_, _) +EvcsAgent2 -> Scheduler: ! Completion(t1) deactivate EvcsAgent2 -EvcsAgent1 -> ExtEvDataService: ! FreeLotsResponse(_, _) -deactivate EvcsAgent1 -ExtEvDataService -> ExtSimulation: queue(ProvideEvcsFreeLots(_)) + +ExtEvDataService -> Scheduler: ! Completion(None) deactivate ExtEvDataService -... Running external mobility simulation,\n determining EV positions ... -ExtSimulation -> ExtEvDataService: ! EvMovementsMessage(_) -ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) +==Simulation== +Scheduler -> EvcsAgent1: ! Activation(t1) +Scheduler -> EvcsAgent2: ! Activation(t1) +Scheduler -> ExtSimAdapter: ! Activation(t1) activate ExtSimAdapter -ExtSimAdapter -> SimScheduler: ! ScheduleTriggerMessage(\n\t_, dataServiceRef) + +ExtSimAdapter -> ExtSimulation: queue(ActivationMessage(t1)) deactivate ExtSimAdapter +activate ExtSimulation -activate SimScheduler -SimScheduler -> ExtEvDataService: ! ActivityStartTrigger(tick) -deactivate SimScheduler +group Request free lots + ExtSimulation -> ExtEvDataService: ! RequestEvcsFreeLots -activate ExtEvDataService -ExtEvDataService -> EvcsAgent1: ! ProvideEvDataMessage(\n\ttick, _) -ExtEvDataService -> EvcsAgent2: ! ProvideEvDataMessage(\n\ttick, _) -ExtEvDataService -> SimScheduler: ! CompletionMessage(evcsTriggers) -deactivate ExtEvDataService + ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) + activate ExtSimAdapter -activate SimScheduler -SimScheduler -> EvcsAgent1: ! ActivityStartTrigger(tick) -activate EvcsAgent1 -SimScheduler -> EvcsAgent2: ! ActivityStartTrigger(tick) -deactivate SimScheduler + ExtSimAdapter -> Scheduler: ! ScheduleActivation(\n\t_, dataServiceRef) + deactivate ExtSimAdapter + activate Scheduler -activate EvcsAgent2 -EvcsAgent1 -> SimScheduler: ! CompletionMessage(None) -deactivate EvcsAgent1 + Scheduler -> ExtEvDataService: ! Activation(t1) + deactivate Scheduler + activate ExtEvDataService -EvcsAgent2 -> ExtEvDataService: ! DepartedEvsResponse(_, _) -activate ExtEvDataService -EvcsAgent2 -> SimScheduler: ! CompletionMessage(None) -deactivate EvcsAgent2 + ExtEvDataService -> EvcsAgent1: ! EvFreeLotsRequest(t1) + activate EvcsAgent1 -ExtEvDataService -> ExtSimulation: queue(AllDepartedEvsResponse(_)) -deactivate ExtEvDataService + ExtEvDataService -> EvcsAgent2: ! EvFreeLotsRequest(t1) + activate EvcsAgent2 -ExtSimulation -> ExtSimAdapter: ! CompletionMessage(newTriggers) + ExtEvDataService -> Scheduler: ! Completion(None) + + EvcsAgent2 -> ExtEvDataService: ! FreeLotsResponse(_, _) + deactivate EvcsAgent2 + + EvcsAgent1 -> ExtEvDataService: ! FreeLotsResponse(_, _) + deactivate EvcsAgent1 + + ExtEvDataService -> ExtSimulation: queue(ProvideEvcsFreeLots(_)) + deactivate ExtEvDataService +end + +group Request current prices (dummy implementation) + ExtSimulation -> ExtEvDataService: ! RequestCurrentPrices + + ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) + activate ExtSimAdapter + + ExtSimAdapter -> Scheduler: ! ScheduleActivation(\n\t_, dataServiceRef) + deactivate ExtSimAdapter + activate Scheduler + + Scheduler -> ExtEvDataService: ! Activation(t1) + deactivate Scheduler + activate ExtEvDataService + + ExtEvDataService -> ExtSimulation: queue(ProvideCurrentPrices(_)) + + ExtEvDataService -> Scheduler: ! Completion(None) + + deactivate ExtEvDataService +end + +group Request departing EVs + ExtSimulation -> ExtEvDataService: ! RequestDepartingEvs + + ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) + activate ExtSimAdapter + + ExtSimAdapter -> Scheduler: ! ScheduleActivation(\n\t_, dataServiceRef) + deactivate ExtSimAdapter + activate Scheduler + + Scheduler -> ExtEvDataService: ! Activation(t1) + deactivate Scheduler + activate ExtEvDataService + + ExtEvDataService -> EvcsAgent1: ! DepartingEvsRequest(t1) + activate EvcsAgent1 + + ExtEvDataService -> EvcsAgent2: ! DepartingEvsRequest(t1) + activate EvcsAgent2 + + ExtEvDataService -> Scheduler: ! Completion(None) + + EvcsAgent2 -> ExtEvDataService: ! DepartingEvsResponse(_, _) + deactivate EvcsAgent2 + + EvcsAgent1 -> ExtEvDataService: ! DepartingEvsResponse(_, _) + deactivate EvcsAgent1 + + ExtEvDataService -> ExtSimulation: queue(ProvideDepartingEvs(_)) + deactivate ExtEvDataService +end + +... Running external mobility simulation,\n determining EV positions ... + +group Provide arriving EVs + ExtSimulation -> ExtEvDataService: ! ProvideArrivingEvs(_) + ExtSimulation -> ExtSimAdapter: ! ScheduleDataServiceMessage(\n\tdataServiceRef\n) + activate ExtSimAdapter + + ExtSimAdapter -> Scheduler: ! ScheduleActivation(\n\t_, dataServiceRef) + deactivate ExtSimAdapter + activate Scheduler + + Scheduler -> ExtEvDataService: ! Activation(t1) + deactivate Scheduler + activate ExtEvDataService + + ExtEvDataService -> EvcsAgent1: ! ProvideEvDataMessage(evs, t2) + activate EvcsAgent1 + + ExtEvDataService -> EvcsAgent2: ! ProvideEvDataMessage(evs, t2) + activate EvcsAgent2 + + ExtEvDataService -> Scheduler: ! Completion(None) + deactivate ExtEvDataService + + EvcsAgent1 -> Scheduler: ! Completion(t2) + deactivate EvcsAgent1 + + EvcsAgent2 -> Scheduler: ! Completion(t2) + deactivate EvcsAgent2 + +end + +ExtSimulation -> ExtSimAdapter: ! CompletionMessage(t2) deactivate ExtSimulation activate ExtSimAdapter -ExtSimAdapter -> SimScheduler: ! CompletionMessage(newTriggers) +ExtSimAdapter -> Scheduler: ! Completion(t2) deactivate ExtSimAdapter @enduml \ No newline at end of file From f74c24aa953603f5272949a6cc56d0bef1b853c6 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Mon, 12 Aug 2024 16:15:37 +0200 Subject: [PATCH 22/39] Improved format Signed-off-by: Sebastian Peter --- docs/uml/protocol/ExtEvSimulationSequence.puml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/uml/protocol/ExtEvSimulationSequence.puml b/docs/uml/protocol/ExtEvSimulationSequence.puml index 954da645af..dde2f869d6 100644 --- a/docs/uml/protocol/ExtEvSimulationSequence.puml +++ b/docs/uml/protocol/ExtEvSimulationSequence.puml @@ -190,13 +190,12 @@ group Provide arriving EVs EvcsAgent2 -> Scheduler: ! Completion(t2) deactivate EvcsAgent2 - end ExtSimulation -> ExtSimAdapter: ! CompletionMessage(t2) deactivate ExtSimulation - activate ExtSimAdapter + ExtSimAdapter -> Scheduler: ! Completion(t2) deactivate ExtSimAdapter From b9195bea268f8a1f1fa12c4a5fdfa506ef42d106 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 13 Aug 2024 14:26:39 +0200 Subject: [PATCH 23/39] Enhanced testing of switches Signed-off-by: Sebastian Peter --- .../edu/ie3/simona/model/grid/GridSpec.scala | 169 +++++++++++++----- 1 file changed, 125 insertions(+), 44 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala index e78d0def7f..060042f45d 100644 --- a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala @@ -267,50 +267,6 @@ class GridSpec } - "Correctly handle multiple closed switches at node" in new BasicGridWithSwitches { - // enable nodes - override val nodes: Seq[NodeModel] = super.nodes - nodes.foreach(_.enable()) - - // add a second switch @ node13 (between node1 and node13) - val secondSwitch = new SwitchModel( - UUID.fromString("ebeaad04-0ee3-4b2e-ae85-8c76a583295b"), - "SecondSwitch1", - defaultOperationInterval, - node1.uuid, - node13.uuid, - ) - // add the second switch + enable switches - override val switches: Set[SwitchModel] = super.switches + secondSwitch - switches.foreach(_.enable()) - // open the switches - switches.foreach(_.close()) - - // get the grid from the raw data - val gridModel = new GridModel( - 1, - default400Kva10KvRefSystem, - GridComponents( - nodes, - lines, - Set(transformer2wModel), - Set.empty[Transformer3wModel], - switches, - ), - GridControls.empty, - ) - - updateUuidToIndexMap(gridModel) - - // nodes 1, 13 and 14 should map to the same node - gridModel.nodeUuidToIndexMap - .get(node1.uuid) - .value shouldBe gridModel.nodeUuidToIndexMap.get(node13.uuid).value - gridModel.nodeUuidToIndexMap - .get(node1.uuid) - .value shouldBe gridModel.nodeUuidToIndexMap.get(node14.uuid).value - } - "update the nodeUuidToIndexMap correctly, when the given grid" must { "contain 3 open switches" in new BasicGridWithSwitches { @@ -473,6 +429,131 @@ class GridSpec nodes.map(node => node.uuid).toVector.sorted ) } + + "contains two closed switches with a common node" in new BasicGridWithSwitches { + // enable nodes + override val nodes: Seq[NodeModel] = super.nodes + nodes.foreach(_.enable()) + + // add a second switch @ node13 (between node1 and node13) + val secondSwitch = new SwitchModel( + UUID.fromString("ebeaad04-0ee3-4b2e-ae85-8c76a583295b"), + "SecondSwitch1", + defaultOperationInterval, + node1.uuid, + node13.uuid, + ) + // add the second switch + enable switches + override val switches: Set[SwitchModel] = super.switches + secondSwitch + switches.foreach(_.enable()) + // open the switches + switches.foreach(_.close()) + + // get the grid from the raw data + val gridModel = new GridModel( + 1, + default400Kva10KvRefSystem, + GridComponents( + nodes, + lines, + Set(transformer2wModel), + Set.empty[Transformer3wModel], + switches, + ), + GridControls.empty, + ) + + updateUuidToIndexMap(gridModel) + + // nodes 1, 13 and 14 should map to the same node + val node1Index = gridModel.nodeUuidToIndexMap + .get(node1.uuid) + .value + gridModel.nodeUuidToIndexMap.get(node13.uuid).value shouldBe node1Index + gridModel.nodeUuidToIndexMap.get(node14.uuid).value shouldBe node1Index + } + + "contains closed switches in a complex pattern" in new BasicGridWithSwitches { + // just six nodes from the basic grid + override val nodes: Seq[NodeModel] = + Seq(node1, node2, node3, node4, node5, node6) + nodes.foreach(_.enable()) + + // nodes 1-4 connected by switches in a rectangle plus diagonal, + // nodes 5-6 connected separately + override val switches: Set[SwitchModel] = Set( + SwitchModel( + UUID.fromString("0-0-0-0-1"), + "Switch1", + defaultOperationInterval, + node1.uuid, + node2.uuid, + ), + SwitchModel( + UUID.fromString("0-0-0-0-2"), + "Switch2", + defaultOperationInterval, + node2.uuid, + node3.uuid, + ), + SwitchModel( + UUID.fromString("0-0-0-0-3"), + "Switch3", + defaultOperationInterval, + node3.uuid, + node4.uuid, + ), + SwitchModel( + UUID.fromString("0-0-0-0-4"), + "Switch4", + defaultOperationInterval, + node1.uuid, + node4.uuid, + ), + SwitchModel( + UUID.fromString("0-0-0-0-5"), + "Switch5", + defaultOperationInterval, + node2.uuid, + node4.uuid, + ), + SwitchModel( + UUID.fromString("0-0-0-0-6"), + "Switch6", + defaultOperationInterval, + node5.uuid, + node6.uuid, + ), + ) + switches.foreach(_.enable()) + // open the switches + switches.foreach(_.close()) + + // get the grid from the raw data + val gridModel = new GridModel( + 1, + default400Kva10KvRefSystem, + GridComponents( + nodes, + Set.empty, + Set.empty, + Set.empty, + switches, + ), + GridControls.empty, + ) + + // testing the assignment of nodes to indices + updateUuidToIndexMap(gridModel) + + gridModel.nodeUuidToIndexMap.get(node1.uuid).value shouldBe 0 + gridModel.nodeUuidToIndexMap.get(node2.uuid).value shouldBe 0 + gridModel.nodeUuidToIndexMap.get(node3.uuid).value shouldBe 0 + gridModel.nodeUuidToIndexMap.get(node4.uuid).value shouldBe 0 + gridModel.nodeUuidToIndexMap.get(node5.uuid).value shouldBe 1 + gridModel.nodeUuidToIndexMap.get(node6.uuid).value shouldBe 1 + } + } "build correct transformer control models" should { From 10a5586b18f2eb41de11e63d27b36327b91347a6 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 13 Aug 2024 14:28:12 +0200 Subject: [PATCH 24/39] Adapted new algorithm to only use immutable data structures Signed-off-by: Sebastian Peter --- .../edu/ie3/simona/model/grid/GridModel.scala | 74 +++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala index 0591b30823..af90cfc344 100644 --- a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala +++ b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala @@ -634,6 +634,8 @@ object GridModel { val switches = gridModel.gridComponents.switches val nodes = gridModel.gridComponents.nodes.distinct + // map for each node that is directly connected to a switch, + // to all nodes that are directly connected via switch val nodeConnections: Map[UUID, Set[UUID]] = switches.filter(_.isClosed).foldLeft(Map.empty[UUID, Set[UUID]]) { (acc, switch) => @@ -647,51 +649,65 @@ object GridModel { acc.getOrElse(switch.nodeBUuid, Set.empty) + switch.nodeAUuid, ) } + + // create sets of nodes to be fused together and assign common indices val switchConnectedNodes = findConnectedNodes(nodeConnections).zipWithIndex.flatMap { - case (nodes, int) => nodes.map(n => n -> int) + case (nodes, idx) => nodes.map(_ -> idx) }.toMap - var offset = switchConnectedNodes.values.maxOption.getOrElse(-1) - val updatedNodeToUuidMap = nodes + + // also account for all missing nodes (not connected to a switch) + val offset = switchConnectedNodes.values.maxOption.map(_ + 1).getOrElse(0) + val (updatedNodeToUuidMap, _) = nodes .filter(_.isInOperation) - .foldLeft(Map.empty[UUID, Int]) { case (map, nodeModel) => - switchConnectedNodes.get(nodeModel.uuid) match { - case Some(idx) => map + (nodeModel.uuid -> idx) - case None => - offset += 1 - map + (nodeModel.uuid -> offset) - } + .foldLeft(Map.empty[UUID, Int], offset) { + case ((map, nextIdx), nodeModel) => + switchConnectedNodes.get(nodeModel.uuid) match { + case Some(idx) => (map + (nodeModel.uuid -> idx), nextIdx) + case None => + (map + (nodeModel.uuid -> nextIdx), nextIdx + 1) + } } gridModel._nodeUuidToIndexMap = updatedNodeToUuidMap } + /** Build sets of connected nodes via depth-first search + * + * @param nodeConnections + * The connected nodes for each node + * @return + * The sets of nodes + */ private def findConnectedNodes( nodeConnections: Map[UUID, Set[UUID]] ): Seq[Seq[UUID]] = { - val visited = scala.collection.mutable.Set[UUID]() - var components = Seq.empty[Seq[UUID]] - - def dfs( - node: UUID, - component: scala.collection.mutable.ListBuffer[UUID], - ): Unit = { - visited += node - component += node - for (neighbor <- nodeConnections.getOrElse(node, Set.empty)) { - if (!visited.contains(neighbor)) { - dfs(neighbor, component) + + def dfs(node: UUID, visited: Set[UUID] = Set.empty): Set[UUID] = { + nodeConnections + .getOrElse(node, Set.empty) + .foldLeft(visited + node) { case (accVisited, neighbor) => + if (accVisited.contains(neighbor)) + accVisited + else + dfs(neighbor, accVisited) } - } } - for (node <- nodeConnections.keys) { - if (!visited.contains(node)) { - val component = scala.collection.mutable.ListBuffer[UUID]() - dfs(node, component) - components = components :+ component.toSeq + val (_, components) = + nodeConnections.keys.foldLeft((Set.empty[UUID], Seq.empty[Seq[UUID]])) { + case ((visited, components), node) => + if (visited.contains(node)) { + (visited, components) + } else { + val component = dfs(node) + val updatedVisited = visited ++ component + val updatedComponents = components :+ component.toSeq + + (updatedVisited, updatedComponents) + } } - } + components } } From 5a516815f1a7f6e63e6ef87582dd6892b9c23166 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 13 Aug 2024 18:04:07 +0200 Subject: [PATCH 25/39] remove code smell --- src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala index ce2669b300..fed8f600f3 100644 --- a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala +++ b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala @@ -30,8 +30,7 @@ object SimonaActorNaming { */ private def simonaActorId(actorId: String): String = { val randomNumber = Random.nextInt(1000).toString - val finalId = s"$actorId-$randomNumber" - finalId + s"$actorId-$randomNumber" } /** Constructs an actor name based on the simona convention for actor names. From 46e3dcc43d05788e9a177922591538c2a00d62fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 07:34:40 +0000 Subject: [PATCH 26/39] Bump com.sksamuel.scapegoat:scalac-scapegoat-plugin_2.13.14 (#899) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dac93ee8d2..50c430f3d4 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ ext { jtsVersion = '1.19.0' confluentKafkaVersion = '7.4.0' tscfgVersion = '1.0.0' - scapegoatVersion = '2.1.6' + scapegoatVersion = '3.0.0' testContainerVersion = '0.41.4' From cee80b9686dc252432546343d2268b20a04039a2 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 14 Aug 2024 14:04:05 +0200 Subject: [PATCH 27/39] Apply suggestions from code review We're closing the switches ofc Co-authored-by: Daniel Feismann <98817556+danielfeismann@users.noreply.github.com> --- src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala index 060042f45d..2f29248054 100644 --- a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala @@ -446,7 +446,7 @@ class GridSpec // add the second switch + enable switches override val switches: Set[SwitchModel] = super.switches + secondSwitch switches.foreach(_.enable()) - // open the switches + // close the switches switches.foreach(_.close()) // get the grid from the raw data @@ -526,7 +526,7 @@ class GridSpec ), ) switches.foreach(_.enable()) - // open the switches + // close the switches switches.foreach(_.close()) // get the grid from the raw data From f562a13b0624c8c86f75241ef5346316d0687463 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 14 Aug 2024 18:29:26 +0200 Subject: [PATCH 28/39] Converting all instances of `eval-rst` to the equivalent myst syntax --- CHANGELOG.md | 1 + docs/readthedocs/index.md | 9 +-- docs/readthedocs/models/cts_model.md | 4 +- docs/readthedocs/models/pv_model.md | 69 +++++------------- docs/readthedocs/models/reference_system.md | 73 +++++++++---------- .../models/three_winding_transformer_model.md | 5 +- docs/readthedocs/references.md | 25 +++---- 7 files changed, 71 insertions(+), 115 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 216d239639..6344047499 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated dependabot reviewers [#888](https://github.com/ie3-institute/simona/issues/888) - Merged `HpModelTestData` with `HpTestData` to `HpInputTestData` [#872](https://github.com/ie3-institute/simona/issues/872) - Harmonised both methods that check the inner temperature of thermal house against the boundaries [#880](https://github.com/ie3-institute/simona/issues/880) +- Convert all `eval-rst` instances in rtd to myst syntax [#901](https://github.com/ie3-institute/simona/issues/901) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) diff --git a/docs/readthedocs/index.md b/docs/readthedocs/index.md index 714d51bcf2..966d0a975d 100644 --- a/docs/readthedocs/index.md +++ b/docs/readthedocs/index.md @@ -1,10 +1,9 @@ # Welcome to simona docs -```{eval-rst} -.. figure:: ../logo/logo_tightcrop_transparent.png - :figwidth: 25% - :align: right - :alt: logo of simona +```{image} ../logo/logo_tightcrop_transparent.png +:width: 25% +:align: right +:alt: logo of simona ``` Welcome to the documentation of simona - an agent-based discrete-event power system simulation model developed at the diff --git a/docs/readthedocs/models/cts_model.md b/docs/readthedocs/models/cts_model.md index 0ca51a7395..be079dbe74 100644 --- a/docs/readthedocs/models/cts_model.md +++ b/docs/readthedocs/models/cts_model.md @@ -39,9 +39,7 @@ $$ $$ Reference: -```{eval-rst} -* :cite:ts:`Quaschning.2013` -``` +* {cite:cts}`Quaschning.2013` That is the mathematical description of loading and unloading processes concerning the buffer storage. Whenever heat is stored within the storage or removed from the storage this equation is used. This includes the case that the whole heat demand is satisfied by the storage. diff --git a/docs/readthedocs/models/pv_model.md b/docs/readthedocs/models/pv_model.md index 8ddb822c0b..6f193edf30 100644 --- a/docs/readthedocs/models/pv_model.md +++ b/docs/readthedocs/models/pv_model.md @@ -52,11 +52,8 @@ $$ $$ **References:** - -```{eval-rst} -* :cite:cts:`Maleki.2017` -* :cite:ts:`Spencer.1971` -``` +* {cite:cts}`Maleki.2017` +* {cite:cts}`Spencer.1971` ### Hour Angle @@ -103,12 +100,9 @@ $$ **Note:** The used formulas are based on *\"DIN 5034-2: Tageslicht in Innenräumen, Grundlagen.\"* and therefore valid especially for Germany and Europe. For international calculations a more general formulation that can be found in [Maleki, S.A., Hizam, H., & Gomes, C. (2017). Estimation of Hourly, Daily and Monthly Global Solar Radiation on Inclined Surfaces: Models Re-Visited.](https://res.mdpi.com/d_attachment/energies/energies-10-00134/article_deploy/energies-10-00134-v2.pdf) might be used. **References:** - -```{eval-rst} -* :cite:cts:`Watter.2013` -* :cite:ts:`Maleki.2017` -* :cite:ts:`Wang.2019` -``` +* {cite:cts}`Watter.2013` +* {cite:cts}`Maleki.2017` +* {cite:cts}`Wang.2019` ### Sunrise Angle @@ -127,10 +121,8 @@ $$ **$\phi$** = observer's latitude **References:** -```{eval-rst} -* :cite:cts:`Maleki.2017` -* :cite:ts:`Itaca_Sun` -``` +* {cite:cts}`Maleki.2017` +* {cite:cts}`Itaca_Sun` ### Solar Altitude Angle @@ -146,11 +138,8 @@ $$ **$\omega$**= hour angle **References:** - -```{eval-rst} -* :cite:ts:`Maleki.2017` p. 5 -* :cite:ts:`Itaca_Sun` -``` +* {cite:cts}`Maleki.2017` p. 5 +* {cite:cts}`Itaca_Sun` ### Zenith Angle @@ -191,11 +180,8 @@ $$ **$\omega$** = hour angle **References:** - -```{eval-rst} -* :cite:ts:`Quaschning.2013` -* :cite:ts:`Maleki.2017` p. 18 -``` +* {cite:cts}`Quaschning.2013` +* {cite:cts}`Maleki.2017` p. 18 ### Air Mass @@ -210,11 +196,8 @@ airmass = \sqrt{(707.8\overline{8} \cdot \cos({\theta_z}))^2 +2 \cdot 707.8\over $$ **References:** - -```{eval-rst} -* :cite:ts:`Schoenberg.1929` -* :cite:ts:`WikiAirMass` -``` +* {cite:cts}`Schoenberg.1929` +* {cite:cts}`WikiAirMass` ### Extraterrestrial Radiation @@ -236,11 +219,8 @@ $$ **J** = day angle **References:** - -```{eval-rst} -* :cite:ts:`Zheng.2017` p. 53, formula 2.3b -* :cite:ts:`Iqbal.1983` -``` +* {cite:cts}`Zheng.2017` p. 53, formula 2.3b +* {cite:cts}`Iqbal.1983` ### Beam Radiation on Sloped Surface @@ -295,10 +275,7 @@ $$ **$E_{beam,H}$** = beam radiation (horizontal surface) **Reference:** - -```{eval-rst} -* :cite:ts:`Duffie.2013` p. 88 -``` +* {cite:cts}`Duffie.2013` p. 88 ### Diffuse Radiation on Sloped Surface @@ -407,12 +384,9 @@ $$ **$E_{dif,H}$** = diffuse radiation (horizontal surface) **References:** - -```{eval-rst} -* :cite:ts:`Perez.1987` -* :cite:ts:`Perez.1990` -* :cite:ts:`Myers.2017` p. 96f -``` +* {cite:cts}`Perez.1987` +* {cite:cts}`Perez.1990` +* {cite:cts}`Myers.2017` p. 96f ### Reflected Radiation on Sloped Surface @@ -427,10 +401,7 @@ $$ **$\rho$** = albedo **Reference:** -```{eval-rst} -* :cite:ts:`Maleki.2017` p. 19 -``` - +* {cite:cts}`Maleki.2017` p. 19 ### Output diff --git a/docs/readthedocs/models/reference_system.md b/docs/readthedocs/models/reference_system.md index 28b066bfcc..5a0e944356 100644 --- a/docs/readthedocs/models/reference_system.md +++ b/docs/readthedocs/models/reference_system.md @@ -6,44 +6,41 @@ The reference system is built up by specifying the included voltage levels. The ## Default reference system -```{eval-rst} -.. list-table:: - :widths: 33 33 33 - :header-rows: 0 - - - * - Voltage level (id) - - Nominal voltage (vNom) - - Apparent power (sNom) - - * - LV - - 0.4 kV - - 100 kVA - - * - MV - - 10 kV - - 40 MVA - - * - MV - - 20 kV - - 60 MVA - - * - MV - - 30 kV - - 150 MVA - - * - HV - - 110 kV - - 600 MVA - - * - EHV - - 220 kV - - 800 MVA - - * - EHV - - 380 kV - - 1000 MVA - +```{list-table} +:widths: auto +:header-rows: 1 + +* - Voltage level (id) + - Nominal voltage (vNom) + - Apparent power (sNom) + +* - LV + - 0.4 kV + - 100 kVA + +* - MV + - 10 kV + - 40 MVA + +* - MV + - 20 kV + - 60 MVA + +* - MV + - 30 kV + - 150 MVA + +* - HV + - 110 kV + - 600 MVA + +* - EHV + - 220 kV + - 800 MVA + +* - EHV + - 380 kV + - 1000 MVA ``` diff --git a/docs/readthedocs/models/three_winding_transformer_model.md b/docs/readthedocs/models/three_winding_transformer_model.md index 72a97abba1..756259cfa2 100644 --- a/docs/readthedocs/models/three_winding_transformer_model.md +++ b/docs/readthedocs/models/three_winding_transformer_model.md @@ -40,7 +40,4 @@ More details on the physical model transformation can be found in the Ph.D. thes - The tap changer is always apparent in subgrid A. If subgrid B or C require an adaption, they send a tap request with provided power values, subgrid A takes a decision and re-evaluates the outcome **References:** - -```{eval-rst} -:cite:cts:`Kittl_2022` -``` +* {cite:cts}`Kittl_2022` diff --git a/docs/readthedocs/references.md b/docs/readthedocs/references.md index 0aa2febc1e..4e96cb3f9d 100644 --- a/docs/readthedocs/references.md +++ b/docs/readthedocs/references.md @@ -1,26 +1,19 @@ # Publications and References -## Publications +## SIMONA publications The following publications discuss SIMONA and implementation details as well as outcomes where SIMONA have been used: -```{eval-rst} -The following publications discuss SIMONA and implementation details as well as outcomes where SIMONA have been used: - -.. rubric:: SIMONA Publications - -.. bibliography:: _static/bibliography/bibAboutSimona.bib - :style: unsrt - :all: - +```{bibliography} _static/bibliography/bibAboutSimona.bib +:style: unsrt +:all: +``` -References -=============== +## References References of publications SIMONA referred on: -.. rubric:: References -.. bibliography:: _static/bibliography/bibtexAll.bib - :style: custom - :all: +```{bibliography} _static/bibliography/bibtexAll.bib +:style: custom +:all: ``` From cf7aafc13e9e31bb2987e277d277e0bbbf8f1a26 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 14 Aug 2024 18:35:27 +0200 Subject: [PATCH 29/39] Fixing codacy issues --- docs/readthedocs/models/cts_model.md | 2 ++ docs/readthedocs/models/pv_model.md | 20 +++++++++++++++++++ .../models/three_winding_transformer_model.md | 1 + 3 files changed, 23 insertions(+) diff --git a/docs/readthedocs/models/cts_model.md b/docs/readthedocs/models/cts_model.md index be079dbe74..5750820624 100644 --- a/docs/readthedocs/models/cts_model.md +++ b/docs/readthedocs/models/cts_model.md @@ -39,8 +39,10 @@ $$ $$ Reference: + * {cite:cts}`Quaschning.2013` + That is the mathematical description of loading and unloading processes concerning the buffer storage. Whenever heat is stored within the storage or removed from the storage this equation is used. This includes the case that the whole heat demand is satisfied by the storage. The same relationship is used to determine the quantity of heat which is stored in the storage by converting the equation to: diff --git a/docs/readthedocs/models/pv_model.md b/docs/readthedocs/models/pv_model.md index 6f193edf30..c847b6e158 100644 --- a/docs/readthedocs/models/pv_model.md +++ b/docs/readthedocs/models/pv_model.md @@ -52,9 +52,11 @@ $$ $$ **References:** + * {cite:cts}`Maleki.2017` * {cite:cts}`Spencer.1971` + ### Hour Angle The hour angle is a conceptual description of the rotation of the earth around its polar axis. It starts with a negative value in the morning, arrives at 0° at noon (solar time) and ends with a positive value in the evening. The hour angle (in radian!) is calculated as follows @@ -100,10 +102,12 @@ $$ **Note:** The used formulas are based on *\"DIN 5034-2: Tageslicht in Innenräumen, Grundlagen.\"* and therefore valid especially for Germany and Europe. For international calculations a more general formulation that can be found in [Maleki, S.A., Hizam, H., & Gomes, C. (2017). Estimation of Hourly, Daily and Monthly Global Solar Radiation on Inclined Surfaces: Models Re-Visited.](https://res.mdpi.com/d_attachment/energies/energies-10-00134/article_deploy/energies-10-00134-v2.pdf) might be used. **References:** + * {cite:cts}`Watter.2013` * {cite:cts}`Maleki.2017` * {cite:cts}`Wang.2019` + ### Sunrise Angle The hour angles at sunrise and sunset are very useful quantities to know. These two values have the same absolute value, however the sunset angle ($\omega_{SS}$) is positive and the sunrise angle ($\omega_{SR}$) is negative. Both can be calculated from: @@ -121,9 +125,11 @@ $$ **$\phi$** = observer's latitude **References:** + * {cite:cts}`Maleki.2017` * {cite:cts}`Itaca_Sun` + ### Solar Altitude Angle Represents the angle between the horizontal and the line to the sun, that is, the complement of the zenith angle. @@ -138,9 +144,11 @@ $$ **$\omega$**= hour angle **References:** + * {cite:cts}`Maleki.2017` p. 5 * {cite:cts}`Itaca_Sun` + ### Zenith Angle Represents the angle between the vertical and the line to the sun, that is, the angle of incidence of beam radiation on a horizontal surface. @@ -180,9 +188,11 @@ $$ **$\omega$** = hour angle **References:** + * {cite:cts}`Quaschning.2013` * {cite:cts}`Maleki.2017` p. 18 + ### Air Mass Calculating the air mass ratio by dividing the radius of the earth with approx. effective height of the atmosphere (each in kilometer) @@ -196,9 +206,11 @@ airmass = \sqrt{(707.8\overline{8} \cdot \cos({\theta_z}))^2 +2 \cdot 707.8\over $$ **References:** + * {cite:cts}`Schoenberg.1929` * {cite:cts}`WikiAirMass` + ### Extraterrestrial Radiation The extraterrestrial radiation $I_0$ is calculated by multiplying the eccentricity correction factor @@ -219,9 +231,11 @@ $$ **J** = day angle **References:** + * {cite:cts}`Zheng.2017` p. 53, formula 2.3b * {cite:cts}`Iqbal.1983` + ### Beam Radiation on Sloped Surface For our use case, $\omega_{2}$ is normally set to the hour angle one hour after $\omega_{1}$. Within one hour distance to sunrise/sunset, we adjust $\omega_{1}$ and $\omega_{2}$ accordingly: @@ -275,8 +289,10 @@ $$ **$E_{beam,H}$** = beam radiation (horizontal surface) **Reference:** + * {cite:cts}`Duffie.2013` p. 88 + ### Diffuse Radiation on Sloped Surface The diffuse radiation is computed using the Perez model, which divides the radiation in three parts. First, there is an intensified radiation from the direct vicinity of the sun. Furthermore, there is Rayleigh scattering, backscatter (which lead to increased in intensity on the horizon) and isotropic radiation considered. @@ -384,10 +400,12 @@ $$ **$E_{dif,H}$** = diffuse radiation (horizontal surface) **References:** + * {cite:cts}`Perez.1987` * {cite:cts}`Perez.1990` * {cite:cts}`Myers.2017` p. 96f + ### Reflected Radiation on Sloped Surface $$ @@ -401,8 +419,10 @@ $$ **$\rho$** = albedo **Reference:** + * {cite:cts}`Maleki.2017` p. 19 + ### Output Received energy is calculated as the sum of all three types of irradiation. diff --git a/docs/readthedocs/models/three_winding_transformer_model.md b/docs/readthedocs/models/three_winding_transformer_model.md index 756259cfa2..9b77dc0cc1 100644 --- a/docs/readthedocs/models/three_winding_transformer_model.md +++ b/docs/readthedocs/models/three_winding_transformer_model.md @@ -40,4 +40,5 @@ More details on the physical model transformation can be found in the Ph.D. thes - The tap changer is always apparent in subgrid A. If subgrid B or C require an adaption, they send a tap request with provided power values, subgrid A takes a decision and re-evaluates the outcome **References:** + * {cite:cts}`Kittl_2022` From 47a75991bef39a338e81adba143b01782aaafc7c Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 14 Aug 2024 18:41:43 +0200 Subject: [PATCH 30/39] Fixing more codacy issues --- docs/readthedocs/models/pv_model.md | 4 ++-- docs/readthedocs/models/three_winding_transformer_model.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/readthedocs/models/pv_model.md b/docs/readthedocs/models/pv_model.md index c847b6e158..b36df95f00 100644 --- a/docs/readthedocs/models/pv_model.md +++ b/docs/readthedocs/models/pv_model.md @@ -53,7 +53,7 @@ $$ **References:** -* {cite:cts}`Maleki.2017` +* {cite:cts}`Maleki.2017` * {cite:cts}`Spencer.1971` @@ -126,7 +126,7 @@ $$ **References:** -* {cite:cts}`Maleki.2017` +* {cite:cts}`Maleki.2017` * {cite:cts}`Itaca_Sun` diff --git a/docs/readthedocs/models/three_winding_transformer_model.md b/docs/readthedocs/models/three_winding_transformer_model.md index 9b77dc0cc1..d10790848b 100644 --- a/docs/readthedocs/models/three_winding_transformer_model.md +++ b/docs/readthedocs/models/three_winding_transformer_model.md @@ -41,4 +41,4 @@ More details on the physical model transformation can be found in the Ph.D. thes **References:** -* {cite:cts}`Kittl_2022` +- {cite:cts}`Kittl_2022` From f28ff0ae74bf0d1de7f0fb412c9143da4006f186 Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Thu, 15 Aug 2024 15:08:37 +0200 Subject: [PATCH 31/39] =?UTF-8?q?Added=20Simon=20H=C3=BCtte=20and=20Philip?= =?UTF-8?q?p=20Schmelter=20to=20authors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AUTHORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 98638879b4..07c026cb69 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -62,3 +62,5 @@ Scala and uses pekko as its main library for concurrent calculation and to repre - Staudt, Marius - Roumeliotis, Lara - Bung, Vicky +- Hütte, Simon +- Schmelter, Philipp From 9d1f36c78c8f7b1d4a2221520903630597113a13 Mon Sep 17 00:00:00 2001 From: Toastwear Date: Thu, 15 Aug 2024 15:08:43 +0200 Subject: [PATCH 32/39] Adding Pierre Petersmeier and Marvin Heintze to Authors List --- AUTHORS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 98638879b4..4d179f5679 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -62,3 +62,5 @@ Scala and uses pekko as its main library for concurrent calculation and to repre - Staudt, Marius - Roumeliotis, Lara - Bung, Vicky +- Petersmeier, Pierre +- Heintze, Marvin \ No newline at end of file From 6e1ed57e84d2066314f733001f55be2845cc5560 Mon Sep 17 00:00:00 2001 From: Toastwear Date: Thu, 15 Aug 2024 15:20:24 +0200 Subject: [PATCH 33/39] format --- AUTHORS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 4d179f5679..57bc3082df 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -63,4 +63,4 @@ Scala and uses pekko as its main library for concurrent calculation and to repre - Roumeliotis, Lara - Bung, Vicky - Petersmeier, Pierre -- Heintze, Marvin \ No newline at end of file +- Heintze, Marvin From c19c1b5fd0e02989a2edc8199634f61034e73419 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:06:45 +0000 Subject: [PATCH 34/39] Bump ch.qos.logback:logback-classic from 1.5.6 to 1.5.7 (#908) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8974152e9b..c123cf1e73 100644 --- a/build.gradle +++ b/build.gradle @@ -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.6" + implementation "ch.qos.logback:logback-classic:1.5.7" /* testing */ testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' From 2e59adb263861e96ec0d1d8ada5ea6f207e8b5ab Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Fri, 16 Aug 2024 12:49:15 +0200 Subject: [PATCH 35/39] Added missing information to CHANGELOG.md after updating AUTHORS.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index be3de2d30c..a554ab08cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Harmonised both methods that check the inner temperature of thermal house against the boundaries [#880](https://github.com/ie3-institute/simona/issues/880) - Convert all `eval-rst` instances in rtd to myst syntax [#901](https://github.com/ie3-institute/simona/issues/901) - External simulation should provide information about next tick of MobSim [#776](https://github.com/ie3-institute/simona/issues/776) +- Updated AUTHORS.md [#904](...) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) From 02bea083c1c6043e73edfb60809faba0a1f444ae Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Fri, 16 Aug 2024 12:50:59 +0200 Subject: [PATCH 36/39] Added missing information to CHANGELOG.md after updating AUTHORS.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a554ab08cf..b12fcfb62b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Harmonised both methods that check the inner temperature of thermal house against the boundaries [#880](https://github.com/ie3-institute/simona/issues/880) - Convert all `eval-rst` instances in rtd to myst syntax [#901](https://github.com/ie3-institute/simona/issues/901) - External simulation should provide information about next tick of MobSim [#776](https://github.com/ie3-institute/simona/issues/776) -- Updated AUTHORS.md [#904](...) +- Updated AUTHORS.md [#904](https://github.com/ie3-institute/simona/issues/904) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) From f598aba7b497c307a3f3c0c089bfad35d18bc84b Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Fri, 16 Aug 2024 13:25:29 +0200 Subject: [PATCH 37/39] Reverted workaround in spotless.gradle. --- CHANGELOG.md | 1 + gradle/scripts/spotless.gradle | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be3de2d30c..31cf47e26a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Harmonised both methods that check the inner temperature of thermal house against the boundaries [#880](https://github.com/ie3-institute/simona/issues/880) - Convert all `eval-rst` instances in rtd to myst syntax [#901](https://github.com/ie3-institute/simona/issues/901) - External simulation should provide information about next tick of MobSim [#776](https://github.com/ie3-institute/simona/issues/776) +- Reverted temporary workaround in `spotless.gradle` [#681](https://github.com/ie3-institute/simona/issues/681) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) diff --git a/gradle/scripts/spotless.gradle b/gradle/scripts/spotless.gradle index 104ff70c86..f127f2bdd5 100644 --- a/gradle/scripts/spotless.gradle +++ b/gradle/scripts/spotless.gradle @@ -19,16 +19,14 @@ spotless { licenseHeader ie3LicHead // the Groovy Eclipse formatter extends the Java Eclipse formatter, // so it formats Java files by default (unless `excludeJava` is used). - // FIXME rolled back greclipse version https://github.com/diffplug/spotless/issues/1860 - greclipse('4.27').configFile('greclipse.properties') + greclipse().configFile('greclipse.properties') indentWithSpaces 2 } groovyGradle { // same as groovy, but for .gradle (defaults to '*.gradle') target '*.gradle', 'gradle/scripts/*.gradle' - // FIXME rolled back greclipse version https://github.com/diffplug/spotless/issues/1860 - greclipse('4.27') + greclipse() indentWithSpaces 2 } From 9095171cc6f60f9d7836bb084ac35b1868d0b3e7 Mon Sep 17 00:00:00 2001 From: Toastwear Date: Fri, 16 Aug 2024 16:42:12 +0200 Subject: [PATCH 38/39] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b12fcfb62b..f55a036ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Convert all `eval-rst` instances in rtd to myst syntax [#901](https://github.com/ie3-institute/simona/issues/901) - External simulation should provide information about next tick of MobSim [#776](https://github.com/ie3-institute/simona/issues/776) - Updated AUTHORS.md [#904](https://github.com/ie3-institute/simona/issues/904) +- Updated AUTHORS.md [#905](https://github.com/ie3-institute/simona/issues/905) ### Fixed - Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658) From 93ead0d7e68063b7d7865b1642c7d6de1ae197e4 Mon Sep 17 00:00:00 2001 From: Toastwear Date: Mon, 19 Aug 2024 16:27:49 +0200 Subject: [PATCH 39/39] Removed the trait CylindricalStorageInputTestData from src/test --- .../CylindricalStorageInputTestData.scala | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala diff --git a/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala deleted file mode 100644 index b7c0f678ce..0000000000 --- a/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.test.common.input - -import java.util.UUID - -import edu.ie3.datamodel.models.StandardUnits -import edu.ie3.datamodel.models.input.thermal.{ - CylindricalStorageInput, - ThermalBusInput, -} -import tech.units.indriya.quantity.Quantities.getQuantity - -trait CylindricalStorageInputTestData { - - protected val csInputModel = new CylindricalStorageInput( - UUID.randomUUID(), - "ThermalStorage", - new ThermalBusInput(UUID.randomUUID(), "ThermalBus"), - getQuantity(100, StandardUnits.VOLUME), - getQuantity(20, StandardUnits.VOLUME), - getQuantity(30, StandardUnits.TEMPERATURE), - getQuantity(40, StandardUnits.TEMPERATURE), - getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY), - ) -}