1515from frequenz .channels import Broadcast , ChannelClosedError , Receiver , Sender
1616
1717from ..._internal ._asyncio import cancel_and_await
18+ from ..._internal ._channels import ReceiverFetcher
1819from ...actor import ChannelRegistry , ComponentMetricRequest
20+ from ...actor .power_distributing import ComponentPoolStatus
1921from ...microgrid import connection_manager
2022from ...microgrid .component import ComponentCategory , ComponentMetricId
2123from .. import Sample , Sample3Phase
24+ from .._base_types import SystemBounds
2225from .._quantities import Current , Power , Quantity
2326from ..formula_engine import FormulaEngine , FormulaEngine3Phase
2427from ..formula_engine ._formula_engine_pool import FormulaEnginePool
2932)
3033from ._set_current_bounds import BoundsSetter , ComponentCurrentLimit
3134from ._state_tracker import EVChargerState , StateTracker
35+ from ._system_bounds_tracker import EVCSystemBoundsTracker
3236
3337_logger = logging .getLogger (__name__ )
3438
@@ -73,10 +77,11 @@ class EVChargerPool:
7377 method for limiting the max current of individual EV Chargers in the pool.
7478 """
7579
76- def __init__ (
80+ def __init__ ( # pylint: disable=too-many-arguments
7781 self ,
7882 channel_registry : ChannelRegistry ,
7983 resampler_subscription_sender : Sender [ComponentMetricRequest ],
84+ status_receiver : Receiver [ComponentPoolStatus ],
8085 component_ids : abc .Set [int ] | None = None ,
8186 repeat_interval : timedelta = timedelta (seconds = 3.0 ),
8287 ) -> None :
@@ -92,6 +97,8 @@ def __init__(
9297 actor.
9398 resampler_subscription_sender: A sender for sending metric requests to the
9499 resampling actor.
100+ status_receiver: A receiver that streams the status of the EV Chargers in
101+ the pool.
95102 component_ids: An optional list of component_ids belonging to this pool. If
96103 not specified, IDs of all EV Chargers in the microgrid will be fetched
97104 from the component graph.
@@ -103,6 +110,7 @@ def __init__(
103110 self ._resampler_subscription_sender : Sender [ComponentMetricRequest ] = (
104111 resampler_subscription_sender
105112 )
113+ self ._status_receiver : Receiver [ComponentPoolStatus ] = status_receiver
106114 self ._component_ids : abc .Set [int ] = set ()
107115 if component_ids is not None :
108116 self ._component_ids = component_ids
@@ -126,6 +134,14 @@ def __init__(
126134 )
127135 self ._bounds_setter : BoundsSetter | None = None
128136
137+ self ._bounds_channel : Broadcast [SystemBounds ] = Broadcast (
138+ name = f"System Bounds for EV Chargers: { component_ids } "
139+ )
140+ self ._bounds_tracker : EVCSystemBoundsTracker = EVCSystemBoundsTracker (
141+ self .component_ids , self ._status_receiver , self ._bounds_channel .new_sender ()
142+ )
143+ self ._bounds_tracker .start ()
144+
129145 @property
130146 def component_ids (self ) -> abc .Set [int ]:
131147 """Return component IDs of all EV Chargers managed by this EVChargerPool.
@@ -353,3 +369,8 @@ async def _stream_component_data(
353369 state = state ,
354370 )
355371 )
372+
373+ @property
374+ def _system_power_bounds (self ) -> ReceiverFetcher [SystemBounds ]:
375+ """Return a receiver for the system power bounds."""
376+ return self ._bounds_channel
0 commit comments