@@ -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,38 +212,40 @@ def _create_users_tasks(self) -> List[asyncio.Task[None]]:
208212 )
209213 return tasks
210214
211- def _get_upper_bound (self , batteries : Set [int ]) -> int :
215+ def _get_upper_bound (self , batteries : Set [int ], use_all : bool ) -> int :
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 bound = sum (
225230 min (battery .power_upper_bound , inverter .active_power_upper_bound )
226231 for battery , inverter in pairs_data
227232 )
228233 return floor (bound )
229234
230- def _get_lower_bound (self , batteries : Set [int ]) -> int :
235+ def _get_lower_bound (self , batteries : Set [int ], use_all : bool ) -> int :
231236 """Get total lower bound of power to be set for given batteries.
232237
233238 Note, output of that function doesn't guarantee that this bound will be
234239 the same when the request is processed.
235240
236241 Args:
237242 batteries: List of batteries
243+ use_all: flag whether all batteries must be used for the power request.
238244
239245 Returns:
240246 Lower bound for `set_power` operation.
241247 """
242- pairs_data : List [InvBatPair ] = self ._get_components_data (batteries )
248+ pairs_data : List [InvBatPair ] = self ._get_components_data (batteries , use_all )
243249 bound = sum (
244250 max (battery .power_lower_bound , inverter .active_power_lower_bound )
245251 for battery , inverter in pairs_data
@@ -268,7 +274,7 @@ async def run(self) -> None:
268274
269275 try :
270276 pairs_data : List [InvBatPair ] = self ._get_components_data (
271- request .batteries
277+ request .batteries , request . force
272278 )
273279 except KeyError as err :
274280 await user .channel .send (Error (request = request , msg = str (err )))
@@ -375,7 +381,7 @@ def _check_request(self, request: Request) -> Optional[Result]:
375381 Result for the user if the request is wrong, None otherwise.
376382 """
377383 for battery in request .batteries :
378- if battery not in self ._battery_receivers :
384+ if battery not in self ._battery_receivers and request . force is False :
379385 msg = (
380386 f"No battery { battery } , available batteries: "
381387 f"{ list (self ._battery_receivers .keys ())} "
@@ -384,11 +390,11 @@ def _check_request(self, request: Request) -> Optional[Result]:
384390
385391 if not request .adjust_power :
386392 if request .power < 0 :
387- bound = self ._get_lower_bound (request .batteries )
393+ bound = self ._get_lower_bound (request .batteries , request . force )
388394 if request .power < bound :
389395 return OutOfBound (request = request , bound = bound )
390396 else :
391- bound = self ._get_upper_bound (request .batteries )
397+ bound = self ._get_upper_bound (request .batteries , request . force )
392398 if request .power > bound :
393399 return OutOfBound (request = request , bound = bound )
394400
@@ -537,11 +543,14 @@ def _get_components_pairs(
537543
538544 return bat_inv_map , inv_bat_map
539545
540- def _get_components_data (self , batteries : Set [int ]) -> List [InvBatPair ]:
546+ def _get_components_data (
547+ self , batteries : Set [int ], use_all : bool
548+ ) -> List [InvBatPair ]:
541549 """Get data for the given batteries and adjacent inverters.
542550
543551 Args:
544552 batteries: Batteries that needs data.
553+ use_all: flag whether all batteries must be used for the power request.
545554
546555 Raises:
547556 KeyError: If any battery in the given list doesn't exists in microgrid.
@@ -551,11 +560,13 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
551560 """
552561 pairs_data : List [InvBatPair ] = []
553562 working_batteries = (
554- self ._all_battery_status .get_working_batteries (batteries ) or batteries
563+ batteries
564+ if use_all
565+ else self ._all_battery_status .get_working_batteries (batteries ) or batteries
555566 )
556567
557568 for battery_id in working_batteries :
558- if battery_id not in self ._battery_receivers :
569+ if battery_id not in self ._battery_receivers and use_all is False :
559570 raise KeyError (
560571 f"No battery { battery_id } , "
561572 f"available batteries: { list (self ._battery_receivers .keys ())} "
@@ -564,6 +575,8 @@ def _get_components_data(self, batteries: Set[int]) -> List[InvBatPair]:
564575 inverter_id : int = self ._bat_inv_map [battery_id ]
565576
566577 data = self ._get_battery_inverter_data (battery_id , inverter_id )
578+ if data is None and use_all is True :
579+ data = self ._cached_metrics [battery_id ]
567580 if data is None :
568581 _logger .warning (
569582 "Skipping battery %d because its message isn't correct." ,
@@ -631,7 +644,8 @@ def _get_battery_inverter_data(
631644
632645 # If all values are ok then return them.
633646 if not any (map (isnan , replaceable_metrics )):
634- return InvBatPair (battery_data , inverter_data )
647+ self ._cached_metrics [battery_id ] = InvBatPair (battery_data , inverter_data )
648+ return self ._cached_metrics [battery_id ]
635649
636650 # Replace NaN with the corresponding value in the adjacent component.
637651 # If both metrics are None, return None to ignore this battery.
@@ -653,10 +667,11 @@ def _get_battery_inverter_data(
653667 elif isnan (inv_bound ):
654668 inverter_new_metrics [inv_attr ] = bat_bound
655669
656- return InvBatPair (
670+ self . _cached_metrics [ battery_id ] = InvBatPair (
657671 replace (battery_data , ** battery_new_metrics ),
658672 replace (inverter_data , ** inverter_new_metrics ),
659673 )
674+ return self ._cached_metrics [battery_id ]
660675
661676 async def _create_channels (self ) -> None :
662677 """Create channels to get data of components in microgrid."""
0 commit comments