2626import grpc
2727from frequenz .channels import Peekable , Receiver , Sender
2828
29+ from frequenz .sdk .timeseries ._quantities import Power
30+
2931from ..._internal ._math import is_close_to_zero
3032from ...actor import ChannelRegistry
3133from ...actor ._actor import Actor
@@ -96,7 +98,7 @@ class PowerDistributingActor(Actor):
9698
9799 It is recommended to wait for PowerDistributingActor output with timeout. Otherwise if
98100 the processing function fails then the response will never come.
99- The timeout should be Result:request_timeout_sec + time for processing the request.
101+ The timeout should be Result:request_timeout + time for processing the request.
100102
101103 Edge cases:
102104 * If there are 2 requests to be processed for the same subset of batteries, then
@@ -271,7 +273,9 @@ async def _run(self) -> None:
271273 )
272274 continue
273275
274- distributed_power_value = request .power - distribution .remaining_power
276+ distributed_power_value = (
277+ request .power .as_watts () - distribution .remaining_power
278+ )
275279 battery_distribution = {
276280 self ._inv_bat_map [bat_id ]: dist
277281 for bat_id , dist in distribution .distribution .items ()
@@ -283,27 +287,27 @@ async def _run(self) -> None:
283287 )
284288
285289 failed_power , failed_batteries = await self ._set_distributed_power (
286- api , distribution , request .request_timeout_sec
290+ api , distribution , request .request_timeout
287291 )
288292
289293 response : Success | PartialFailure
290294 if len (failed_batteries ) > 0 :
291295 succeed_batteries = set (battery_distribution .keys ()) - failed_batteries
292296 response = PartialFailure (
293297 request = request ,
294- succeeded_power = distributed_power_value ,
298+ succeeded_power = Power . from_watts ( distributed_power_value ) ,
295299 succeeded_batteries = succeed_batteries ,
296- failed_power = failed_power ,
300+ failed_power = Power . from_watts ( failed_power ) ,
297301 failed_batteries = failed_batteries ,
298- excess_power = distribution .remaining_power ,
302+ excess_power = Power . from_watts ( distribution .remaining_power ) ,
299303 )
300304 else :
301305 succeed_batteries = set (battery_distribution .keys ())
302306 response = Success (
303307 request = request ,
304- succeeded_power = distributed_power_value ,
308+ succeeded_power = Power . from_watts ( distributed_power_value ) ,
305309 succeeded_batteries = succeed_batteries ,
306- excess_power = distribution .remaining_power ,
310+ excess_power = Power . from_watts ( distribution .remaining_power ) ,
307311 )
308312
309313 asyncio .gather (
@@ -319,14 +323,14 @@ async def _set_distributed_power(
319323 self ,
320324 api : MicrogridApiClient ,
321325 distribution : DistributionResult ,
322- timeout_sec : float ,
326+ timeout : timedelta ,
323327 ) -> Tuple [float , Set [int ]]:
324328 """Send distributed power to the inverters.
325329
326330 Args:
327331 api: Microgrid api client
328332 distribution: Distribution result
329- timeout_sec : How long wait for the response
333+ timeout : How long wait for the response
330334
331335 Returns:
332336 Tuple where first element is total failed power, and the second element
@@ -339,13 +343,13 @@ async def _set_distributed_power(
339343
340344 _ , pending = await asyncio .wait (
341345 tasks .values (),
342- timeout = timeout_sec ,
346+ timeout = timeout . total_seconds () ,
343347 return_when = ALL_COMPLETED ,
344348 )
345349
346350 await self ._cancel_tasks (pending )
347351
348- return self ._parse_result (tasks , distribution .distribution , timeout_sec )
352+ return self ._parse_result (tasks , distribution .distribution , timeout )
349353
350354 def _get_power_distribution (
351355 self , request : Request , inv_bat_pairs : List [InvBatPair ]
@@ -367,11 +371,11 @@ def _get_power_distribution(
367371
368372 if request .include_broken_batteries and not available_bat_ids :
369373 return self .distribution_algorithm .distribute_power_equally (
370- request .power , unavailable_inv_ids
374+ request .power . as_watts () , unavailable_inv_ids
371375 )
372376
373377 result = self .distribution_algorithm .distribute_power (
374- request .power , inv_bat_pairs
378+ request .power . as_watts () , inv_bat_pairs
375379 )
376380
377381 if request .include_broken_batteries and unavailable_inv_ids :
@@ -412,9 +416,11 @@ def _check_request(
412416
413417 bounds = self ._get_bounds (pairs_data )
414418
419+ power = request .power .as_watts ()
420+
415421 # Zero power requests are always forwarded to the microgrid API, even if they
416422 # are outside the exclusion bounds.
417- if is_close_to_zero (request . power ):
423+ if is_close_to_zero (power ):
418424 return None
419425
420426 if request .adjust_power :
@@ -423,15 +429,11 @@ def _check_request(
423429 #
424430 # If the requested power is in the exclusion bounds, it is NOT possible to
425431 # increase it so that it is outside the exclusion bounds.
426- if bounds .exclusion_lower < request . power < bounds .exclusion_upper :
432+ if bounds .exclusion_lower < power < bounds .exclusion_upper :
427433 return OutOfBounds (request = request , bounds = bounds )
428434 else :
429- in_lower_range = (
430- bounds .inclusion_lower <= request .power <= bounds .exclusion_lower
431- )
432- in_upper_range = (
433- bounds .exclusion_upper <= request .power <= bounds .inclusion_upper
434- )
435+ in_lower_range = bounds .inclusion_lower <= power <= bounds .exclusion_lower
436+ in_upper_range = bounds .exclusion_upper <= power <= bounds .inclusion_upper
435437 if not (in_lower_range or in_upper_range ):
436438 return OutOfBounds (request = request , bounds = bounds )
437439
@@ -630,7 +632,7 @@ def _parse_result(
630632 self ,
631633 tasks : Dict [int , asyncio .Task [None ]],
632634 distribution : Dict [int , float ],
633- request_timeout_sec : float ,
635+ request_timeout : timedelta ,
634636 ) -> Tuple [float , Set [int ]]:
635637 """Parse the results of `set_power` requests.
636638
@@ -642,7 +644,7 @@ def _parse_result(
642644 set the power for this inverter. Each task should be finished or cancelled.
643645 distribution: A dictionary where the key is the inverter ID and the value is how much
644646 power was set to the corresponding inverter.
645- request_timeout_sec : The timeout that was used for the request.
647+ request_timeout : The timeout that was used for the request.
646648
647649 Returns:
648650 A tuple where the first element is the total failed power, and the second element is
@@ -676,7 +678,7 @@ def _parse_result(
676678 _logger .warning (
677679 "Battery %d didn't respond in %f sec. Mark it as broken." ,
678680 battery_id ,
679- request_timeout_sec ,
681+ request_timeout . total_seconds () ,
680682 )
681683
682684 return failed_power , failed_batteries
0 commit comments