Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Converted ExtSimAdapter to typed. #1095

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Prepare 'ChpModelSpec' and 'CylindricalThermalStorageSpec' for Storage without storageVolumeLvlMin [#1012](https://github.com/ie3-institute/simona/issues/1012)
- Fixed SonarQube quality gate using the correct parameter '-Dsonar.qualitygate.wait=true' [#1072](https://github.com/ie3-institute/simona/issues/1072)
- Updated `simonaAPI` to version `0.6.0` [#1080](https://github.com/ie3-institute/simona/issues/1080)
- Converted `ExtSimAdapter` to typed [#1094](https://github.com/ie3-institute/simona/issues/1094)

### Fixed
- Fix rendering of references in documentation [#505](https://github.com/ie3-institute/simona/issues/505)
Expand Down
109 changes: 67 additions & 42 deletions src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,32 @@

package edu.ie3.simona.api

import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
import org.apache.pekko.actor.{Actor, ActorRef, PoisonPill, Props}
import edu.ie3.simona.api.ExtSimAdapter.{Create, ExtSimAdapterStateData, Stop}
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.api.simulation.ontology.{
ActivationMessage,
ControlResponseMessageFromExt,
TerminationCompleted,
TerminationMessage,
CompletionMessage => ExtCompletionMessage,
}
import edu.ie3.simona.logging.SimonaActorLogging
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.ScheduleServiceActivation
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import org.apache.pekko.actor.typed.scaladsl.Behaviors
import org.apache.pekko.actor.typed.{ActorRef, Behavior}

import scala.jdk.OptionConverters._

object ExtSimAdapter {

def props(scheduler: ActorRef): Props =
Props(
new ExtSimAdapter(scheduler)
)
sealed trait AdapterMessage extends ControlResponseMessageFromExt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call this Request, which is a pekko convention we took over? So then, from other classes one would specify the message type as ExtSimAdapter.Request


/** The [[ExtSimAdapterData]] can only be constructed once the ExtSimAdapter
* actor is created. Thus, we need an extra initialization message.
Expand All @@ -44,80 +40,109 @@ object ExtSimAdapter {
* The [[ExtSimAdapterData]] of the corresponding external simulation
*/
final case class Create(extSimData: ExtSimAdapterData, unlockKey: ScheduleKey)
extends AdapterMessage

final case class Stop(simulationSuccessful: Boolean)
final case class Stop(simulationSuccessful: Boolean) extends AdapterMessage

final case class ExtSimAdapterStateData(
extSimData: ExtSimAdapterData,
currentTick: Option[Long] = None,
)
}
) extends AdapterMessage

final case class WrappedActivation(activation: Activation)
extends AdapterMessage
final case class WrappedScheduleDataServiceMessage(
msg: ScheduleDataServiceMessage
) extends AdapterMessage

def adapter(
ref: ActorRef[ControlResponseMessageFromExt]
): Behavior[ScheduleDataServiceMessage] = Behaviors.receiveMessagePartial {
extMsg =>
ref ! WrappedScheduleDataServiceMessage(extMsg)
Behaviors.same
}

final case class ExtSimAdapter(scheduler: ActorRef)
extends Actor
with SimonaActorLogging {
override def receive: Receive = { case Create(extSimAdapterData, unlockKey) =>
// triggering first time at init tick
scheduler ! ScheduleActivation(
self.toTyped,
INIT_SIM_TICK,
Some(unlockKey),
)
context become receiveIdle(
ExtSimAdapterStateData(extSimAdapterData)
)
def apply(
scheduler: ActorRef[SchedulerMessage]
): Behavior[ControlResponseMessageFromExt] = Behaviors.setup { ctx =>
val activationAdapter = ctx.messageAdapter(WrappedActivation)
initialize(scheduler, activationAdapter)
}

private def receiveIdle(implicit
stateData: ExtSimAdapterStateData
): Receive = {
case Activation(tick) =>
private def initialize(implicit
scheduler: ActorRef[SchedulerMessage],
activationAdapter: ActorRef[Activation],
): Behavior[ControlResponseMessageFromExt] = Behaviors.receiveMessage {
case Create(extSimData, unlockKey) =>
// triggering first time at init tick
scheduler ! ScheduleActivation(
activationAdapter,
INIT_SIM_TICK,
Some(unlockKey),
)

receiveIdle(ExtSimAdapterStateData(extSimData))
}

private[api] def receiveIdle(stateData: ExtSimAdapterStateData)(implicit
scheduler: ActorRef[SchedulerMessage],
activationAdapter: ActorRef[Activation],
): Behavior[ControlResponseMessageFromExt] = Behaviors.receive {
case (ctx, WrappedActivation(Activation(tick))) =>
stateData.extSimData.queueExtMsg(
new ActivationMessage(tick)
)
log.debug(
ctx.log.debug(
"Tick {} has been activated in external simulation",
tick,
)

context become receiveIdle(
stateData.copy(currentTick = Some(tick))
)
receiveIdle(stateData.copy(currentTick = Some(tick)))

case extCompl: ExtCompletionMessage =>
case (ctx, extCompl: ExtCompletionMessage) =>
// when multiple triggers have been sent, a completion message
// always refers to the oldest tick

val newTick = extCompl.nextActivation().toScala.map(Long2long)

scheduler ! Completion(self.toTyped, newTick)
log.debug(
scheduler ! Completion(activationAdapter, newTick)
ctx.log.debug(
"Tick {} has been completed in external simulation",
stateData.currentTick,
)

context become receiveIdle(stateData.copy(currentTick = None))
receiveIdle(stateData.copy(currentTick = None))

case scheduleDataService: ScheduleDataServiceMessage =>
case (
ctx,
WrappedScheduleDataServiceMessage(
scheduleDataService: ScheduleDataServiceMessage
),
) =>
val tick = stateData.currentTick.getOrElse(
throw new RuntimeException("No tick has been triggered")
)
val key = ScheduleLock.singleKey(context, scheduler.toTyped, tick)
val key = ScheduleLock.singleKey(ctx, scheduler, tick)

scheduleDataService.getDataService ! ScheduleServiceActivation(
tick,
key,
)

case Stop(simulationSuccessful) =>
Behaviors.same

case (_, Stop(simulationSuccessful)) =>
// let external sim know that we have terminated
stateData.extSimData.queueExtMsg(
new TerminationMessage(simulationSuccessful)
)

case _: TerminationCompleted =>
Behaviors.same

case (_, _: TerminationCompleted) =>
// external simulation has terminated as well, we can exit
self ! PoisonPill
Behaviors.stopped
}

}
6 changes: 3 additions & 3 deletions src/main/scala/edu/ie3/simona/sim/SimonaSim.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package edu.ie3.simona.sim

import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.api.ExtSimAdapter
import edu.ie3.simona.api.simulation.ontology.ControlResponseMessageFromExt
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.listener.{DelayedStopHelper, RuntimeEventListener}
import edu.ie3.simona.main.RunSimona.SimonaEnded
Expand All @@ -17,7 +18,6 @@ import edu.ie3.util.scala.Scope
import org.apache.pekko.actor.typed.scaladsl.adapter._
import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
import org.apache.pekko.actor.typed.{ActorRef, Behavior, PostStop, Terminated}
import org.apache.pekko.actor.{ActorRef => ClassicRef}

/** Main entrance point to a simona simulation as the guardian actor. This actor
* starts the initialization of all actors and waits for the simulation to end.
Expand Down Expand Up @@ -118,7 +118,7 @@ object SimonaSim {
/* watch all actors */
resultEventListeners.foreach(ctx.watch)
ctx.watch(runtimeEventListener)
extSimulationData.extSimAdapters.map(_.toTyped).foreach(ctx.watch)
extSimulationData.extSimAdapters.foreach(ctx.watch)
otherActors.foreach(ctx.watch)

// Start simulation
Expand Down Expand Up @@ -267,7 +267,7 @@ object SimonaSim {
*/
private final case class ActorData(
starter: ActorRef[SimonaEnded],
extSimAdapters: Iterable[ClassicRef],
extSimAdapters: Iterable[ActorRef[ControlResponseMessageFromExt]],
runtimeEventListener: ActorRef[RuntimeEventListener.Request],
delayedStoppingActors: Seq[ActorRef[DelayedStopHelper.StoppingMsg]],
otherActors: Iterable[ActorRef[_]],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

package edu.ie3.simona.sim.setup

import edu.ie3.simona.api.simulation.ontology.ControlResponseMessageFromExt
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],
extSimAdapters: Iterable[ActorRef[ControlResponseMessageFromExt]],
extDataServices: Map[Class[_], ClassicRef],
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.agent.grid.GridAgent
import edu.ie3.simona.agent.grid.GridAgentMessages.CreateGridAgent
import edu.ie3.simona.api.ExtSimAdapter
import edu.ie3.simona.api.data.ExtDataConnection
import edu.ie3.simona.api.data.ev.ExtEvDataConnection
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.config.{ArgsParser, RefSystemParser, SimonaConfig}
Expand Down Expand Up @@ -206,11 +205,18 @@ class SimonaStandaloneSetup(
val (extSimAdapters, extDataServices) =
extLinks.zipWithIndex.map { case (extLink, index) =>
// external simulation always needs at least an ExtSimAdapter
val extSimAdapter = context.toClassic.simonaActorOf(
ExtSimAdapter.props(scheduler.toClassic),
val extSimAdapter = context.spawn(
ExtSimAdapter(scheduler),
s"$index",
)
Comment on lines +208 to 211
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pick an expressive actor name here. Beforehand, simonaActorOf took care of that by prepending the class name.

val extSimAdapterData = new ExtSimAdapterData(extSimAdapter, args)

val extSimAdapterMsgAdapter = context.spawn(
ExtSimAdapter.adapter(extSimAdapter),
s"$index-message-adapter",
)

val extSimAdapterData =
new ExtSimAdapterData(extSimAdapterMsgAdapter.toClassic, args)

// send init data right away, init activation is scheduled
extSimAdapter ! ExtSimAdapter.Create(
Expand All @@ -230,7 +236,10 @@ class SimonaStandaloneSetup(
ExtEvDataService.props(scheduler.toClassic),
s"$index-$dIndex",
)
evConnection.setActorRefs(extEvDataService, extSimAdapter)
evConnection.setActorRefs(
extEvDataService,
extSimAdapterMsgAdapter.toClassic,
)

extEvDataService ! SimonaService.Create(
InitExtEvData(evConnection),
Expand Down
Loading
Loading