diff --git a/src/main/scala/edu/ie3/simona/agent/participant2/ParticipantAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant2/ParticipantAgent.scala index ad712cfefd..84fc728c5b 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant2/ParticipantAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant2/ParticipantAgent.scala @@ -230,12 +230,9 @@ object ParticipantAgent { ): Behavior[Request] = Behaviors.receivePartial { case (ctx, request: ParticipantRequest) => + // ParticipantRequests are always directly answered + // without taking into account possible new input data val updatedShell = modelShell - .updateModelInput( - inputHandler.getData, - gridAdapter.nodalVoltage, - request.tick, - ) .handleRequest(ctx, request) ParticipantAgent( @@ -400,7 +397,7 @@ object ParticipantAgent { val (updatedShell, updatedGridAdapter) = Scope(modelShell) .map( - _.updateModelInput( + _.handleInputData( inputHandler.getData, gridAdapter.nodalVoltage, activation.tick, diff --git a/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModel.scala b/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModel.scala index 0a23f5bacb..6ee2f74e88 100644 --- a/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModel.scala @@ -13,7 +13,6 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{ } import edu.ie3.simona.agent.participant.data.Data import edu.ie3.simona.model.participant2.ParticipantModel.{ - ModelInput, ModelState, OperatingPoint, } @@ -70,7 +69,7 @@ abstract class ParticipantModel[ /** Determines the initial state given an initial model input. */ - val initialState: ModelInput => S + val initialState: (Long, ZonedDateTime) => S /** Determines the current state given the last state and the operating point * that has been valid from the last state up until now. @@ -80,17 +79,38 @@ abstract class ParticipantModel[ * @param operatingPoint * The operating point valid from the simulation time of the last state up * until now. - * @param input - * The model input data for the current tick. + * @param tick + * The current tick + * @param simulationTime + * The current simulation time * @return * The current state. */ def determineState( lastState: S, operatingPoint: OP, - input: ModelInput, + tick: Long, + simulationTime: ZonedDateTime, ): S + /** Handles input data (primary or secondary) by integrating into the current + * mode state. + * + * @param state + * The current state + * @param receivedData + * The received primary or secondary data. + * @param nodalVoltage + * The voltage at the node that we're connected to. + * @return + * The current state with updated input data + */ + def handleInput( + state: S, + receivedData: Seq[Data], + nodalVoltage: Dimensionless, + ): S = state + /** Returns a partial function that transfers the current nodal voltage and * active power into reactive power based on the participants properties. * @@ -191,24 +211,6 @@ abstract class ParticipantModel[ object ParticipantModel { - /** Holds all potentially relevant input data for model calculation. - * - * @param receivedData - * The received primary or secondary data. - * @param nodalVoltage - * The voltage at the node that we're connected to. - * @param currentTick - * The current tick. - * @param currentSimulationTime - * The current simulation time (matches the tick). - */ - final case class ModelInput( - receivedData: Seq[Data], - nodalVoltage: Dimensionless, - currentTick: Long, - currentSimulationTime: ZonedDateTime, - ) - trait OperatingPoint { val activePower: Power @@ -239,14 +241,15 @@ object ParticipantModel { ] { this: ParticipantModel[OP, FixedState] => - override val initialState: ModelInput => FixedState = - input => FixedState(input.currentTick) + override val initialState: (Long, ZonedDateTime) => FixedState = + (tick, _) => FixedState(tick) override def determineState( lastState: FixedState, operatingPoint: OP, - input: ModelInput, - ): FixedState = FixedState(input.currentTick) + tick: Long, + simulationTime: ZonedDateTime, + ): FixedState = FixedState(tick) } @@ -264,15 +267,16 @@ object ParticipantModel { ] { this: ParticipantModel[OP, DateTimeState] => - override val initialState: ModelInput => DateTimeState = input => - DateTimeState(input.currentTick, input.currentSimulationTime) + override val initialState: (Long, ZonedDateTime) => DateTimeState = + (tick, simulationTime) => DateTimeState(tick, simulationTime) override def determineState( lastState: DateTimeState, operatingPoint: OP, - input: ModelInput, + tick: Long, + simulationTime: ZonedDateTime, ): DateTimeState = - DateTimeState(input.currentTick, input.currentSimulationTime) + DateTimeState(tick, simulationTime) } diff --git a/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModelShell.scala b/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModelShell.scala index 0570ca602c..b45b9892db 100644 --- a/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModelShell.scala +++ b/src/main/scala/edu/ie3/simona/model/participant2/ParticipantModelShell.scala @@ -24,7 +24,6 @@ import edu.ie3.simona.exceptions.CriticalFailureException import edu.ie3.simona.model.SystemComponent import edu.ie3.simona.model.em.EmTools import edu.ie3.simona.model.participant2.ParticipantModel.{ - ModelInput, ModelState, OperatingPoint, OperationChangeIndicator, @@ -67,9 +66,6 @@ import scala.reflect.ClassTag * The date and time at which simulation started. * @param _state * The most recent model state, if one has been calculated already. - * @param _input - * The most recent model input used for calculation, if it has been received - * already. * @param _flexOptions * The most recent flex options, if they have been calculated already. * @param _lastOperatingPoint @@ -94,7 +90,6 @@ final case class ParticipantModelShell[ private val operationInterval: OperationInterval, private val simulationStartDate: ZonedDateTime, private val _state: Option[S] = None, - private val _input: Option[ModelInput] = None, private val _flexOptions: Option[ProvideFlexOptions] = None, private val _lastOperatingPoint: Option[OP] = None, private val _operatingPoint: Option[OP] = None, @@ -128,18 +123,6 @@ final case class ParticipantModelShell[ def requiredServices: Iterable[ServiceType] = model.getRequiredSecondaryServices - /** Returns the current relevant data, if present, or throws a - * [[CriticalFailureException]]. Only call this if you are certain the model - * input data has been set. - * - * @return - * The model input data. - */ - private def modelInput: ModelInput = - _input.getOrElse( - throw new CriticalFailureException("No relevant data available!") - ) - /** Returns the current operating point, if present, or throws a * [[CriticalFailureException]]. Only call this if you are certain the * operating point has been set. @@ -177,7 +160,7 @@ final case class ParticipantModelShell[ def reactivePowerFunc: Dimensionless => Power => ReactivePower = model.reactivePowerFunc - /** Updates the model input according to the received data, the current nodal + /** Updates the model state according to the received data, the current nodal * voltage and the current tick. * * @param receivedData @@ -189,23 +172,16 @@ final case class ParticipantModelShell[ * @return * An updated [[ParticipantModelShell]]. */ - def updateModelInput( + def handleInputData( receivedData: Seq[Data], nodalVoltage: Dimensionless, tick: Long, ): ParticipantModelShell[OP, S] = { - val currentSimulationTime = tick.toDateTime(simulationStartDate) - - copy(_input = - Some( - ModelInput( - receivedData, - nodalVoltage, - tick, - currentSimulationTime, - ) - ) - ) + val currentState = determineCurrentState(tick) + val updatedState = + model.handleInput(currentState, receivedData, nodalVoltage) + + copy(_state = Some(updatedState)) } /** Update operating point when the model is '''not''' em-controlled. @@ -454,13 +430,23 @@ final case class ParticipantModelShell[ private def determineCurrentState(tick: Long): S = { // new state is only calculated if there's an old state and an operating point val state = _state - .zip(_operatingPoint) - .flatMap { case (st, op) => - Option.when(st.tick < tick) { - model.determineState(st, op, modelInput) + .map { st => + if (st.tick < tick) { + // If the state is old, an operating point needs + // to be present to determine the curren state + model.determineState( + st, + operatingPoint, + tick, + tick.toDateTime(simulationStartDate), + ) + } else { + // The state is up-to-date, no need to update + st } } - .getOrElse(model.initialState(modelInput)) + // No state present, create an initial one + .getOrElse(model.initialState(tick, tick.toDateTime(simulationStartDate))) if (state.tick != tick) throw new CriticalFailureException( diff --git a/src/main/scala/edu/ie3/simona/model/participant2/PrimaryDataParticipantModel.scala b/src/main/scala/edu/ie3/simona/model/participant2/PrimaryDataParticipantModel.scala index e86302dd8c..101927f673 100644 --- a/src/main/scala/edu/ie3/simona/model/participant2/PrimaryDataParticipantModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant2/PrimaryDataParticipantModel.scala @@ -20,7 +20,6 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{ import edu.ie3.simona.exceptions.CriticalFailureException import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.participant2.ParticipantModel.{ - ModelInput, ModelState, OperatingPoint, OperationChangeIndicator, @@ -30,7 +29,7 @@ import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions import edu.ie3.simona.service.ServiceType import edu.ie3.util.scala.quantities.{ApparentPower, ReactivePower} -import squants.Power +import squants.{Dimensionless, Power} import java.time.ZonedDateTime import java.util.UUID @@ -62,22 +61,27 @@ final case class PrimaryDataParticipantModel[PD <: PrimaryData: ClassTag]( PrimaryDataState[PD], ] { - override val initialState: ModelInput => PrimaryDataState[PD] = { input => - val primaryData = getPrimaryData(input.receivedData) - PrimaryDataState( - primaryData, - input.currentTick, - ) + override val initialState: (Long, ZonedDateTime) => PrimaryDataState[PD] = { + (tick, _) => + PrimaryDataState( + primaryDataExtra.zero, + tick, + ) } override def determineState( lastState: PrimaryDataState[PD], operatingPoint: PrimaryOperatingPoint[PD], - input: ModelInput, - ): PrimaryDataState[PD] = initialState(input) + tick: Long, + simulationTime: ZonedDateTime, + ): PrimaryDataState[PD] = lastState.copy(tick = tick) - private def getPrimaryData(receivedData: Seq[Data]): PD = { - receivedData + override def handleInput( + state: PrimaryDataState[PD], + receivedData: Seq[Data], + nodalVoltage: Dimensionless, + ): PrimaryDataState[PD] = { + val primaryData = receivedData .collectFirst { case data: PD => data } @@ -88,6 +92,8 @@ final case class PrimaryDataParticipantModel[PD <: PrimaryData: ClassTag]( s"got $receivedData" ) } + + state.copy(data = primaryData) } override def determineOperatingPoint( diff --git a/src/main/scala/edu/ie3/simona/model/participant2/PvModel.scala b/src/main/scala/edu/ie3/simona/model/participant2/PvModel.scala index a79120b4b5..5ac0158706 100644 --- a/src/main/scala/edu/ie3/simona/model/participant2/PvModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant2/PvModel.scala @@ -22,7 +22,6 @@ import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.participant2.ParticipantFlexibility.ParticipantSimpleFlexibility import edu.ie3.simona.model.participant2.ParticipantModel.{ ActivePowerOperatingPoint, - ModelInput, ModelState, } import edu.ie3.simona.model.participant2.PvModel.PvState @@ -30,6 +29,7 @@ import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData import edu.ie3.simona.service.ServiceType import edu.ie3.util.quantities.PowerSystemUnits import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble +import edu.ie3.util.scala.quantities.DefaultQuantities.zeroWPerSM import edu.ie3.util.scala.quantities._ import squants._ import squants.space.{Degrees, SquareMeters} @@ -74,25 +74,30 @@ class PvModel private ( private val activationThreshold = sRated.toActivePower(cosPhiRated) * 0.001 * -1 - override val initialState: ModelInput => PvState = { input => - val weatherData = getWeatherData(input.receivedData) - - PvState( - input.currentTick, - input.currentSimulationTime, - weatherData.diffIrr, - weatherData.dirIrr, - ) + override val initialState: (Long, ZonedDateTime) => PvState = { + (tick, simulationTime) => + PvState( + tick, + simulationTime, + zeroWPerSM, + zeroWPerSM, + ) } override def determineState( lastState: PvState, operatingPoint: ActivePowerOperatingPoint, - input: ModelInput, - ): PvState = initialState(input) + tick: Long, + simulationTime: ZonedDateTime, + ): PvState = + lastState.copy(tick = tick, dateTime = simulationTime) - private def getWeatherData(receivedData: Seq[Data]): WeatherData = { - receivedData + override def handleInput( + state: PvState, + receivedData: Seq[Data], + nodalVoltage: Dimensionless, + ): PvState = { + val weatherData = receivedData .collectFirst { case weatherData: WeatherData => weatherData } @@ -101,6 +106,11 @@ class PvModel private ( s"Expected WeatherData, got $receivedData" ) } + + state.copy( + diffIrradiance = weatherData.diffIrr, + dirIrradiance = weatherData.dirIrr, + ) } /** Calculate the active power behaviour of the model. diff --git a/src/main/scala/edu/ie3/simona/model/participant2/WecModel.scala b/src/main/scala/edu/ie3/simona/model/participant2/WecModel.scala index 64e896daf8..6e0da86bf7 100644 --- a/src/main/scala/edu/ie3/simona/model/participant2/WecModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant2/WecModel.scala @@ -23,7 +23,6 @@ import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.participant2.ParticipantFlexibility.ParticipantSimpleFlexibility import edu.ie3.simona.model.participant2.ParticipantModel.{ ActivePowerOperatingPoint, - ModelInput, ModelState, } import edu.ie3.simona.model.participant2.WecModel.{ @@ -45,7 +44,7 @@ import squants.energy.Watts import squants.mass.{Kilograms, KilogramsPerCubicMeter} import squants.motion.{MetersPerSecond, Pressure} import squants.space.SquareMeters -import squants.thermal.JoulesPerKelvin +import squants.thermal.{Celsius, JoulesPerKelvin} import tech.units.indriya.unit.Units._ import java.time.ZonedDateTime @@ -67,12 +66,11 @@ class WecModel private ( with ParticipantSimpleFlexibility[WecState] with LazyLogging { - override val initialState: ModelInput => WecState = { input => - val weatherData = getWeatherData(input.receivedData) + override val initialState: (Long, ZonedDateTime) => WecState = { (tick, _) => WecState( - input.currentTick, - weatherData.windVel, - weatherData.temp, + tick, + MetersPerSecond(0d), + Celsius(0d), None, ) } @@ -80,11 +78,16 @@ class WecModel private ( override def determineState( lastState: WecState, operatingPoint: ActivePowerOperatingPoint, - input: ModelInput, - ): WecState = initialState(input) + tick: Long, + simulationTime: ZonedDateTime, + ): WecState = lastState.copy(tick = tick) - private def getWeatherData(receivedData: Seq[Data]): WeatherData = { - receivedData + override def handleInput( + state: WecState, + receivedData: Seq[Data], + nodalVoltage: Dimensionless, + ): WecState = { + val weatherData = receivedData .collectFirst { case weatherData: WeatherData => weatherData } @@ -93,6 +96,11 @@ class WecModel private ( s"Expected WeatherData, got $receivedData" ) } + + state.copy( + windVelocity = weatherData.windVel, + temperature = weatherData.temp, + ) } override def determineOperatingPoint( diff --git a/src/main/scala/edu/ie3/util/scala/quantities/DefaultQuantities.scala b/src/main/scala/edu/ie3/util/scala/quantities/DefaultQuantities.scala index 698f757eff..9248f35bb2 100644 --- a/src/main/scala/edu/ie3/util/scala/quantities/DefaultQuantities.scala +++ b/src/main/scala/edu/ie3/util/scala/quantities/DefaultQuantities.scala @@ -14,6 +14,8 @@ object DefaultQuantities { val zeroKW: Power = Kilowatts(0d) val zeroMW: Power = Megawatts(0d) + val zeroWPerSM: Irradiance = WattsPerSquareMeter(0d) + val zeroKVAr: ReactivePower = Kilovars(0d) val zeroMVAr: ReactivePower = Megavars(0d) diff --git a/src/test/scala/edu/ie3/simona/agent/participant2/MockParticipantModel.scala b/src/test/scala/edu/ie3/simona/agent/participant2/MockParticipantModel.scala index 42ea376447..9e3b47220f 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant2/MockParticipantModel.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant2/MockParticipantModel.scala @@ -7,6 +7,7 @@ package edu.ie3.simona.agent.participant2 import edu.ie3.datamodel.models.result.system.SystemParticipantResult +import edu.ie3.simona.agent.participant.data.Data import edu.ie3.simona.agent.participant.data.Data.{PrimaryData, SecondaryData} import edu.ie3.simona.agent.participant2.MockParticipantModel._ import edu.ie3.simona.agent.participant2.ParticipantAgent.ParticipantRequest @@ -22,7 +23,9 @@ import edu.ie3.util.scala.quantities.DefaultQuantities._ import edu.ie3.util.scala.quantities.{ApparentPower, Kilovoltamperes} import org.apache.pekko.actor.typed.ActorRef import org.apache.pekko.actor.typed.scaladsl.ActorContext -import squants.energy.{Kilowatts, Power} +import squants.Dimensionless +import squants.energy.{Energy, Kilowatts, Power} +import squants.time.Seconds import tech.units.indriya.ComparableQuantity import java.time.ZonedDateTime @@ -52,23 +55,41 @@ class MockParticipantModel( MockState, ] { - override val initialState: ModelInput => MockState = { input => - val maybeAdditionalPower = input.receivedData.collectFirst { - case data: MockSecondaryData => - data.additionalP - } - + override val initialState: (Long, ZonedDateTime) => MockState = { (tick, _) => MockState( - maybeAdditionalPower, - input.currentTick, + None, + zeroKWh, + tick, ) } override def determineState( lastState: MockState, operatingPoint: ActivePowerOperatingPoint, - input: ModelInput, - ): MockState = initialState(input) + tick: Long, + simulationTime: ZonedDateTime, + ): MockState = { + val energySinceLastState = + operatingPoint.activePower * Seconds(tick - lastState.tick) + + lastState.copy( + tick = tick, + countedEnergy = lastState.countedEnergy + energySinceLastState, + ) + } + + override def handleInput( + state: MockState, + receivedData: Seq[Data], + nodalVoltage: Dimensionless, + ): MockState = { + val maybeAdditionalPower = receivedData.collectFirst { + case data: MockSecondaryData => + data.additionalP + } + + state.copy(additionalP = maybeAdditionalPower) + } override def determineOperatingPoint( state: MockState @@ -146,7 +167,7 @@ class MockParticipantModel( ): MockState = { msg match { case MockRequestMessage(_, replyTo) => - replyTo ! MockResponseMessage + replyTo ! MockResponseMessage(state.countedEnergy) } state @@ -156,14 +177,20 @@ class MockParticipantModel( object MockParticipantModel { - /** Simple [[ModelState]] to test its usage in operation point calculations + /** Simple [[ModelState]] to test its usage in operation point calculations. + * Produced and consumed energy is counted in order to test the handling of + * states. * * @param additionalP * Power value that is added to the power or flex options power for testing * purposes + * @param countedEnergy + * The counted produced and consumed energy since beginning of the + * simulation */ final case class MockState( additionalP: Option[Power], + countedEnergy: Energy, override val tick: Long, ) extends ModelState @@ -176,10 +203,17 @@ object MockParticipantModel { final case class MockRequestMessage( override val tick: Long, - replyTo: ActorRef[MockResponseMessage.type], + replyTo: ActorRef[MockResponseMessage], ) extends ParticipantRequest - case object MockResponseMessage + /** Mock response message that also enables testing of state handling + * + * @param countedEnergy + * The counted energy per current state + */ + final case class MockResponseMessage( + countedEnergy: Energy + ) final case class MockSecondaryData(additionalP: Power) extends SecondaryData diff --git a/src/test/scala/edu/ie3/simona/agent/participant2/ParticipantAgentSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant2/ParticipantAgentSpec.scala index bc5fe13f39..3a91ee0c8e 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant2/ParticipantAgentSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant2/ParticipantAgentSpec.scala @@ -47,11 +47,12 @@ import edu.ie3.simona.util.TickUtil.TickLong import edu.ie3.util.TimeUtil import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble import edu.ie3.util.scala.OperationInterval +import edu.ie3.util.scala.quantities.DefaultQuantities.zeroKWh import edu.ie3.util.scala.quantities.{Kilovars, ReactivePower} import org.apache.pekko.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import org.apache.pekko.actor.typed.ActorRef import org.apache.pekko.actor.typed.scaladsl.adapter._ -import squants.energy.Kilowatts +import squants.energy.{KilowattHours, Kilowatts} import squants.{Each, Power} import java.time.ZonedDateTime @@ -83,7 +84,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val scheduler = createTestProbe[SchedulerMessage]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() - val responseReceiver = createTestProbe[MockResponseMessage.type]() + val responseReceiver = createTestProbe[MockResponseMessage]() // receiving the activation adapter val receiveAdapter = createTestProbe[ActorRef[Activation]]() @@ -119,8 +120,11 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 8 * 3600: Start of operation interval - participantAgent ! MockRequestMessage(0, responseReceiver.ref) - responseReceiver.expectMessage(MockResponseMessage) + participantAgent ! MockRequestMessage( + operationInterval.start, + responseReceiver.ref, + ) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) activationRef ! Activation(operationInterval.start) @@ -138,8 +142,8 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: GridAgent requests power - participantAgent ! MockRequestMessage(0, responseReceiver.ref) - responseReceiver.expectMessage(MockResponseMessage) + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(24))) // first request participantAgent ! RequestAssetPowerMessage( @@ -187,10 +191,15 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { participantAgent ! GridSimulationFinished(12 * 3600, 24 * 3600) + // TICK 14 * 3600: Mock request + + participantAgent ! MockRequestMessage(14 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(36))) + // TICK 20 * 3600: Outside of operation interval (last tick) - participantAgent ! MockRequestMessage(0, responseReceiver.ref) - responseReceiver.expectMessage(MockResponseMessage) + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(72))) activationRef ! Activation(operationInterval.end) @@ -206,8 +215,8 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 24 * 3600: GridAgent requests power - participantAgent ! MockRequestMessage(0, responseReceiver.ref) - responseReceiver.expectMessage(MockResponseMessage) + participantAgent ! MockRequestMessage(24 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(72))) participantAgent ! RequestAssetPowerMessage( 24 * 3600, @@ -231,6 +240,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val scheduler = createTestProbe[SchedulerMessage]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() + val responseReceiver = createTestProbe[MockResponseMessage]() // receiving the activation adapter val receiveAdapter = createTestProbe[ActorRef[Activation]]() @@ -272,6 +282,12 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 8 * 3600: Start of operation interval + participantAgent ! MockRequestMessage( + operationInterval.start, + responseReceiver.ref, + ) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + activationRef ! Activation(operationInterval.start) resultListener.expectMessageType[ParticipantResultEvent] match { @@ -288,6 +304,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: Inside of operation interval and GridAgent requests power + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(24))) + activationRef ! Activation(12 * 3600) participantAgent ! RequestAssetPowerMessage( @@ -322,8 +341,16 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { Completion(activationRef, Some(operationInterval.end)) ) + // TICK 14 * 3600: Mock request + + participantAgent ! MockRequestMessage(14 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(36))) + // TICK 20 * 3600: Outside of operation interval (last tick) + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(72))) + activationRef ! Activation(operationInterval.end) resultListener.expectMessageType[ParticipantResultEvent] match { @@ -338,6 +365,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 24 * 3600: GridAgent requests power + participantAgent ! MockRequestMessage(24 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(72))) + participantAgent ! RequestAssetPowerMessage( 24 * 3600, Each(1), @@ -364,6 +394,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val scheduler = createTestProbe[SchedulerMessage]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() + val responseReceiver = createTestProbe[MockResponseMessage]() val service = createTestProbe() // receiving the activation adapter @@ -406,6 +437,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 0: Outside of operation interval + participantAgent ! MockRequestMessage(0, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + activationRef ! Activation(0) // nothing should happen, still waiting for secondary data... @@ -436,6 +470,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 6 * 3600: Outside of operation interval, only data expected, no activation + participantAgent ! MockRequestMessage(6 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + participantAgent ! DataProvision( 6 * 3600, service.ref.toClassic, @@ -448,6 +485,12 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 8 * 3600: Start of operation interval + participantAgent ! MockRequestMessage( + operationInterval.start, + responseReceiver.ref, + ) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + activationRef ! Activation(operationInterval.start) resultListener.expectMessageType[ParticipantResultEvent] match { @@ -465,6 +508,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: Inside of operation interval, secondary data and GridAgent requests power + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(36))) + activationRef ! Activation(12 * 3600) participantAgent ! RequestAssetPowerMessage( @@ -510,6 +556,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 15 * 3600: Inside of operation interval, but empty input data received + participantAgent ! MockRequestMessage(15 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(72))) + activationRef ! Activation(15 * 3600) // nothing should happen, still waiting for secondary data... @@ -531,6 +580,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 18 * 3600: Inside of operation interval because of expected secondary data + participantAgent ! MockRequestMessage(18 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(108))) + activationRef ! Activation(18 * 3600) // nothing should happen, still waiting for secondary data... @@ -559,6 +611,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 20 * 3600: Outside of operation interval (last tick) + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(138))) + activationRef ! Activation(operationInterval.end) resultListener.expectMessageType[ParticipantResultEvent] match { @@ -574,6 +629,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 24 * 3600: GridAgent requests power + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(138))) + participantAgent ! RequestAssetPowerMessage( 24 * 3600, Each(1), @@ -826,6 +884,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val em = createTestProbe[FlexResponse]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() + val responseReceiver = createTestProbe[MockResponseMessage]() // receiving the activation adapter val receiveAdapter = createTestProbe[ActorRef[FlexRequest]]() @@ -860,6 +919,12 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 8 * 3600: Start of operation interval + participantAgent ! MockRequestMessage( + operationInterval.start, + responseReceiver.ref, + ) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + flexRef ! FlexActivation(operationInterval.start) em.expectMessageType[ProvideMinMaxFlexOptions] match { @@ -905,6 +970,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: GridAgent requests power + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(12))) + participantAgent ! RequestAssetPowerMessage( 12 * 3600, Each(1), @@ -923,6 +991,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 20 * 3600: Outside of operation interval (last tick) + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(36))) + flexRef ! FlexActivation(operationInterval.end) em.expectMessageType[ProvideMinMaxFlexOptions] match { @@ -986,6 +1057,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val em = createTestProbe[FlexResponse]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() + val responseReceiver = createTestProbe[MockResponseMessage]() // receiving the activation adapter val receiveAdapter = createTestProbe[ActorRef[FlexRequest]]() @@ -1076,6 +1148,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: Inside of operation interval and GridAgent requests power + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(12))) + flexRef ! FlexActivation(12 * 3600) participantAgent ! RequestAssetPowerMessage( @@ -1181,6 +1256,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 24 * 3600: GridAgent requests power + participantAgent ! MockRequestMessage(24 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(20))) + participantAgent ! RequestAssetPowerMessage( 24 * 3600, Each(1), @@ -1208,6 +1286,7 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { val em = createTestProbe[FlexResponse]() val gridAgent = createTestProbe[GridAgent.Request]() val resultListener = createTestProbe[ResultEvent]() + val responseReceiver = createTestProbe[MockResponseMessage]() val service = createTestProbe() // receiving the activation adapter @@ -1254,6 +1333,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 0: Outside of operation interval + participantAgent ! MockRequestMessage(0, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + flexRef ! FlexActivation(0) // nothing should happen, still waiting for secondary data... @@ -1313,6 +1395,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 6 * 3600: Outside of operation interval, only data expected, no activation + participantAgent ! MockRequestMessage(6 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + participantAgent ! DataProvision( 6 * 3600, service.ref.toClassic, @@ -1325,6 +1410,12 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 8 * 3600: Start of operation interval + participantAgent ! MockRequestMessage( + operationInterval.start, + responseReceiver.ref, + ) + responseReceiver.expectMessage(MockResponseMessage(zeroKWh)) + flexRef ! FlexActivation(operationInterval.start) em.expectMessageType[ProvideMinMaxFlexOptions] match { @@ -1371,6 +1462,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 12 * 3600: Inside of operation interval, GridAgent requests power + participantAgent ! MockRequestMessage(12 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(12))) + flexRef ! FlexActivation(12 * 3600) participantAgent ! RequestAssetPowerMessage( @@ -1444,6 +1538,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 18 * 3600: Inside of operation interval because of expected secondary data + participantAgent ! MockRequestMessage(18 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(30))) + flexRef ! FlexActivation(18 * 3600) // nothing should happen, still waiting for secondary data... @@ -1502,6 +1599,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 20 * 3600: Outside of operation interval (last tick) + participantAgent ! MockRequestMessage(20 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(42))) + flexRef ! FlexActivation(operationInterval.end) em.expectMessageType[ProvideMinMaxFlexOptions] match { @@ -1543,6 +1643,9 @@ class ParticipantAgentSpec extends ScalaTestWithActorTestKit with UnitSpec { // TICK 24 * 3600: GridAgent requests power + participantAgent ! MockRequestMessage(24 * 3600, responseReceiver.ref) + responseReceiver.expectMessage(MockResponseMessage(KilowattHours(42))) + participantAgent ! RequestAssetPowerMessage( 24 * 3600, Each(1),