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

Implement termination message for external simulation #80

Merged
merged 17 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 3 additions & 2 deletions src/main/scala/edu/ie3/simona/agent/grid/GridAgent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import edu.ie3.simona.agent.grid.GridAgentData.{
GridAgentInitData,
GridAgentUninitializedData
}
import edu.ie3.simona.agent.state.AgentState.{Finish, Idle, Uninitialized}
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.GridAgentState.SimulateGrid
import edu.ie3.simona.agent.{EnvironmentRefs, SimonaAgent}
import edu.ie3.simona.config.SimonaConfig
Expand All @@ -25,6 +25,7 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.{
ScheduleTriggerMessage,
TriggerWithIdMessage
}
import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.ontology.trigger.Trigger.{
ActivityStartTrigger,
InitializeGridAgentTrigger,
Expand Down Expand Up @@ -243,7 +244,7 @@ class GridAgent(
)
)

case Event(Finish, data: GridAgentBaseData) =>
case Event(StopMessage(_), data: GridAgentBaseData) =>
// shutdown children
data.gridEnv.nodeToAssetAgents.foreach { case (_, actors) =>
actors.foreach(context.stop)
Expand Down
2 changes: 0 additions & 2 deletions src/main/scala/edu/ie3/simona/agent/state/AgentState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,4 @@ object AgentState {

case object Idle extends AgentState

case object Finish extends AgentState

}
12 changes: 11 additions & 1 deletion src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

package edu.ie3.simona.api

import akka.actor.{Actor, ActorRef, Props}
import akka.actor.{Actor, ActorRef, PoisonPill, Props}
import edu.ie3.simona.api.ExtMessageUtils.{
RichExtCompletion,
RichExtScheduleTrigger
Expand All @@ -18,6 +18,8 @@ import edu.ie3.simona.api.ExtSimAdapter.{
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.api.simulation.ontology.{
Terminate,
TerminationCompleted,
ActivityStartTrigger => ExtActivityStartTrigger,
CompletionMessage => ExtCompletionMessage
}
Expand All @@ -27,6 +29,7 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.{
ScheduleTriggerMessage,
TriggerWithIdMessage
}
import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.ontology.trigger.Trigger.{
ActivityStartTrigger,
InitializeExtSimAdapterTrigger
Expand Down Expand Up @@ -124,6 +127,13 @@ final case class ExtSimAdapter(scheduler: ActorRef)
)
scheduler ! scheduleDataService.toSimona(oldestTick)

case StopMessage(simulationSuccessful) =>
// let external sim know that we have terminated
stateData.extSimData.queueExtMsg(new Terminate(simulationSuccessful))

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

private def getOldestTickAndTriggerId(implicit
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* © 2022. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.ontology.messages

final case class StopMessage(simulationSuccessful: Boolean)
23 changes: 17 additions & 6 deletions src/main/scala/edu/ie3/simona/sim/SimonaSim.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import akka.actor.{
Actor,
ActorRef,
AllForOneStrategy,
PoisonPill,
Props,
Stash,
SupervisorStrategy,
Expand All @@ -21,8 +20,8 @@ import akka.pattern.after
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
import edu.ie3.simona.agent.state.AgentState.Finish
import edu.ie3.simona.ontology.messages.SchedulerMessage._
import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.ontology.trigger.Trigger.{
InitializeGridAgentTrigger,
InitializeServiceTrigger
Expand Down Expand Up @@ -182,19 +181,21 @@ class SimonaSim(simonaSetup: SimonaSetup)
scheduler ! StartScheduleMessage(pauseScheduleAtTick)

case msg @ (SimulationSuccessfulMessage | SimulationFailureMessage) =>
msg match {
val simulationSuccessful = msg match {
case SimulationSuccessfulMessage =>
logger.info(
"Simulation terminated successfully. Stopping children ..."
)
true
case SimulationFailureMessage =>
logger.error(
"An error occurred during the simulation. See stacktrace for details."
)
false
}

// stop all children
stopAllChildrenGracefully()
stopAllChildrenGracefully(simulationSuccessful)

// inform initSimMessage Sender
data.initSimSender ! msg
Expand All @@ -214,7 +215,7 @@ class SimonaSim(simonaSetup: SimonaSetup)
)

// stop all children
stopAllChildrenGracefully()
stopAllChildrenGracefully(simulationSuccessful = false)

// inform initSimMessage Sender
data.initSimSender ! SimulationFailureMessage
Expand All @@ -236,19 +237,29 @@ class SimonaSim(simonaSetup: SimonaSetup)
}

def stopAllChildrenGracefully(
simulationSuccessful: Boolean,
listenerDelay: FiniteDuration = 500.millis
): Unit = {
gridAgents.foreach { case (gridAgentRef, _) =>
context.unwatch(gridAgentRef)
gridAgentRef ! StopMessage(simulationSuccessful)
}
gridAgents.foreach(_._1 ! Finish)

context.unwatch(scheduler)
context.stop(scheduler)

context.unwatch(weatherService)
context.stop(weatherService)

extSimulationData.extSimAdapters.foreach { case (ref, _) =>
context.unwatch(ref)
ref ! StopMessage(simulationSuccessful)
}
extSimulationData.extDataServices.foreach { case (ref, _) =>
context.unwatch(ref)
context.stop(ref)
}

/* Stop listeners with a delay */
logger.debug("Waiting for {} to stop the listeners.", listenerDelay)
Await.ready(
Expand Down
55 changes: 54 additions & 1 deletion src/test/scala/edu/ie3/simona/api/ExtSimAdapterSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

package edu.ie3.simona.api

import akka.actor.ActorSystem
import akka.actor.{ActorSystem, Terminated}
import akka.testkit.{TestActorRef, TestProbe}
import com.typesafe.config.ConfigFactory
import edu.ie3.simona.api.ExtSimAdapter.InitExtSimAdapter
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.api.simulation.ontology.{
Terminate,
TerminationCompleted,
ActivityStartTrigger => ExtActivityStartTrigger,
CompletionMessage => ExtCompletionMessage
}
Expand All @@ -21,13 +23,15 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.{
ScheduleTriggerMessage,
TriggerWithIdMessage
}
import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.ontology.trigger.Trigger.{
ActivityStartTrigger,
InitializeExtSimAdapterTrigger
}
import edu.ie3.simona.test.common.TestKitWithShutdown
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import org.scalatest.wordspec.AnyWordSpecLike
import org.scalatest.prop.TableDrivenPropertyChecks._

import scala.concurrent.duration.DurationInt
import scala.jdk.CollectionConverters.SeqHasAsJava
Expand Down Expand Up @@ -215,6 +219,55 @@ class ExtSimAdapterSpec
)
dataService.expectNoMessage()
}

"terminate the external simulation and itself when told to" in {
forAll(Table("simSuccessful", true, false)) { (simSuccessful: Boolean) =>
val extSimAdapter = TestActorRef(
new ExtSimAdapter(scheduler.ref)
)

val extData = new ExtSimAdapterData(extSimAdapter, mainArgs)

scheduler.send(
extSimAdapter,
TriggerWithIdMessage(
InitializeExtSimAdapterTrigger(
InitExtSimAdapter(
extData
)
),
1L,
extSimAdapter
)
)

scheduler.expectMsgType[CompletionMessage]

val stopWatcher = TestProbe()
stopWatcher.watch(extSimAdapter)

extSimAdapter ! StopMessage(simSuccessful)

awaitCond(
!extData.receiveMessageQueue.isEmpty,
max = 3.seconds,
message = "No message received"
)
extData.receiveMessageQueue.size() shouldBe 1
extData.receiveMessageQueue.take() shouldBe new Terminate(simSuccessful)

// up until now, extSimAdapter should still be running
stopWatcher.expectNoMessage()

extSimAdapter ! new TerminationCompleted()

// extSimAdapter should have terminated now
stopWatcher.expectMsgType[Terminated].actor shouldBe extSimAdapter

// scheduler is not involved in this
scheduler.expectNoMessage()
}
}
}

}