Skip to content

Commit d428160

Browse files
committed
Don't repeat zero commands to inverters
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent c553d06 commit d428160

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

src/frequenz/sdk/microgrid/_power_distributing/_component_managers/_battery_manager.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ def __init__(
183183
)
184184
"""The distribution algorithm used to distribute power between batteries."""
185185

186+
self._last_set_powers: dict[ComponentId, Power] = {}
187+
"""The last power value set to each battery's inverter."""
188+
186189
@override
187190
def component_ids(self) -> collections.abc.Set[ComponentId]:
188191
"""Return the set of component ids."""
@@ -638,8 +641,15 @@ async def _set_distributed_power(
638641
api.set_power(inverter_id, power.as_watts())
639642
)
640643
for inverter_id, power in distribution.distribution.items()
644+
if power != Power.zero()
645+
or self._last_set_powers.get(inverter_id) != Power.zero()
641646
}
642647

648+
if not tasks:
649+
if _logger.isEnabledFor(logging.DEBUG):
650+
_logger.debug("Not repeating 0W commands to batteries.")
651+
return Power.zero(), set()
652+
643653
if _logger.isEnabledFor(logging.DEBUG):
644654
battery_distribution = {
645655
self._inv_bats_map[inverter_id]: distribution.distribution[inverter_id]
@@ -727,6 +737,8 @@ def _parse_result(
727737
if failed:
728738
failed_power += distribution[inverter_id]
729739
failed_batteries.update(battery_ids)
740+
else:
741+
self._last_set_powers[inverter_id] = distribution[inverter_id]
730742

731743
return failed_power, failed_batteries
732744

tests/timeseries/_battery_pool/test_battery_pool_control_methods.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,48 @@ async def test_case_4(self, mocks: Mocks, mocker: MockerFixture) -> None:
551551
result, _power_distributing.Success
552552
),
553553
)
554+
555+
async def test_no_resend_0w(self, mocks: Mocks, mocker: MockerFixture) -> None:
556+
"""Test that 0W command is not resent unnecessarily."""
557+
set_power = typing.cast(
558+
AsyncMock, microgrid.connection_manager.get().api_client.set_power
559+
)
560+
await self._patch_battery_pool_status(mocks, mocker)
561+
await self._init_data_for_batteries(mocks)
562+
await self._init_data_for_inverters(mocks)
563+
564+
battery_pool = microgrid.new_battery_pool(priority=5)
565+
bounds_rx = battery_pool.power_status.new_receiver()
566+
567+
self._assert_report(
568+
await bounds_rx.receive(), power=None, lower=-4000.0, upper=4000.0
569+
)
570+
571+
# First send 0W command. This should result in API calls.
572+
await battery_pool.propose_power(Power.from_watts(0.0))
573+
574+
self._assert_report(
575+
await bounds_rx.receive(), power=0.0, lower=-4000.0, upper=4000.0
576+
)
577+
await asyncio.sleep(1.0) # Wait for the power to be distributed.
578+
assert set_power.call_count == 4
579+
assert sorted(set_power.call_args_list) == [
580+
mocker.call(inv_id, 0.0) for inv_id in mocks.microgrid.battery_inverter_ids
581+
]
582+
583+
set_power.reset_mock()
584+
585+
# Sending 0W again should not result in any API calls.
586+
await battery_pool.propose_power(Power.from_watts(0.0))
587+
await asyncio.sleep(1.0) # Wait for the power to be distributed.
588+
assert set_power.call_count == 0
589+
590+
set_power.reset_mock()
591+
592+
# Sending a different power should result in API calls.
593+
await battery_pool.propose_power(Power.from_watts(100.0))
594+
await asyncio.sleep(1.0)
595+
assert set_power.call_count == 4
596+
assert sorted(set_power.call_args_list) == [
597+
mocker.call(inv_id, 25.0) for inv_id in mocks.microgrid.battery_inverter_ids
598+
]

0 commit comments

Comments
 (0)