Skip to content

Commit 5ddf1f2

Browse files
Update power distributor to use frequenz.sdk.microgrid (#156)
PowerDistributingActor should not take microgrid_api and component_graph as constructor argument. Instead it should get them from frequenz.sdk.microgrid.
2 parents 0c61eb9 + 77c4db8 commit 5ddf1f2

File tree

7 files changed

+139
-134
lines changed

7 files changed

+139
-134
lines changed

RELEASE_NOTES.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
- Add a formula generator for SoC in the LogicalMeter
2222
https://github.com/frequenz-floss/frequenz-sdk-python/pull/137
2323

24-
2524
## Bug Fixes
2625

2726
- Formulas with repeated operators like `#1 - #2 - #3` were getting
2827
calculated incorrectly as `#1 - (#2 - #3)`. This has been fixed in
2928
https://github.com/frequenz-floss/frequenz-sdk-python/pull/141
29+
30+
- Remove `microgrid_api` and `component_graph` arguments from PowerDistributingActor constructor
31+
https://github.com/frequenz-floss/frequenz-sdk-python/pull/156

benchmarks/power_distribution/power_distributor.py

+9-18
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
from dataclasses import dataclass
1111
from typing import Any, Coroutine, Dict, List, Set # pylint: disable=unused-import
1212

13-
import grpc.aio as grpcaio
1413
from frequenz.channels import Bidirectional
1514

15+
from frequenz.sdk import microgrid
1616
from frequenz.sdk.actor.power_distributing import (
1717
Error,
1818
Ignored,
@@ -23,12 +23,9 @@
2323
Result,
2424
Success,
2525
)
26-
from frequenz.sdk.microgrid import ComponentGraph
27-
from frequenz.sdk.microgrid._graph import _MicrogridComponentGraph
28-
from frequenz.sdk.microgrid.client import MicrogridApiClient, MicrogridGrpcClient
2926
from frequenz.sdk.microgrid.component import Component, ComponentCategory
3027

31-
HOST = "157.90.243.180"
28+
HOST = "microgrid.sandbox.api.frequenz.io"
3229
PORT = 61060
3330

3431

@@ -104,16 +101,13 @@ async def run_test( # pylint: disable=too-many-locals
104101
users_num: int,
105102
requests_per_user: int,
106103
batteries: Set[int],
107-
api: MicrogridApiClient,
108-
graph: ComponentGraph,
109104
) -> Dict[str, Any]:
110105
"""Run test.
111106
112107
Args:
113108
users_num: Number of users to register
114109
requests_per_user: How many request user should send.
115110
batteries: Set of batteries for each request.
116-
distributor: PowerDistributingActor instance.
117111
118112
Returns:
119113
Dictionary with statistics.
@@ -129,7 +123,7 @@ async def run_test( # pylint: disable=too-many-locals
129123
user_id: channel.service_handle for user_id, channel in channels.items()
130124
}
131125

132-
distributor = PowerDistributingActor(api, graph, service_channels)
126+
distributor = PowerDistributingActor(service_channels)
133127

134128
tasks: List[Coroutine[Any, Any, List[Result]]] = []
135129
for user_id, channel in channels.items():
@@ -152,25 +146,22 @@ async def run_test( # pylint: disable=too-many-locals
152146
async def run() -> None:
153147
"""Create microgrid api and run tests."""
154148
# pylint: disable=protected-access
155-
grpc_channel = grpcaio.insecure_channel(f"{HOST}:{PORT}")
156-
api = MicrogridGrpcClient(grpc_channel, target=f"{HOST}:{PORT}")
157149

158-
graph = _MicrogridComponentGraph()
159-
await graph.refresh_from_api(api)
150+
await microgrid.initialize(HOST, PORT)
160151

161-
all_batteries: Set[Component] = graph.components(
152+
all_batteries: Set[Component] = microgrid.get().component_graph.components(
162153
component_category={ComponentCategory.BATTERY}
163154
)
164155
batteries_ids = {c.component_id for c in all_batteries}
165156
# Take some time to get data from components
166157
await asyncio.sleep(4)
167158
with open("/dev/stdout", "w", encoding="utf-8") as csvfile:
168-
fields = await run_test(0, 0, batteries_ids, api, graph)
159+
fields = await run_test(0, 0, batteries_ids)
169160
out = csv.DictWriter(csvfile, fields.keys())
170161
out.writeheader()
171-
out.writerow(await run_test(1, 1, batteries_ids, api, graph))
172-
out.writerow(await run_test(1, 10, batteries_ids, api, graph))
173-
out.writerow(await run_test(10, 10, batteries_ids, api, graph))
162+
out.writerow(await run_test(1, 1, batteries_ids))
163+
out.writerow(await run_test(1, 10, batteries_ids))
164+
out.writerow(await run_test(10, 10, batteries_ids))
174165

175166

176167
async def main() -> None:

examples/power_distribution.py

-3
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,6 @@ async def run() -> None:
184184
}
185185

186186
power_distributor = PowerDistributingActor(
187-
microgrid_api=microgrid.get().api_client,
188-
# microgrid_api=microgrid_api.microgrid_api, in v0.8.0
189-
component_graph=microgrid.get().component_graph,
190187
users_channels={
191188
key: channel.service_handle
192189
for key, channel in power_distributor_channels.items()

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

+41-28
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from frequenz.channels import Bidirectional, Peekable, Receiver
3333
from google.protobuf.empty_pb2 import Empty # pylint: disable=no-name-in-module
3434

35+
from ... import microgrid
3536
from ...actor._decorator import actor
3637
from ...microgrid import ComponentGraph
3738
from ...microgrid.client import MicrogridApiClient
@@ -41,7 +42,7 @@
4142
ComponentCategory,
4243
InverterData,
4344
)
44-
from ...power import DistributionAlgorithm, InvBatPair
45+
from ...power import DistributionAlgorithm, DistributionResult, InvBatPair
4546
from ._battery_pool_status import BatteryPoolStatus
4647
from .request import Request
4748
from .result import Error, Ignored, OutOfBound, PartialFailure, Result, Success
@@ -140,22 +141,17 @@ class PowerDistributingActor:
140141

141142
def __init__(
142143
self,
143-
microgrid_api: MicrogridApiClient,
144-
component_graph: ComponentGraph,
145144
users_channels: Dict[str, Bidirectional.Handle[Result, Request]],
146145
wait_for_data_sec: float = 2,
147146
) -> None:
148147
"""Create class instance.
149148
150149
Args:
151-
microgrid_api: api for sending the requests.
152-
component_graph: component graph of the given microgrid api.
153150
users_channels: BidirectionalHandle for each user. Key should be
154151
user id and value should be BidirectionalHandle.
155152
wait_for_data_sec: How long actor should wait before processing first
156153
request. It is a time needed to collect first components data.
157154
"""
158-
self._api = microgrid_api
159155
self._wait_for_data_sec = wait_for_data_sec
160156

161157
# NOTE: power_distributor_exponent should be received from ConfigManager
@@ -164,19 +160,16 @@ def __init__(
164160
self.power_distributor_exponent
165161
)
166162

167-
batteries = component_graph.components(
168-
component_category={ComponentCategory.BATTERY}
169-
)
163+
graph = microgrid.get().component_graph
164+
batteries = graph.components(component_category={ComponentCategory.BATTERY})
170165

171166
self._battery_pool = BatteryPoolStatus(
172167
battery_ids={battery.component_id for battery in batteries},
173168
max_blocking_duration_sec=30.0,
174169
max_data_age_sec=10.0,
175170
)
176171

177-
self._bat_inv_map, self._inv_bat_map = self._get_components_pairs(
178-
component_graph
179-
)
172+
self._bat_inv_map, self._inv_bat_map = self._get_components_pairs(graph)
180173
self._battery_receivers: Dict[int, Peekable[BatteryData]] = {}
181174
self._inverter_receivers: Dict[int, Peekable[InverterData]] = {}
182175

@@ -250,6 +243,7 @@ async def run(self) -> None:
250243
"""
251244
await self._create_channels()
252245
await self._battery_pool.async_init()
246+
api = microgrid.get().api_client
253247

254248
# Wait few seconds to get data from the channels created above.
255249
await asyncio.sleep(self._wait_for_data_sec)
@@ -291,20 +285,10 @@ async def run(self) -> None:
291285
str(battery_distribution),
292286
)
293287

294-
tasks = {
295-
inverter_id: asyncio.create_task(
296-
self._api.set_power(inverter_id, power)
297-
)
298-
for inverter_id, power in distribution.distribution.items()
299-
}
300-
301-
_, pending = await asyncio.wait(
302-
tasks.values(),
303-
timeout=request.request_timeout_sec,
304-
return_when=ALL_COMPLETED,
288+
tasks = await self._set_distributed_power(
289+
api, distribution, request.request_timeout_sec
305290
)
306291

307-
await self._cancel_tasks(pending)
308292
failed_power, failed_batteries = self._parse_result(
309293
tasks, distribution.distribution, request.request_timeout_sec
310294
)
@@ -330,6 +314,36 @@ async def run(self) -> None:
330314
self._battery_pool.update_last_request_status(response)
331315
await user.channel.send(response)
332316

317+
async def _set_distributed_power(
318+
self,
319+
api: MicrogridApiClient,
320+
distribution: DistributionResult,
321+
timeout_sec: float,
322+
) -> Dict[int, asyncio.Task[Empty]]:
323+
"""Send distributed power to the inverters.
324+
325+
Args:
326+
api: Microgrid api client
327+
distribution: Distribution result
328+
timeout_sec: How long wait for the response
329+
330+
Returns:
331+
Dict with finished or cancelled task for each inverter.
332+
"""
333+
tasks = {
334+
inverter_id: asyncio.create_task(api.set_power(inverter_id, power))
335+
for inverter_id, power in distribution.distribution.items()
336+
}
337+
338+
_, pending = await asyncio.wait(
339+
tasks.values(),
340+
timeout=timeout_sec,
341+
return_when=ALL_COMPLETED,
342+
)
343+
344+
await self._cancel_tasks(pending)
345+
return tasks
346+
333347
def _check_request(self, request: Request) -> Optional[Result]:
334348
"""Check whether the given request if correct.
335349
@@ -546,13 +560,12 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
546560

547561
async def _create_channels(self) -> None:
548562
"""Create channels to get data of components in microgrid."""
563+
api = microgrid.get().api_client
549564
for battery_id, inverter_id in self._bat_inv_map.items():
550-
bat_recv: Receiver[BatteryData] = await self._api.battery_data(battery_id)
565+
bat_recv: Receiver[BatteryData] = await api.battery_data(battery_id)
551566
self._battery_receivers[battery_id] = bat_recv.into_peekable()
552567

553-
inv_recv: Receiver[InverterData] = await self._api.inverter_data(
554-
inverter_id
555-
)
568+
inv_recv: Receiver[InverterData] = await api.inverter_data(inverter_id)
556569
self._inverter_receivers[inverter_id] = inv_recv.into_peekable()
557570

558571
def _parse_result(

src/frequenz/sdk/timeseries/_resampling.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ async def resample(self, *, one_shot: bool = False) -> None:
284284
period.
285285
286286
Raises:
287-
ResamplingError: If some timseries source or sink encounters any
287+
ResamplingError: If some timeseries source or sink encounters any
288288
errors while receiving or sending samples. In this case the
289289
timer still runs and the timeseries will keep receiving data.
290290
The user should remove (and re-add if desired) the faulty

0 commit comments

Comments
 (0)