66from __future__ import annotations
77
88import asyncio
9+ import uuid
910from collections .abc import Set
1011from datetime import timedelta
1112from typing import Any
1213
13- from frequenz .channels import Receiver
14+ from frequenz .channels import Receiver , Sender
1415
1516from ..._internal ._constants import RECEIVER_MAX_SIZE
1617from ..._internal .asyncio import cancel_and_await
18+ from ...actor import ChannelRegistry , ComponentMetricRequest
1719from ...actor .power_distributing ._battery_pool_status import BatteryStatus
1820from ...microgrid import connection_manager
1921from ...microgrid .component import ComponentCategory
22+ from .._formula_engine import FormulaEngine , FormulaEnginePool
23+ from .._formula_engine ._formula_generators import (
24+ BatteryPowerFormula ,
25+ FormulaGeneratorConfig ,
26+ )
2027from ._methods import AggregateMethod , SendOnUpdate
2128from ._metric_calculator import CapacityCalculator , PowerBoundsCalculator , SoCCalculator
2229from ._result_types import CapacityMetrics , PowerMetrics , SoCMetrics
@@ -29,15 +36,21 @@ class BatteryPool:
2936 fetching high level metrics for this subset.
3037 """
3138
32- def __init__ (
39+ def __init__ ( # pylint: disable=too-many-arguments
3340 self ,
41+ channel_registry : ChannelRegistry ,
42+ resampler_subscription_sender : Sender [ComponentMetricRequest ],
3443 batteries_status_receiver : Receiver [BatteryStatus ],
3544 min_update_interval : timedelta ,
3645 batteries_id : Set [int ] | None = None ,
3746 ) -> None :
3847 """Create the class instance.
3948
4049 Args:
50+ channel_registry: A channel registry instance shared with the resampling
51+ actor.
52+ resampler_subscription_sender: A sender for sending metric requests to the
53+ resampling actor.
4154 batteries_status_receiver: Receiver to receive status of the batteries.
4255 Receivers should has maxsize = 1 to fetch only the latest status.
4356 Battery status channel should has resend_latest = True.
@@ -71,6 +84,13 @@ def __init__(
7184 self ._min_update_interval = min_update_interval
7285 self ._active_methods : dict [str , AggregateMethod [Any ]] = {}
7386
87+ self ._namespace : str = f"battery-pool-{ self ._batteries } -{ uuid .uuid4 ()} "
88+ self ._formula_pool : FormulaEnginePool = FormulaEnginePool (
89+ self ._namespace ,
90+ channel_registry ,
91+ resampler_subscription_sender ,
92+ )
93+
7494 @property
7595 def battery_ids (self ) -> Set [int ]:
7696 """Return ids of the batteries in the pool.
@@ -80,6 +100,26 @@ def battery_ids(self) -> Set[int]:
80100 """
81101 return self ._batteries
82102
103+ @property
104+ def power (self ) -> FormulaEngine :
105+ """Fetch the total power of the batteries in the pool.
106+
107+ If a formula engine to calculate this metric is not already running, it will be
108+ started.
109+
110+ A receiver from the formula engine can be obtained by calling the `new_receiver`
111+ method.
112+
113+ Returns:
114+ A FormulaEngine that will calculate and stream the total power of all
115+ batteries in the pool.
116+ """
117+ return self ._formula_pool .from_generator (
118+ "battery_pool_power" ,
119+ BatteryPowerFormula ,
120+ FormulaGeneratorConfig (component_ids = self ._batteries ),
121+ ) # type: ignore[return-value]
122+
83123 async def soc (
84124 self , maxsize : int | None = RECEIVER_MAX_SIZE
85125 ) -> Receiver [SoCMetrics | None ]:
0 commit comments