diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index db95b7666c..a26b402769 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -558,6 +558,10 @@ def check_min_max_current(self, required_current: float, phases: int) -> float: required_current = self.data.get.max_discharge_power / phases / 230 msg = f"Die vom Auto übertragene Entladeleistung begrenzt den Strom auf " \ f"maximal {round(required_current, 2)} A." + # eigene Hardware-Stromgrenze des Ladepunkts auch beim Entladen durchsetzen, nicht nur + # beim Laden - check_cp_max_current() behandelt negative Werte bereits korrekt + # (Vorzeichen wird ueber abs()/sign wieder hergestellt). + required_current = self.check_cp_max_current(required_current, phases) else: if self.data.get.max_charge_power / phases / 230 < required_current: required_current = self.data.get.max_charge_power / phases / 230 diff --git a/packages/control/chargepoint/chargepoint_test.py b/packages/control/chargepoint/chargepoint_test.py index 57a746649b..c7a036e363 100644 --- a/packages/control/chargepoint/chargepoint_test.py +++ b/packages/control/chargepoint/chargepoint_test.py @@ -5,6 +5,8 @@ from control import data from control.chargelog import chargelog +from control.chargemode import Chargemode +from control.chargepoint.charging_type import ChargingType from control.chargepoint.chargepoint import Chargepoint from control.chargepoint.chargepoint_state import ChargepointState from control.chargepoint.chargepoint_template import CpTemplate @@ -66,6 +68,33 @@ def test_check_min_max_current(required_current, phases, expected_required_curre assert ret == expected_required_current +@pytest.mark.parametrize("required_current, max_discharge_power, dc_max_current, expected_required_current", + [ + pytest.param(-50, -30000, 25, -25, + id="Entladung durch CP-Hardwaregrenze begrenzt"), + pytest.param(-10, -30000, 25, -10, + id="Entladung innerhalb der CP-Hardwaregrenze"), + ]) +def test_check_min_max_current_bidi_discharge_respects_cp_hardware_limit( + required_current, max_discharge_power, dc_max_current, expected_required_current): + """ Beim Entladen (BIDI_CHARGING, required_current < 0) muss die eigene Hardware-Stromgrenze des + Ladepunkts (dc_max_current) genauso durchgesetzt werden wie beim Laden - vor diesem Fix wurde + check_cp_max_current() im Entlade-Zweig gar nicht aufgerufen (siehe #3795). """ + # setup + cp = Chargepoint(0, None) + cp.template = CpTemplate() + cp.template.data.charging_type = ChargingType.DC.value + cp.template.data.dc_max_current = dc_max_current + cp.data.control_parameter.submode = Chargemode.BIDI_CHARGING + cp.data.get.max_discharge_power = max_discharge_power + + # evaluation + ret = cp.check_min_max_current(required_current, 1) + + # assertion + assert ret == expected_required_current + + @dataclass class Params: name: str diff --git a/packages/control/counter.py b/packages/control/counter.py index 4266315049..3e9634ea46 100644 --- a/packages/control/counter.py +++ b/packages/control/counter.py @@ -210,7 +210,12 @@ def _set_current_left(self, loadmanagement_available: bool) -> None: load.data.get.currents) except KeyError: element_current = [get_medium_charging_current(load.data.get.currents)]*3 - if min(element_current) < 0: + # Vorzeichen anhand der Gesamtleistung entscheiden, nicht anhand einzelner Phasenströme + # (min(element_current) < 0): bei ein-/zweiphasigem Laden zeigen ungenutzte Phasen oft ein + # geringes negatives Messrauschen (zB -0.05A), das sonst den ganzen Load faelschlich als + # Einspeisung einordnet - der eigene Ladestrom wird dann nirgends von currents_raw + # abgezogen und das Lastmanagement haelt die eigene Ladung faelschlich fuer Fremdlast. + if load.data.get.power < 0: # nur Hausverbraucher ohne Einspeisung currents_exported_raw = list(map(operator.sub, currents_exported_raw, element_current)) else: diff --git a/packages/control/counter_test.py b/packages/control/counter_test.py index 640b38fb85..b8550a815a 100644 --- a/packages/control/counter_test.py +++ b/packages/control/counter_test.py @@ -111,6 +111,30 @@ def test_set_current_left(loadmanagement_available: bool, assert counter.data.set.raw_currents_left == expected_raw_currents_left +def test_set_current_left_nets_out_own_current_despite_noise_on_unused_phase(monkeypatch, data_): + """ Ein einphasig ladender Ladepunkt zeigt auf ungenutzten Phasen oft ein geringes negatives + Messrauschen (zB -0.05A). min(element_current) < 0 hat das faelschlich als Einspeisung eingeordnet, + wodurch der eigene Ladestrom nirgends von currents_raw abgezogen wurde - das Lastmanagement hielt + die eigene Ladung dann faelschlich fuer Fremdlast (siehe Matts Log in Discussion #3908).""" + # setup + get_loads_of_counter_mock = Mock(return_value=["cp4"]) + monkeypatch.setattr(data.data.counter_all_data, "get_loads_of_counter", get_loads_of_counter_mock) + data.data.cp_data["cp4"].data.config.phase_1 = 1 + data.data.cp_data["cp4"].data.get.currents = [20, 0.05, -0.05] + data.data.cp_data["cp4"].data.get.power = 4600 + counter = Counter(0) + counter.data.config.max_currents = [35]*3 + counter.data.config.max_total_power = 35*3*230 + counter.data.config.max_power_errorcase = 7000 + counter.data.get.currents = [10, -5, -3] + + # execution + counter._set_current_left(True) + + # evaluation + assert counter.data.set.raw_currents_left == [45, 40.05, 37.95] + + @dataclass class Params: name: str