Skip to content

Commit 364d594

Browse files
authored
Add a ComponentCategory parameter to the PowerDistributingActor (#896)
Currently only `ComponentCategory.BATTERY` is accepted, but making it a parameter this paves the way for adding support for more component categories. Also rearranges some component_status tests, to make it easier to add tests for status trackers for more component types.
2 parents ab10cc3 + b22f471 commit 364d594

File tree

8 files changed

+61
-13
lines changed

8 files changed

+61
-13
lines changed

benchmarks/power_distribution/power_distributor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ async def run_test( # pylint: disable=too-many-locals
115115
battery_status_channel = Broadcast[ComponentPoolStatus](name="battery-status")
116116
power_result_channel = Broadcast[Result](name="power-result")
117117
async with PowerDistributingActor(
118+
component_category=ComponentCategory.BATTERY,
118119
requests_receiver=power_request_channel.new_receiver(),
119120
results_sender=power_result_channel.new_sender(),
120121
component_pool_status_sender=battery_status_channel.new_sender(),

src/frequenz/sdk/actor/power_distributing/power_distributing.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import asyncio
1616

1717
from frequenz.channels import Receiver, Sender
18+
from frequenz.client.microgrid import ComponentCategory
1819

1920
from ...actor._actor import Actor
2021
from ._component_managers import BatteryManager, ComponentManager
@@ -51,6 +52,7 @@ class PowerDistributingActor(Actor):
5152

5253
def __init__( # pylint: disable=too-many-arguments
5354
self,
55+
component_category: ComponentCategory,
5456
requests_receiver: Receiver[Request],
5557
results_sender: Sender[Result],
5658
component_pool_status_sender: Sender[ComponentPoolStatus],
@@ -61,6 +63,8 @@ def __init__( # pylint: disable=too-many-arguments
6163
"""Create class instance.
6264
6365
Args:
66+
component_category: The category of the components that this actor is
67+
responsible for.
6468
requests_receiver: Receiver for receiving power requests from the power
6569
manager.
6670
results_sender: Sender for sending results to the power manager.
@@ -70,15 +74,23 @@ def __init__( # pylint: disable=too-many-arguments
7074
request. It is a time needed to collect first components data.
7175
name: The name of the actor. If `None`, `str(id(self))` will be used. This
7276
is used mostly for debugging purposes.
77+
78+
Raises:
79+
ValueError: If the given component category is not supported.
7380
"""
7481
super().__init__(name=name)
82+
self._component_category = component_category
7583
self._requests_receiver = requests_receiver
7684
self._result_sender = results_sender
7785
self._wait_for_data_sec = wait_for_data_sec
7886

79-
self._component_manager: ComponentManager = BatteryManager(
80-
component_pool_status_sender
81-
)
87+
self._component_manager: ComponentManager
88+
if component_category == ComponentCategory.BATTERY:
89+
self._component_manager = BatteryManager(component_pool_status_sender)
90+
else:
91+
raise ValueError(
92+
f"PowerDistributor doesn't support controlling: {component_category}"
93+
)
8294

8395
async def _run(self) -> None: # pylint: disable=too-many-locals
8496
"""Run actor main function.

src/frequenz/sdk/microgrid/_data_pipeline.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from dataclasses import dataclass
1717

1818
from frequenz.channels import Broadcast, Sender
19+
from frequenz.client.microgrid import ComponentCategory
1920

2021
from ..actor._actor import Actor
2122
from ..timeseries._grid_frequency import GridFrequency
@@ -88,7 +89,9 @@ def __init__(
8889
self._data_sourcing_actor: _ActorInfo | None = None
8990
self._resampling_actor: _ActorInfo | None = None
9091

91-
self._battery_power_wrapper = PowerWrapper(self._channel_registry)
92+
self._battery_power_wrapper = PowerWrapper(
93+
ComponentCategory.BATTERY, self._channel_registry
94+
)
9295

9396
self._logical_meter: LogicalMeter | None = None
9497
self._consumer: Consumer | None = None

src/frequenz/sdk/microgrid/_power_wrapper.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@
3434
class PowerWrapper:
3535
"""Wrapper around the power managing and power distributing actors."""
3636

37-
def __init__(self, channel_registry: ChannelRegistry):
37+
def __init__(
38+
self, component_category: ComponentCategory, channel_registry: ChannelRegistry
39+
):
3840
"""Initialize the power control.
3941
4042
Args:
43+
component_category: The category of the components that actors started by
44+
this instance of the PowerWrapper will be responsible for.
4145
channel_registry: A channel registry for use in the actors.
4246
"""
47+
self._component_category = component_category
4348
self._channel_registry = channel_registry
4449

4550
self.status_channel: Broadcast[ComponentPoolStatus] = Broadcast(
@@ -74,18 +79,19 @@ def _start_power_managing_actor(self) -> None:
7479
# constraint needs to be relaxed if the actor is extended to support other
7580
# components.
7681
if not component_graph.components(
77-
component_categories={ComponentCategory.BATTERY}
82+
component_categories={self._component_category}
7883
):
7984
_logger.warning(
80-
"No batteries found in the component graph. "
81-
"The power managing actor will not be started."
85+
"No %s found in the component graph. "
86+
"The power managing actor will not be started.",
87+
self._component_category,
8288
)
8389
return
8490

8591
from ..actor._power_managing._power_managing_actor import PowerManagingActor
8692

8793
self._power_managing_actor = PowerManagingActor(
88-
component_category=ComponentCategory.BATTERY,
94+
component_category=self._component_category,
8995
proposals_receiver=self.proposal_channel.new_receiver(),
9096
bounds_subscription_receiver=(
9197
self.bounds_subscription_channel.new_receiver()
@@ -109,11 +115,12 @@ def _start_power_distributing_actor(self) -> None:
109115

110116
component_graph = microgrid.connection_manager.get().component_graph
111117
if not component_graph.components(
112-
component_categories={ComponentCategory.BATTERY}
118+
component_categories={self._component_category}
113119
):
114120
_logger.warning(
115-
"No batteries found in the component graph. "
116-
"The power distributing actor will not be started."
121+
"No %s found in the component graph. "
122+
"The power distributing actor will not be started.",
123+
self._component_category,
117124
)
118125
return
119126

@@ -123,6 +130,7 @@ def _start_power_distributing_actor(self) -> None:
123130
# Until the PowerManager is implemented, support for multiple use-case actors
124131
# will not be available in the high level interface.
125132
self._power_distributing_actor = PowerDistributingActor(
133+
component_category=self._component_category,
126134
requests_receiver=self._power_distribution_requests_channel.new_receiver(),
127135
results_sender=self._power_distribution_results_channel.new_sender(),
128136
component_pool_status_sender=self.status_channel.new_sender(),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# License: MIT
2+
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Tests for component status tracking for the power distributing actor."""

tests/actor/test_battery_status.py renamed to tests/actor/power_distributing/_component_status/test_battery_status.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
)
3838
from tests.timeseries.mock_microgrid import MockMicrogrid
3939

40-
from ..utils.component_data_wrapper import BatteryDataWrapper, InverterDataWrapper
40+
from ....utils.component_data_wrapper import BatteryDataWrapper, InverterDataWrapper
4141

4242

4343
def battery_data( # pylint: disable=too-many-arguments

tests/actor/power_distributing/test_power_distributing.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ async def test_constructor_with_grid_meter(self, mocker: MockerFixture) -> None:
107107
name="battery_status"
108108
)
109109
async with PowerDistributingActor(
110+
component_category=ComponentCategory.BATTERY,
110111
requests_receiver=requests_channel.new_receiver(),
111112
results_sender=results_channel.new_sender(),
112113
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -136,6 +137,7 @@ async def test_constructor_without_grid_meter(self, mocker: MockerFixture) -> No
136137
name="battery_status"
137138
)
138139
async with PowerDistributingActor(
140+
component_category=ComponentCategory.BATTERY,
139141
requests_receiver=requests_channel.new_receiver(),
140142
results_sender=results_channel.new_sender(),
141143
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -199,6 +201,7 @@ async def test_power_distributor_one_user(self, mocker: MockerFixture) -> None:
199201

200202
battery_status_channel = Broadcast[ComponentPoolStatus](name="battery_status")
201203
async with PowerDistributingActor(
204+
component_category=ComponentCategory.BATTERY,
202205
requests_receiver=requests_channel.new_receiver(),
203206
results_sender=results_channel.new_sender(),
204207
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -258,6 +261,7 @@ async def test_power_distributor_exclusion_bounds(
258261
name="battery_status"
259262
)
260263
async with PowerDistributingActor(
264+
component_category=ComponentCategory.BATTERY,
261265
requests_receiver=requests_channel.new_receiver(),
262266
results_sender=results_channel.new_sender(),
263267
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -359,6 +363,7 @@ async def test_two_batteries_one_inverters(self, mocker: MockerFixture) -> None:
359363
)
360364

361365
async with PowerDistributingActor(
366+
component_category=ComponentCategory.BATTERY,
362367
requests_receiver=requests_channel.new_receiver(),
363368
component_pool_status_sender=battery_status_channel.new_sender(),
364369
results_sender=results_channel.new_sender(),
@@ -437,6 +442,7 @@ async def test_two_batteries_one_broken_one_inverters(
437442
)
438443

439444
async with PowerDistributingActor(
445+
component_category=ComponentCategory.BATTERY,
440446
requests_receiver=requests_channel.new_receiver(),
441447
component_pool_status_sender=battery_status_channel.new_sender(),
442448
results_sender=results_channel.new_sender(),
@@ -491,6 +497,7 @@ async def test_battery_two_inverters(self, mocker: MockerFixture) -> None:
491497
)
492498

493499
async with PowerDistributingActor(
500+
component_category=ComponentCategory.BATTERY,
494501
requests_receiver=requests_channel.new_receiver(),
495502
component_pool_status_sender=battery_status_channel.new_sender(),
496503
results_sender=results_channel.new_sender(),
@@ -541,6 +548,7 @@ async def test_two_batteries_three_inverters(self, mocker: MockerFixture) -> Non
541548
)
542549

543550
async with PowerDistributingActor(
551+
component_category=ComponentCategory.BATTERY,
544552
requests_receiver=requests_channel.new_receiver(),
545553
component_pool_status_sender=battery_status_channel.new_sender(),
546554
results_sender=results_channel.new_sender(),
@@ -625,6 +633,7 @@ async def test_two_batteries_one_inverter_different_exclusion_bounds_2(
625633
)
626634

627635
async with PowerDistributingActor(
636+
component_category=ComponentCategory.BATTERY,
628637
requests_receiver=requests_channel.new_receiver(),
629638
component_pool_status_sender=battery_status_channel.new_sender(),
630639
results_sender=results_channel.new_sender(),
@@ -710,6 +719,7 @@ async def test_two_batteries_one_inverter_different_exclusion_bounds(
710719
)
711720

712721
async with PowerDistributingActor(
722+
component_category=ComponentCategory.BATTERY,
713723
requests_receiver=requests_channel.new_receiver(),
714724
component_pool_status_sender=battery_status_channel.new_sender(),
715725
results_sender=results_channel.new_sender(),
@@ -774,6 +784,7 @@ async def test_connected_but_not_requested_batteries(
774784
)
775785

776786
async with PowerDistributingActor(
787+
component_category=ComponentCategory.BATTERY,
777788
requests_receiver=requests_channel.new_receiver(),
778789
component_pool_status_sender=battery_status_channel.new_sender(),
779790
results_sender=results_channel.new_sender(),
@@ -830,6 +841,7 @@ async def test_battery_soc_nan(self, mocker: MockerFixture) -> None:
830841
name="battery_status"
831842
)
832843
async with PowerDistributingActor(
844+
component_category=ComponentCategory.BATTERY,
833845
requests_receiver=requests_channel.new_receiver(),
834846
results_sender=results_channel.new_sender(),
835847
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -883,6 +895,7 @@ async def test_battery_capacity_nan(self, mocker: MockerFixture) -> None:
883895
name="battery_status"
884896
)
885897
async with PowerDistributingActor(
898+
component_category=ComponentCategory.BATTERY,
886899
requests_receiver=requests_channel.new_receiver(),
887900
results_sender=results_channel.new_sender(),
888901
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -955,6 +968,7 @@ async def test_battery_power_bounds_nan(self, mocker: MockerFixture) -> None:
955968
name="battery_status"
956969
)
957970
async with PowerDistributingActor(
971+
component_category=ComponentCategory.BATTERY,
958972
requests_receiver=requests_channel.new_receiver(),
959973
results_sender=results_channel.new_sender(),
960974
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -999,6 +1013,7 @@ async def test_power_distributor_invalid_battery_id(
9991013
name="battery_status"
10001014
)
10011015
async with PowerDistributingActor(
1016+
component_category=ComponentCategory.BATTERY,
10021017
requests_receiver=requests_channel.new_receiver(),
10031018
results_sender=results_channel.new_sender(),
10041019
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -1042,6 +1057,7 @@ async def test_power_distributor_one_user_adjust_power_consume(
10421057
name="battery_status"
10431058
)
10441059
async with PowerDistributingActor(
1060+
component_category=ComponentCategory.BATTERY,
10451061
requests_receiver=requests_channel.new_receiver(),
10461062
results_sender=results_channel.new_sender(),
10471063
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -1087,6 +1103,7 @@ async def test_power_distributor_one_user_adjust_power_supply(
10871103
name="battery_status"
10881104
)
10891105
async with PowerDistributingActor(
1106+
component_category=ComponentCategory.BATTERY,
10901107
requests_receiver=requests_channel.new_receiver(),
10911108
results_sender=results_channel.new_sender(),
10921109
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -1132,6 +1149,7 @@ async def test_power_distributor_one_user_adjust_power_success(
11321149
name="battery_status"
11331150
)
11341151
async with PowerDistributingActor(
1152+
component_category=ComponentCategory.BATTERY,
11351153
requests_receiver=requests_channel.new_receiver(),
11361154
results_sender=results_channel.new_sender(),
11371155
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -1170,6 +1188,7 @@ async def test_not_all_batteries_are_working(self, mocker: MockerFixture) -> Non
11701188
name="battery_status"
11711189
)
11721190
async with PowerDistributingActor(
1191+
component_category=ComponentCategory.BATTERY,
11731192
requests_receiver=requests_channel.new_receiver(),
11741193
results_sender=results_channel.new_sender(),
11751194
component_pool_status_sender=battery_status_channel.new_sender(),
@@ -1222,6 +1241,7 @@ async def test_partial_failure_result(self, mocker: MockerFixture) -> None:
12221241
name="battery_status"
12231242
)
12241243
async with PowerDistributingActor(
1244+
component_category=ComponentCategory.BATTERY,
12251245
requests_receiver=requests_channel.new_receiver(),
12261246
results_sender=results_channel.new_sender(),
12271247
component_pool_status_sender=battery_status_channel.new_sender(),

0 commit comments

Comments
 (0)