Skip to content

Commit fd93b32

Browse files
committed
Add a system bounds tracker for EV charger pools
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent 851f4b8 commit fd93b32

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# License: MIT
2+
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
3+
4+
"""System bounds tracker for the EV chargers."""
5+
6+
7+
import asyncio
8+
from collections import abc
9+
10+
from frequenz.channels import Receiver, Sender
11+
from frequenz.channels.util import Merge, select, selected_from
12+
13+
from ... import microgrid
14+
from ...actor import BackgroundService
15+
from ...actor.power_distributing._component_status import ComponentPoolStatus
16+
from .. import Power
17+
from .._base_types import Bounds, SystemBounds
18+
19+
20+
class EVCSystemBoundsTracker(BackgroundService):
21+
"""Track the system bounds for the EV chargers."""
22+
23+
def __init__(
24+
self,
25+
component_ids: abc.Set[int],
26+
status_receiver: Receiver[ComponentPoolStatus],
27+
bounds_sender: Sender[SystemBounds],
28+
):
29+
"""Initialize the system bounds tracker.
30+
31+
Args:
32+
component_ids: The ids of the components to track.
33+
status_receiver: A receiver that streams the status of the EV Chargers in
34+
the pool.
35+
bounds_sender: A sender to send the system bounds to.
36+
"""
37+
super().__init__()
38+
39+
self._component_ids = component_ids
40+
self._status_receiver = status_receiver
41+
self._bounds_sender = bounds_sender
42+
self._latest_component_data: dict[int, microgrid.component.EVChargerData] = {}
43+
self._last_sent_bounds: SystemBounds | None = None
44+
45+
def start(self) -> None:
46+
"""Start the EV charger system bounds tracker."""
47+
self._tasks.add(asyncio.create_task(self._run()))
48+
49+
async def _send_bounds(self) -> None:
50+
inclusion_bounds = Bounds(
51+
lower=Power.from_watts(
52+
sum(
53+
data.active_power_inclusion_lower_bound
54+
for data in self._latest_component_data.values()
55+
)
56+
),
57+
upper=Power.from_watts(
58+
sum(
59+
data.active_power_inclusion_upper_bound
60+
for data in self._latest_component_data.values()
61+
)
62+
),
63+
)
64+
exclusion_bounds = Bounds(
65+
lower=Power.from_watts(
66+
sum(
67+
data.active_power_exclusion_lower_bound
68+
for data in self._latest_component_data.values()
69+
)
70+
),
71+
upper=Power.from_watts(
72+
sum(
73+
data.active_power_exclusion_upper_bound
74+
for data in self._latest_component_data.values()
75+
)
76+
),
77+
)
78+
79+
if (
80+
self._last_sent_bounds is None
81+
or inclusion_bounds != self._last_sent_bounds.inclusion_bounds
82+
or exclusion_bounds != self._last_sent_bounds.exclusion_bounds
83+
):
84+
self._last_sent_bounds = SystemBounds(
85+
timestamp=max(
86+
msg.timestamp for msg in self._latest_component_data.values()
87+
),
88+
inclusion_bounds=inclusion_bounds,
89+
exclusion_bounds=exclusion_bounds,
90+
)
91+
await self._bounds_sender.send(self._last_sent_bounds)
92+
93+
async def _run(self) -> None:
94+
"""Run the system bounds tracker."""
95+
api_client = microgrid.connection_manager.get().api_client
96+
status_rx = self._status_receiver
97+
ev_data_rx = Merge(
98+
*(
99+
await asyncio.gather(
100+
*[api_client.ev_charger_data(cid) for cid in self._component_ids]
101+
)
102+
)
103+
)
104+
105+
component_pool_status = ComponentPoolStatus(set(), set())
106+
107+
async for selected in select(status_rx, ev_data_rx):
108+
if selected_from(selected, status_rx):
109+
status = selected.value
110+
component_pool_status = status
111+
for comp_id in self._latest_component_data:
112+
if (
113+
comp_id not in component_pool_status.working
114+
and comp_id not in component_pool_status.uncertain
115+
):
116+
self._latest_component_data.pop(comp_id, None)
117+
elif selected_from(selected, ev_data_rx):
118+
if (
119+
comp_id not in component_pool_status.working
120+
and comp_id not in component_pool_status.uncertain
121+
):
122+
continue
123+
data = selected.value
124+
self._latest_component_data[data.component_id] = data
125+
126+
await self._send_bounds()

0 commit comments

Comments
 (0)