diff --git a/CHANGELOG.md b/CHANGELOG.md index 78de049d13..66ee1da0a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Await and send responses for distinct pairs of sender reference and target node - Removed deprecations from `CsvGridSource` and added `TestGridFactory` [#304](https://github.com/ie3-institute/simona/issues/304) - Fixed config of vn_146_lv_small [#290](https://github.com/ie3-institute/simona/issues/290) +- Adapted to changes of EvcsInput in PSDM [#377](https://github.com/ie3-institute/simona/pull/377) ### Removed - Remove workaround for tscfg tmp directory [#178](https://github.com/ie3-institute/simona/issues/178) diff --git a/input/samples/pcm/grid/evcs_input.csv b/input/samples/pcm/grid/evcs_input.csv index f41f889588..fc3c766bf7 100644 --- a/input/samples/pcm/grid/evcs_input.csv +++ b/input/samples/pcm/grid/evcs_input.csv @@ -1,7 +1,7 @@ -uuid,cos_phi_rated,id,node,operates_from,operates_until,operator,q_characteristics,chargingpoints,type,location_type -925c13f9-f82a-4010-ac12-92e1e8a0f419,1.0,evcs_home,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,home -06ca05f8-e4ac-4d4d-ad1c-67e6604b395a,1.0,evcs_work,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,work -d625249e-8e60-4318-837e-a0cfa9b91f3e,1.0,evcs_customer_parking,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,customer_parking -9c20507d-f5d5-4d61-b98d-0542e7d68f1b,1.0,evcs_street,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,street -58b78eed-ae99-44b5-b817-2e990018edb9,1.0,evcs_charging_hub_town,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,charging_hub_town -6e219da2-b233-48e7-9960-6aad29b39dc8,1.0,evcs_charging_hub_highway,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,charging_hub_highway \ No newline at end of file +uuid,cos_phi_rated,id,node,operates_from,operates_until,operator,q_characteristics,chargingpoints,type,location_type,"v2gsupport" +925c13f9-f82a-4010-ac12-92e1e8a0f419,1.0,evcs_home,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,home,true +06ca05f8-e4ac-4d4d-ad1c-67e6604b395a,1.0,evcs_work,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,work,true +d625249e-8e60-4318-837e-a0cfa9b91f3e,1.0,evcs_customer_parking,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,customer_parking,true +9c20507d-f5d5-4d61-b98d-0542e7d68f1b,1.0,evcs_street,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,street,true +58b78eed-ae99-44b5-b817-2e990018edb9,1.0,evcs_charging_hub_town,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,charging_hub_town,true +6e219da2-b233-48e7-9960-6aad29b39dc8,1.0,evcs_charging_hub_highway,2a5e9609-b66c-428d-a67a-a1437130ffb0,,,,cosPhiFixed:{(0.00,1.0)},100000,cee16,charging_hub_highway,true \ No newline at end of file diff --git a/input/samples/vn_simona/fullGrid/evcs_input.csv b/input/samples/vn_simona/fullGrid/evcs_input.csv index f4ccae2028..fbddc47be4 100644 --- a/input/samples/vn_simona/fullGrid/evcs_input.csv +++ b/input/samples/vn_simona/fullGrid/evcs_input.csv @@ -1,3 +1,3 @@ -"uuid","cos_phi_rated","id","node","operates_from","operates_until","operator","q_characteristics","chargingpoints","type","location_type" -06a14909-366e-4e94-a593-1016e1455b30,0.9,test_evcs_1,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME -104acdaa-5dc5-4197-aed2-2fddb3c4f237,0.9,test_evcs_2,ed4697fd-016c-40c2-a66b-e793878dadea,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME \ No newline at end of file +"uuid","cos_phi_rated","id","node","operates_from","operates_until","operator","q_characteristics","chargingpoints","type","location_type","v2gsupport" +06a14909-366e-4e94-a593-1016e1455b30,0.9,test_evcs_1,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME,true +104acdaa-5dc5-4197-aed2-2fddb3c4f237,0.9,test_evcs_2,ed4697fd-016c-40c2-a66b-e793878dadea,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME,true \ No newline at end of file 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 a24a9d9175..872ca4f480 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -285,7 +285,7 @@ protected trait ParticipantAgentFundamentals[ try { /* Register for services */ val awaitRegistrationResponsesFrom = - registerForServices(inputModel, services) + registerForServices(inputModel, services, self, maybeEmAgent) // always request flex options for first sim tick maybeEmAgent.foreach { 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 57afc16217..872389dcce 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala @@ -24,8 +24,10 @@ import edu.ie3.simona.model.participant.{ ModelState, SystemParticipant } +import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleTriggerMessage import edu.ie3.simona.ontology.messages.services.EvMessage.RegisterForEvDataMessage import edu.ie3.simona.ontology.messages.services.WeatherMessage.RegisterForWeatherMessage +import edu.ie3.simona.ontology.trigger.Trigger.ActivityStartTrigger trait ServiceRegistration[ PD <: PrimaryDataWithApparentPower[PD], @@ -45,20 +47,31 @@ trait ServiceRegistration[ * Input model definition * @param services * Definition of where to get what + * @param agentRef + * The participant agent actor + * @param maybeEmAgent + * The EmAgent if the participant is controlled by an EMS * @return * a vector of actor references to wait for responses */ def registerForServices( inputModel: I, - services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]] - ): Vector[ActorRef] = + services: Option[Seq[SecondaryDataService[_ <: SecondaryData]]], + agentRef: ActorRef, + maybeEmAgent: Option[ActorRef] + ): Seq[ActorRef] = services .map(sources => sources.flatMap(service => - registerForSecondaryService(service, inputModel) + registerForSecondaryService( + service, + inputModel, + agentRef, + maybeEmAgent + ) ) ) - .getOrElse(Vector.empty[ActorRef]) + .getOrElse(Seq.empty[ActorRef]) /** Register for the distinct secondary service * @@ -66,6 +79,10 @@ trait ServiceRegistration[ * Definition of the service * @param inputModel * Input model that is interested in the information + * @param agentRef + * The participant agent actor + * @param maybeEmAgent + * The EmAgent if the participant is controlled by an EMS * @tparam S * Type of the secondary data, that is awaited * @return @@ -76,7 +93,9 @@ trait ServiceRegistration[ S <: SecondaryData ]( serviceDefinition: SecondaryDataService[S], - inputModel: I + inputModel: I, + agentRef: ActorRef, + maybeEmAgent: Option[ActorRef] ): Option[ActorRef] = serviceDefinition match { case SecondaryDataService.ActorPriceService(_) => log.debug( @@ -84,12 +103,12 @@ trait ServiceRegistration[ ActorPriceService ) None - case ActorWeatherService(actorRef) => - registerForWeather(actorRef, inputModel) - Some(actorRef) - case ActorEvMovementsService(actorRef) => - registerForEvMovements(actorRef, inputModel) - Some(actorRef) + case ActorWeatherService(serviceRef) => + registerForWeather(serviceRef, inputModel) + Some(serviceRef) + case ActorEvMovementsService(serviceRef) => + registerForEvMovements(serviceRef, inputModel, agentRef, maybeEmAgent) + Some(serviceRef) } /** Register for the weather service @@ -122,19 +141,42 @@ trait ServiceRegistration[ /** Register for the EV movement service * - * @param actorRef + * @param serviceRef * Actor reference of the EV movements service * @param inputModel * Input model of the simulation mode + * @param maybeEmAgent + * The EmAgent if the participant is controlled by an EMS * @return */ private def registerForEvMovements( - actorRef: ActorRef, - inputModel: I + serviceRef: ActorRef, + inputModel: I, + evcsRef: ActorRef, + maybeEmAgent: Option[ActorRef] ): Unit = { inputModel match { case evcsInput: EvcsInput => - actorRef ! RegisterForEvDataMessage(evcsInput.getUuid) + val scheduleParticipant = (tick: Long) => + ScheduleTriggerMessage( + ActivityStartTrigger( + tick + ), + evcsRef + ) + + // when using an EmAgent, activation schedules have to be stacked + val scheduleFunc = (tick: Long) => + maybeEmAgent + .map { emAgent => + ScheduleTriggerMessage( + scheduleParticipant(tick), + emAgent + ) + } + .getOrElse(scheduleParticipant(tick)) + + serviceRef ! RegisterForEvDataMessage(evcsInput.getUuid, scheduleFunc) case _ => throw new ServiceRegistrationException( s"Cannot register for EV movements information at node ${inputModel.getNode.getId} " + diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala index c6b392cf63..2145d3ec7d 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala @@ -143,7 +143,7 @@ object ParticipantStateData { +PD <: PrimaryDataWithApparentPower[PD] ]( baseStateData: BaseStateData[PD], - pendingResponses: Vector[ActorRef], + pendingResponses: Seq[ActorRef], foreseenNextDataTicks: Map[ActorRef, Long] = Map.empty ) extends ParticipantStateData[PD] } diff --git a/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala index 6c39b0ce88..4542effcec 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala @@ -96,6 +96,7 @@ final case class EvcsModel( cosPhiRated: Double, chargingPoints: Int, locationType: EvcsLocationType, + vehicle2grid: Boolean, strategy: ChargingStrategy.Value ) extends SystemParticipant[EvcsRelevantData, EvcsState]( uuid, @@ -602,7 +603,7 @@ final case class EvcsModel( zeroKW val maxDischarging = - if (!isEmpty(ev)) + if (!isEmpty(ev) && vehicle2grid) maxPower.multiply(-1) else zeroKW @@ -970,6 +971,7 @@ object EvcsModel { inputModel.getCosPhiRated, inputModel.getChargingPoints, inputModel.getLocationType, + inputModel.getV2gSupport, ChargingStrategy(chargingStrategy) ) } @@ -1013,6 +1015,7 @@ object EvcsModel { cosPhiRated: Double, chargingPoints: Int, locationType: EvcsLocationType, + vehicle2grid: Boolean, chargingStrategy: ChargingStrategy.Value ): EvcsModel = { val model = new EvcsModel( @@ -1027,6 +1030,7 @@ object EvcsModel { cosPhiRated, chargingPoints, locationType, + vehicle2grid, chargingStrategy ) diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala index f1064b4208..1dc93d2aac 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala @@ -31,7 +31,10 @@ object SchedulerMessage { ) extends SchedulerMessage /** schedule a new trigger TO the [[SimScheduler]]. This message should send - * only to the [[SimScheduler]] + * only to the [[SimScheduler]]. + * + * Interface Trigger is extended so that ScheduleTriggerMessages can be + * stacked. * * @param trigger * to schedule @@ -42,6 +45,9 @@ object SchedulerMessage { trigger: Trigger, actorToBeScheduled: ActorRef ) extends SchedulerMessage + with Trigger { + override def tick: Long = trigger.tick + } /** Confirm the end of an action e.g. fsm state transitions for one tick to * and ONLY to the [[SimScheduler]] 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 e47c8ee2cf..2fa4e10586 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 @@ -9,6 +9,7 @@ package edu.ie3.simona.ontology.messages.services import edu.ie3.simona.agent.participant.data.Data.SecondaryData import edu.ie3.simona.api.data.ev.model.EvModel import edu.ie3.simona.api.data.ev.ontology.EvMovementsMessage.EvcsMovements +import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleTriggerMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ ProvisionMessage, ServiceRegistrationMessage @@ -25,9 +26,12 @@ object EvMessage { * * @param evcs * the charging station + * @param scheduleFunc + * function providing the proper ScheduleTriggerMessage for a given tick */ final case class RegisterForEvDataMessage( - evcs: UUID + evcs: UUID, + scheduleFunc: Long => ScheduleTriggerMessage ) extends EvMessage with ServiceRegistrationMessage 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 d3327ff49f..bc28a71247 100644 --- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala +++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala @@ -27,7 +27,6 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleTriggerMessage 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.trigger.Trigger.ActivityStartTrigger import edu.ie3.simona.service.ServiceStateData.{ InitializeServiceStateData, ServiceBaseStateData @@ -55,7 +54,8 @@ object ExtEvDataService { final case class ExtEvStateData( extEvData: ExtEvData, - uuidToActorRef: Map[UUID, ActorRef] = Map.empty[UUID, ActorRef], + uuidToActorRef: Map[UUID, (ActorRef, Long => ScheduleTriggerMessage)] = + Map.empty, extEvMessage: Option[ExtEvMessage] = None, freeLots: Map[UUID, Option[Int]] = Map.empty, evModelResponses: Map[UUID, Option[List[EvModel]]] = Map.empty, @@ -66,7 +66,6 @@ object ExtEvDataService { extEvData: ExtEvData ) extends InitializeServiceStateData - val FALLBACK_EV_MOVEMENTS_STEM_DISTANCE: Long = 3600L } class ExtEvDataService(override val scheduler: ActorRef) @@ -116,8 +115,8 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData: ExtEvStateData ): Try[ExtEvStateData] = registrationMessage match { - case RegisterForEvDataMessage(evcs) => - Success(handleRegistrationRequest(sender(), evcs)) + case RegisterForEvDataMessage(evcs, scheduleFunc) => + Success(handleRegistrationRequest(sender(), evcs, scheduleFunc)) case invalidMessage => Failure( InvalidRegistrationRequestException( @@ -134,6 +133,8 @@ class ExtEvDataService(override val scheduler: ActorRef) * the agent that wants to be registered * @param evcs * the charging station + * @param scheduleFunc + * function providing the proper ScheduleTriggerMessage for a given tick * @param serviceStateData * the current service state data of this service * @return @@ -142,7 +143,8 @@ class ExtEvDataService(override val scheduler: ActorRef) */ private def handleRegistrationRequest( agentToBeRegistered: ActorRef, - evcs: UUID + evcs: UUID, + scheduleFunc: Long => ScheduleTriggerMessage )(implicit serviceStateData: ExtEvStateData ): ExtEvStateData = { @@ -159,7 +161,7 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData.copy( uuidToActorRef = - serviceStateData.uuidToActorRef + (evcs -> agentToBeRegistered) + serviceStateData.uuidToActorRef + (evcs -> (agentToBeRegistered, scheduleFunc)) ) case Some(_) => // actor is already registered, do nothing @@ -205,7 +207,7 @@ class ExtEvDataService(override val scheduler: ActorRef) private def requestFreeLots(tick: Long)(implicit serviceStateData: ExtEvStateData ): (ExtEvStateData, Option[Seq[ScheduleTriggerMessage]]) = { - serviceStateData.uuidToActorRef.foreach { case (_, evcsActor) => + serviceStateData.uuidToActorRef.values.foreach { case (evcsActor, _) => evcsActor ! EvFreeLotsRequest(tick) } @@ -231,17 +233,15 @@ class ExtEvDataService(override val scheduler: ActorRef) serviceStateData: ExtEvStateData ): (ExtEvStateData, Option[Seq[ScheduleTriggerMessage]]) = { val scheduleTriggerMsgs = - serviceStateData.uuidToActorRef.map { case (_, evcsActor) => - evcsActor ! ProvideEvDataMessage( - tick, - CurrentPriceRequest - ) + serviceStateData.uuidToActorRef.map { + case (_, (evcsActor, scheduleFunc)) => + evcsActor ! ProvideEvDataMessage( + tick, + CurrentPriceRequest + ) - // schedule activation of participant - ScheduleTriggerMessage( - ActivityStartTrigger(tick), - evcsActor - ) + // schedule activation of participant + scheduleFunc(tick) } val currentPrices: Map[UUID, Option[Double]] = @@ -271,8 +271,8 @@ class ExtEvDataService(override val scheduler: ActorRef) val filteredPositionData = evMovements.asScala.flatMap { case (evcs, movements) => serviceStateData.uuidToActorRef.get(evcs) match { - case Some(evcsActor) => - Some(evcs, evcsActor, movements) + case Some((evcsActor, scheduleFunc)) => + Some(evcs, evcsActor, scheduleFunc, movements) case None => log.warning( "A corresponding actor ref for UUID {} could not be found", @@ -283,21 +283,18 @@ class ExtEvDataService(override val scheduler: ActorRef) } val scheduleTriggerMsgs = - filteredPositionData.map { case (_, evcsActor, movements) => + filteredPositionData.map { case (_, evcsActor, scheduleFunc, movements) => evcsActor ! ProvideEvDataMessage( tick, EvMovementData(movements) ) // schedule activation of participant - ScheduleTriggerMessage( - ActivityStartTrigger(tick), - evcsActor - ) + scheduleFunc(tick) } val departingVehicles: Map[UUID, Option[List[EvModel]]] = - filteredPositionData.flatMap { case (evcs, _, movements) => + filteredPositionData.flatMap { case (evcs, _, _, movements) => val containsDeparture = !movements.getDepartures.isEmpty // only when positions message contains departure, 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 55a535342c..f516e32e1d 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala @@ -277,9 +277,12 @@ class EvcsAgentModelCalculationSpec primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage) /* Expect a registration message */ - evService.expectMsg( - RegisterForEvDataMessage(evcsInputModel.getUuid) - ) + evService.expectMsgPF() { + case RegisterForEvDataMessage(uuid, scheduleFunc) => + uuid shouldBe evcsInputModel.getUuid + scheduleFunc(3L) shouldBe + ScheduleTriggerMessage(ActivityStartTrigger(3L), evcsAgent) + } /* ... as well as corresponding state and state data */ evcsAgent.stateName shouldBe HandleInformation @@ -398,9 +401,12 @@ class EvcsAgentModelCalculationSpec primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage) /* Expect a registration message */ - evService.expectMsg( - RegisterForEvDataMessage(evcsInputModel.getUuid) - ) + evService.expectMsgPF() { + case RegisterForEvDataMessage(uuid, scheduleFunc) => + uuid shouldBe evcsInputModel.getUuid + scheduleFunc(3L) shouldBe + ScheduleTriggerMessage(ActivityStartTrigger(3L), evcsAgent) + } evService.send(evcsAgent, RegistrationSuccessfulMessage(Some(900L))) /* I'm not interested in the content of the CompletionMessage */ @@ -1217,8 +1223,15 @@ class EvcsAgentModelCalculationSpec ) ) - evService.expectMsg(RegisterForEvDataMessage(evcsInputModel.getUuid)) - + evService.expectMsgPF() { + case RegisterForEvDataMessage(uuid, scheduleFunc) => + uuid shouldBe evcsInputModel.getUuid + scheduleFunc(3L) shouldBe + ScheduleTriggerMessage( + ScheduleTriggerMessage(ActivityStartTrigger(3L), evcsAgent), + emAgent.ref + ) + } evService.send(evcsAgent, RegistrationSuccessfulMessage(None)) emAgent.expectMsg( @@ -1353,8 +1366,15 @@ class EvcsAgentModelCalculationSpec ) ) - evService.expectMsg(RegisterForEvDataMessage(evcsInputModel.getUuid)) - + evService.expectMsgPF() { + case RegisterForEvDataMessage(uuid, scheduleFunc) => + uuid shouldBe evcsInputModel.getUuid + scheduleFunc(4L) shouldBe + ScheduleTriggerMessage( + ScheduleTriggerMessage(ActivityStartTrigger(4L), evcsAgent), + emAgent.ref + ) + } evService.send(evcsAgent, RegistrationSuccessfulMessage(None)) emAgent.expectMsg( diff --git a/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala index 587954dc2c..41c718db49 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala @@ -37,6 +37,7 @@ class EvcsModelSpec private val simulationStart = evcsStandardModel.simulationStartDate + // TODO some conditions/functions have not been tested yet "An EVCS model" should { "calculate new schedules correctly" when { @@ -356,6 +357,58 @@ class EvcsModelSpec } + "calculate flex options for an evcs without vehicle2grid correctly" in { + val evcsModel = evcsStandardModel.copy( + vehicle2grid = false, + strategy = ChargingStrategy.CONSTANT_POWER + ) + + val currentTick = 7200L + + val data = EvcsRelevantData( + currentTick, + new EvcsMovementsBuilder().build(), + Map.empty + ) + + val ev1 = new MockEvModel( + UUID.randomUUID(), + "Mock EV 1", + 10.0.asKiloWatt, // AC is relevant, + 20.0.asKiloWatt, // DC is not + 10.0.asKiloWattHour, + 0.0.asKiloWattHour, + 10800L + ) + + val schedule1 = ChargingSchedule( + ev1, + Seq(ChargingSchedule.Entry(3600L, 7200L, 5.0.asKiloWatt)) + ) + + evcsModel.determineFlexOptions( + data, + EvcsState( + Set(ev1), + Map(ev1 -> Some(schedule1)), + Set.empty, + 0L + ) + ) match { + case ProvideMinMaxFlexOptions( + modelUuid, + refPower, + minPower, + maxPower + ) => + modelUuid shouldBe evcsModel.getUuid + refPower should equalWithTolerance(5.0.asKiloWatt) // one hour left + minPower should equalWithTolerance(zeroKW) // no v2g allowed! + maxPower should equalWithTolerance(ev1.getSRatedAC) + } + + } + "handle controlled power change for two evs correctly" in { val currentTick = 3600L 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 63a1ec44fd..478087c143 100644 --- a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala +++ b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala @@ -42,6 +42,7 @@ import edu.ie3.simona.ontology.trigger.Trigger.{ InitializeServiceTrigger } import edu.ie3.simona.service.ev.ExtEvDataService.InitExtEvData +import edu.ie3.simona.service.ev.ExtEvDataServiceSpec.scheduleFunc import edu.ie3.simona.test.common.{EvTestData, TestKitWithShutdown} import edu.ie3.util.quantities.PowerSystemUnits import org.scalatest.wordspec.AnyWordSpecLike @@ -49,7 +50,7 @@ import tech.units.indriya.quantity.Quantities import java.util.UUID import scala.concurrent.duration.DurationInt -import scala.jdk.CollectionConverters.{MapHasAsJava, SeqHasAsJava} +import scala.jdk.CollectionConverters._ class ExtEvDataServiceSpec extends TestKitWithShutdown( @@ -122,7 +123,7 @@ class ExtEvDataServiceSpec // this one should be stashed evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectNoMessage() @@ -173,20 +174,20 @@ class ExtEvDataServiceSpec evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectMsg(RegistrationSuccessfulMessage(None)) evcs2.send( evService, - RegisterForEvDataMessage(evcs2UUID) + RegisterForEvDataMessage(evcs2UUID, scheduleFunc(evcs2.ref)) ) evcs2.expectMsg(RegistrationSuccessfulMessage(None)) // register first one again evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectNoMessage() evcs2.expectNoMessage() @@ -260,13 +261,13 @@ class ExtEvDataServiceSpec evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectMsgType[RegistrationSuccessfulMessage] evcs2.send( evService, - RegisterForEvDataMessage(evcs2UUID) + RegisterForEvDataMessage(evcs2UUID, scheduleFunc(evcs2.ref)) ) evcs2.expectMsgType[RegistrationSuccessfulMessage] @@ -370,13 +371,13 @@ class ExtEvDataServiceSpec evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectMsgType[RegistrationSuccessfulMessage] evcs2.send( evService, - RegisterForEvDataMessage(evcs2UUID) + RegisterForEvDataMessage(evcs2UUID, scheduleFunc(evcs2.ref)) ) evcs2.expectMsgType[RegistrationSuccessfulMessage] @@ -570,7 +571,7 @@ class ExtEvDataServiceSpec evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectMsgType[RegistrationSuccessfulMessage] @@ -661,13 +662,13 @@ class ExtEvDataServiceSpec evcs1.send( evService, - RegisterForEvDataMessage(evcs1UUID) + RegisterForEvDataMessage(evcs1UUID, scheduleFunc(evcs1.ref)) ) evcs1.expectMsgType[RegistrationSuccessfulMessage] evcs2.send( evService, - RegisterForEvDataMessage(evcs2UUID) + RegisterForEvDataMessage(evcs2UUID, scheduleFunc(evcs2.ref)) ) evcs2.expectMsgType[RegistrationSuccessfulMessage] @@ -809,3 +810,8 @@ class ExtEvDataServiceSpec } } } + +object ExtEvDataServiceSpec { + private def scheduleFunc(actor: ActorRef): Long => ScheduleTriggerMessage = + (tick: Long) => ScheduleTriggerMessage(ActivityStartTrigger(tick), actor) +} diff --git a/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala index a726e074b9..7fce8930c5 100644 --- a/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala +++ b/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala @@ -36,7 +36,8 @@ trait EvcsInputTestData extends DefaultTestData with NodeInputTestData { ChargingPointTypeUtils.ChargingStationType2, 2, 0.95, - EvcsLocationType.HOME + EvcsLocationType.HOME, + true ) protected val simonaConfig: SimonaConfig = diff --git a/src/test/scala/edu/ie3/simona/test/common/model/participant/EvcsTestData.scala b/src/test/scala/edu/ie3/simona/test/common/model/participant/EvcsTestData.scala index 2f6763399b..b444350e84 100644 --- a/src/test/scala/edu/ie3/simona/test/common/model/participant/EvcsTestData.scala +++ b/src/test/scala/edu/ie3/simona/test/common/model/participant/EvcsTestData.scala @@ -31,6 +31,7 @@ trait EvcsTestData { 0.95d, 4, EvcsLocationType.HOME, + vehicle2grid = true, ChargingStrategy.MAX_POWER ) }