@@ -195,6 +195,10 @@ def __init__(
195
195
max_data_age_sec = 10.0 ,
196
196
)
197
197
198
+ self ._cached_metrics : dict [int , InvBatPair | None ] = {
199
+ bat_id : None for bat_id , _ in self ._bat_inv_map .items ()
200
+ }
201
+
198
202
def _create_users_tasks (self ) -> List [asyncio .Task [None ]]:
199
203
"""For each user create a task to wait for request.
200
204
@@ -208,37 +212,39 @@ def _create_users_tasks(self) -> List[asyncio.Task[None]]:
208
212
)
209
213
return tasks
210
214
211
- def _get_upper_bound (self , batteries : Set [int ]) -> float :
215
+ def _get_upper_bound (self , batteries : Set [int ], use_all : bool ) -> float :
212
216
"""Get total upper bound of power to be set for given batteries.
213
217
214
218
Note, output of that function doesn't guarantee that this bound will be
215
219
the same when the request is processed.
216
220
217
221
Args:
218
222
batteries: List of batteries
223
+ use_all: flag whether all batteries must be used for the power request.
219
224
220
225
Returns:
221
226
Upper bound for `set_power` operation.
222
227
"""
223
- pairs_data : List [InvBatPair ] = self ._get_components_data (batteries )
228
+ pairs_data : List [InvBatPair ] = self ._get_components_data (batteries , use_all )
224
229
return sum (
225
230
min (battery .power_upper_bound , inverter .active_power_upper_bound )
226
231
for battery , inverter in pairs_data
227
232
)
228
233
229
- def _get_lower_bound (self , batteries : Set [int ]) -> float :
234
+ def _get_lower_bound (self , batteries : Set [int ], use_all : bool ) -> float :
230
235
"""Get total lower bound of power to be set for given batteries.
231
236
232
237
Note, output of that function doesn't guarantee that this bound will be
233
238
the same when the request is processed.
234
239
235
240
Args:
236
241
batteries: List of batteries
242
+ use_all: flag whether all batteries must be used for the power request.
237
243
238
244
Returns:
239
245
Lower bound for `set_power` operation.
240
246
"""
241
- pairs_data : List [InvBatPair ] = self ._get_components_data (batteries )
247
+ pairs_data : List [InvBatPair ] = self ._get_components_data (batteries , use_all )
242
248
return sum (
243
249
max (battery .power_lower_bound , inverter .active_power_lower_bound )
244
250
for battery , inverter in pairs_data
@@ -266,7 +272,7 @@ async def run(self) -> None:
266
272
267
273
try :
268
274
pairs_data : List [InvBatPair ] = self ._get_components_data (
269
- request .batteries
275
+ request .batteries , request . force
270
276
)
271
277
except KeyError as err :
272
278
await user .channel .send (Error (request = request , msg = str (err )))
@@ -373,7 +379,7 @@ def _check_request(self, request: Request) -> Optional[Result]:
373
379
Result for the user if the request is wrong, None otherwise.
374
380
"""
375
381
for battery in request .batteries :
376
- if battery not in self ._battery_receivers :
382
+ if battery not in self ._battery_receivers and request . force is False :
377
383
msg = (
378
384
f"No battery { battery } , available batteries: "
379
385
f"{ list (self ._battery_receivers .keys ())} "
@@ -382,11 +388,11 @@ def _check_request(self, request: Request) -> Optional[Result]:
382
388
383
389
if not request .adjust_power :
384
390
if request .power < 0 :
385
- bound = self ._get_lower_bound (request .batteries )
391
+ bound = self ._get_lower_bound (request .batteries , request . force )
386
392
if request .power < bound :
387
393
return OutOfBound (request = request , bound = bound )
388
394
else :
389
- bound = self ._get_upper_bound (request .batteries )
395
+ bound = self ._get_upper_bound (request .batteries , request . force )
390
396
if request .power > bound :
391
397
return OutOfBound (request = request , bound = bound )
392
398
@@ -535,11 +541,14 @@ def _get_components_pairs(
535
541
536
542
return bat_inv_map , inv_bat_map
537
543
538
- def _get_components_data (self , batteries : Set [int ]) -> List [InvBatPair ]:
544
+ def _get_components_data (
545
+ self , batteries : Set [int ], use_all : bool
546
+ ) -> List [InvBatPair ]:
539
547
"""Get data for the given batteries and adjacent inverters.
540
548
541
549
Args:
542
550
batteries: Batteries that needs data.
551
+ use_all: flag whether all batteries must be used for the power request.
543
552
544
553
Raises:
545
554
KeyError: If any battery in the given list doesn't exists in microgrid.
@@ -549,11 +558,13 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
549
558
"""
550
559
pairs_data : List [InvBatPair ] = []
551
560
working_batteries = (
552
- self ._all_battery_status .get_working_batteries (batteries ) or batteries
561
+ batteries
562
+ if use_all
563
+ else self ._all_battery_status .get_working_batteries (batteries ) or batteries
553
564
)
554
565
555
566
for battery_id in working_batteries :
556
- if battery_id not in self ._battery_receivers :
567
+ if battery_id not in self ._battery_receivers and use_all is False :
557
568
raise KeyError (
558
569
f"No battery { battery_id } , "
559
570
f"available batteries: { list (self ._battery_receivers .keys ())} "
@@ -562,6 +573,8 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
562
573
inverter_id : int = self ._bat_inv_map [battery_id ]
563
574
564
575
data = self ._get_battery_inverter_data (battery_id , inverter_id )
576
+ if data is None and use_all is True :
577
+ data = self ._cached_metrics [battery_id ]
565
578
if data is None :
566
579
_logger .warning (
567
580
"Skipping battery %d because its message isn't correct." ,
@@ -629,7 +642,8 @@ def _get_battery_inverter_data(
629
642
630
643
# If all values are ok then return them.
631
644
if not any (map (isnan , replaceable_metrics )):
632
- return InvBatPair (battery_data , inverter_data )
645
+ self ._cached_metrics [battery_id ] = InvBatPair (battery_data , inverter_data )
646
+ return self ._cached_metrics [battery_id ]
633
647
634
648
# Replace NaN with the corresponding value in the adjacent component.
635
649
# If both metrics are None, return None to ignore this battery.
@@ -651,10 +665,11 @@ def _get_battery_inverter_data(
651
665
elif isnan (inv_bound ):
652
666
inverter_new_metrics [inv_attr ] = bat_bound
653
667
654
- return InvBatPair (
668
+ self . _cached_metrics [ battery_id ] = InvBatPair (
655
669
replace (battery_data , ** battery_new_metrics ),
656
670
replace (inverter_data , ** inverter_new_metrics ),
657
671
)
672
+ return self ._cached_metrics [battery_id ]
658
673
659
674
async def _create_channels (self ) -> None :
660
675
"""Create channels to get data of components in microgrid."""
0 commit comments