@@ -195,6 +195,10 @@ def __init__(
195195 max_data_age_sec = 10.0 ,
196196 )
197197
198+ self ._cached_metrics : dict [int , InvBatPair | None ] = {
199+ bat_id : None for bat_id , _ in self ._bat_inv_map .items ()
200+ }
201+
198202 def _create_users_tasks (self ) -> List [asyncio .Task [None ]]:
199203 """For each user create a task to wait for request.
200204
@@ -208,37 +212,39 @@ def _create_users_tasks(self) -> List[asyncio.Task[None]]:
208212 )
209213 return tasks
210214
211- def _get_upper_bound (self , batteries : Set [int ]) -> float :
215+ def _get_upper_bound (self , batteries : Set [int ], use_all : bool ) -> float :
212216 """Get total upper bound of power to be set for given batteries.
213217
214218 Note, output of that function doesn't guarantee that this bound will be
215219 the same when the request is processed.
216220
217221 Args:
218222 batteries: List of batteries
223+ use_all: flag whether all batteries must be used for the power request.
219224
220225 Returns:
221226 Upper bound for `set_power` operation.
222227 """
223- pairs_data : List [InvBatPair ] = self ._get_components_data (batteries )
228+ pairs_data : List [InvBatPair ] = self ._get_components_data (batteries , use_all )
224229 return sum (
225230 min (battery .power_upper_bound , inverter .active_power_upper_bound )
226231 for battery , inverter in pairs_data
227232 )
228233
229- def _get_lower_bound (self , batteries : Set [int ]) -> float :
234+ def _get_lower_bound (self , batteries : Set [int ], use_all : bool ) -> float :
230235 """Get total lower bound of power to be set for given batteries.
231236
232237 Note, output of that function doesn't guarantee that this bound will be
233238 the same when the request is processed.
234239
235240 Args:
236241 batteries: List of batteries
242+ use_all: flag whether all batteries must be used for the power request.
237243
238244 Returns:
239245 Lower bound for `set_power` operation.
240246 """
241- pairs_data : List [InvBatPair ] = self ._get_components_data (batteries )
247+ pairs_data : List [InvBatPair ] = self ._get_components_data (batteries , use_all )
242248 return sum (
243249 max (battery .power_lower_bound , inverter .active_power_lower_bound )
244250 for battery , inverter in pairs_data
@@ -266,7 +272,7 @@ async def run(self) -> None:
266272
267273 try :
268274 pairs_data : List [InvBatPair ] = self ._get_components_data (
269- request .batteries
275+ request .batteries , request . force
270276 )
271277 except KeyError as err :
272278 await user .channel .send (Error (request = request , msg = str (err )))
@@ -373,7 +379,7 @@ def _check_request(self, request: Request) -> Optional[Result]:
373379 Result for the user if the request is wrong, None otherwise.
374380 """
375381 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 :
377383 msg = (
378384 f"No battery { battery } , available batteries: "
379385 f"{ list (self ._battery_receivers .keys ())} "
@@ -382,11 +388,11 @@ def _check_request(self, request: Request) -> Optional[Result]:
382388
383389 if not request .adjust_power :
384390 if request .power < 0 :
385- bound = self ._get_lower_bound (request .batteries )
391+ bound = self ._get_lower_bound (request .batteries , request . force )
386392 if request .power < bound :
387393 return OutOfBound (request = request , bound = bound )
388394 else :
389- bound = self ._get_upper_bound (request .batteries )
395+ bound = self ._get_upper_bound (request .batteries , request . force )
390396 if request .power > bound :
391397 return OutOfBound (request = request , bound = bound )
392398
@@ -535,11 +541,14 @@ def _get_components_pairs(
535541
536542 return bat_inv_map , inv_bat_map
537543
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 ]:
539547 """Get data for the given batteries and adjacent inverters.
540548
541549 Args:
542550 batteries: Batteries that needs data.
551+ use_all: flag whether all batteries must be used for the power request.
543552
544553 Raises:
545554 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]:
549558 """
550559 pairs_data : List [InvBatPair ] = []
551560 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
553564 )
554565
555566 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 :
557568 raise KeyError (
558569 f"No battery { battery_id } , "
559570 f"available batteries: { list (self ._battery_receivers .keys ())} "
@@ -562,6 +573,8 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
562573 inverter_id : int = self ._bat_inv_map [battery_id ]
563574
564575 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 ]
565578 if data is None :
566579 _logger .warning (
567580 "Skipping battery %d because its message isn't correct." ,
@@ -629,7 +642,8 @@ def _get_battery_inverter_data(
629642
630643 # If all values are ok then return them.
631644 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 ]
633647
634648 # Replace NaN with the corresponding value in the adjacent component.
635649 # If both metrics are None, return None to ignore this battery.
@@ -651,10 +665,11 @@ def _get_battery_inverter_data(
651665 elif isnan (inv_bound ):
652666 inverter_new_metrics [inv_attr ] = bat_bound
653667
654- return InvBatPair (
668+ self . _cached_metrics [ battery_id ] = InvBatPair (
655669 replace (battery_data , ** battery_new_metrics ),
656670 replace (inverter_data , ** inverter_new_metrics ),
657671 )
672+ return self ._cached_metrics [battery_id ]
658673
659674 async def _create_channels (self ) -> None :
660675 """Create channels to get data of components in microgrid."""
0 commit comments