diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py index c5d43b96cc..d05b359d3a 100644 --- a/packages/control/bat_all.py +++ b/packages/control/bat_all.py @@ -179,9 +179,9 @@ def calc_power_for_all_components(self): except Exception: log.exception("Fehler im Bat-Modul") - def _inverter_limited_power(self, inverter: Pv) -> float: - """gibt die maximale Entladeleistung des Speichers zurück, bis die maximale Ausgangsleistung des WR erreicht - ist.""" + def _get_pv_power_beyond_max_ac_out(self, inverter: Pv) -> float: + """gibt die PV-Leistung zurück, die über der maximalen Ausgangsleistung des Wechselrichters liegt und somit + nicht für die Entladung des Speichers genutzt werden kann.""" # tested # Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR # genutzt werden. @@ -193,18 +193,32 @@ def _inverter_limited_power(self, inverter: Pv) -> float: def _limit_bat_power_discharge(self, required_power): """begrenzt die für den Algorithmus benötigte Entladeleistung des Speichers, wenn die maximale Ausgangsleistung des WR erreicht ist.""" - inverter_limited_power = 0 + pv_power_beyond_max_ac_out = 0 if required_power > 0: # Nur wenn der Speicher entladen werden soll, fließt Leistung durch den WR. for inverter in data.data.pv_data.values(): try: - inverter_limited_power += self._inverter_limited_power(inverter) + pv_power_beyond_max_ac_out += self._get_pv_power_beyond_max_ac_out(inverter) except Exception: log.exception(f"Fehler im Bat-Modul {inverter.num}") - if inverter_limited_power > 0: - required_power = max(required_power-inverter_limited_power, 0) - log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W" - " begrenzt.") + # Wenn max_inverter_power_for_bat nur deshalb negativ ist, weil der Speicher aktuell bereits entlädt + # (self.data.get.power < 0) und keine PV-Leistung über der maximalen WR-Ausgangsleistung anliegt + # (pv_power_beyond_max_ac_out == 0), dann würde eine Begrenzung auf 0W die noch verfügbare + # Entladeleistung fälschlicherweise unterdrücken. In diesem Fall wenden wir keine zusätzliche + # Begrenzung durch die WR-Ausgangsleistung an. + if pv_power_beyond_max_ac_out > 0: + max_inverter_power_for_bat = self.data.get.power - pv_power_beyond_max_ac_out + # Negative Werte bedeuten, dass bereits mehr Leistung über den WR fließt, als für den Speicher + # zusätzlich verfügbar ist; in diesem Fall ist keine weitere Entladung möglich. + max_inverter_power_for_bat = max(max_inverter_power_for_bat, 0) + required_power = min(required_power, max_inverter_power_for_bat) + log.debug( + f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W begrenzt." + ) + else: + log.debug( + "Speicher-Entladeleistung nicht durch maximale WR-Ausgangsleistung begrenzt, da keine PV-Leistung " + "über der maximalen WR-Ausgangsleistung anliegt.") return required_power def _set_bat_power_active_control(self, power): diff --git a/packages/control/bat_all_test.py b/packages/control/bat_all_test.py index ca3d98f972..0b43a7560d 100644 --- a/packages/control/bat_all_test.py +++ b/packages/control/bat_all_test.py @@ -16,9 +16,8 @@ from modules.devices.generic.mqtt.config import MqttBatSetup -@pytest.fixture +@pytest.fixture(autouse=True) def data_fixture() -> None: - data.data_init(Mock()) data.data.general_data = General() data.data.cp_all_data = Mock(spec=AllChargepoints, data=Mock( spec=AllChargepointData, get=Mock(spec=AllGet, power=0))) @@ -35,7 +34,7 @@ def data_fixture() -> None: pytest.param(0, -6000, 0, id="max_ac_out ist 0"), ], ) -def test_inverter_limited_power(max_ac_out, power, expected_result): +def test_pv_power_beyond_max_ac_out(max_ac_out: int, power: int, expected_result: int): # Mock für die Pv-Klasse inverter = Pv(1) inverter.data.config.max_ac_out = max_ac_out @@ -43,26 +42,37 @@ def test_inverter_limited_power(max_ac_out, power, expected_result): bat_all = BatAll() # Aufruf der zu testenden Funktion - result = bat_all._inverter_limited_power(inverter) + result = bat_all._get_pv_power_beyond_max_ac_out(inverter) # Überprüfung des Ergebnisses assert result == expected_result @pytest.mark.parametrize( - "required_power, return_inverter_limited_power, expected_power", + "bat_power, required_power, return_pv_power_beyond_max_ac_out, expected_power", [ - pytest.param(1000, 0, 1000, id="maximale Entladeleistung nicht erreicht"), - pytest.param(1000, 100, 900, id="maximale Entladeleistung erreicht"), - pytest.param(-1000, 10, -1000, id="Speicher soll nicht mehr entladen werden"), + pytest.param(1000, 1000, 0, 1000, id="exakt max Leistung des WR"), + pytest.param(1000, 1000, 100, 900, id="max Leistung des WR um 100W überschritten"), + pytest.param(1000, 1000, 1100, 0, id="maximale Entladeleistung erreicht"), + pytest.param(3000, 3000, 2000, 1000, id="max Leistung des WR um 2000W überschritten"), + pytest.param(3000, 5000, 2000, 1000, id="max Leistung des WR um 2000W überschritten, " + + "erlaubte Entladeleistung höher als aktuelle Leistung"), + pytest.param(-1000, 1100, 0, 1100, id="Speicher entlädt, soll entladen"), + pytest.param(-1000, -600, 0, -600, id="Speicher entlädt, soll weniger entladen"), + pytest.param(0, 600, 0, 600, id="Speicher ruht, soll entladen"), ]) -def test_limit_bat_power_discharge(required_power, return_inverter_limited_power, expected_power, monkeypatch): +def test_limit_bat_power_discharge(bat_power: int, + required_power: int, + return_pv_power_beyond_max_ac_out: int, + expected_power: int, + monkeypatch): # setup data.data.pv_data = {"pv2": Pv(2)} - mock_inverter_limited_power = Mock(return_value=return_inverter_limited_power) - monkeypatch.setattr(BatAll, "_inverter_limited_power", mock_inverter_limited_power) + mock_pv_power_beyond_max_ac_out = Mock(return_value=return_pv_power_beyond_max_ac_out) + monkeypatch.setattr(BatAll, "_get_pv_power_beyond_max_ac_out", mock_pv_power_beyond_max_ac_out) b = BatAll() + b.data.get.power = bat_power # execution power = b._limit_bat_power_discharge(required_power)