diff --git a/packages/control/algorithm/integration_test/conftest.py b/packages/control/algorithm/integration_test/conftest.py index dd91361d37..9998c861f3 100644 --- a/packages/control/algorithm/integration_test/conftest.py +++ b/packages/control/algorithm/integration_test/conftest.py @@ -45,6 +45,7 @@ def data_() -> None: data.data.counter_data["counter0"].data.config.max_total_power = 22000 data.data.counter_data["counter6"].data.get.currents = [0, 4, 2] data.data.counter_data["counter6"].data.get.power = 1380 + data.data.counter_data["counter6"].data.set.raw_power_left = None data.data.counter_data["counter6"].data.config.max_currents = [16]*3 data.data.counter_data["counter6"].data.config.max_total_power = 11000 data.data.counter_all_data = CounterAll() diff --git a/packages/control/algorithm/integration_test/pv_charging_test.py b/packages/control/algorithm/integration_test/pv_charging_test.py index 105e421470..8ee3aa0e7d 100644 --- a/packages/control/algorithm/integration_test/pv_charging_test.py +++ b/packages/control/algorithm/integration_test/pv_charging_test.py @@ -192,9 +192,9 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc raw_power_left=39650, raw_currents_left_counter0=[40]*3, raw_currents_left_counter6=[16]*3, - expected_current_cp3=16, - expected_current_cp4=7.8731884057971016, - expected_current_cp5=7.8731884057971016, + expected_current_cp3=11.248792270531402, + expected_current_cp4=7.248792270531401, + expected_current_cp5=7.2487922705314, expected_raw_power_left=24470, expected_surplus_power_left=1090, expected_reserved_surplus=0, diff --git a/packages/control/algorithm/surplus_controlled.py b/packages/control/algorithm/surplus_controlled.py index cb6b51669d..7a1f005458 100644 --- a/packages/control/algorithm/surplus_controlled.py +++ b/packages/control/algorithm/surplus_controlled.py @@ -120,7 +120,7 @@ def _fix_deviating_evse_current(self, chargepoint: Chargepoint) -> float: nicht erreicht wird. Wenn die Soll-Stromstärke nicht angepasst worden ist, nicht den ungenutzten EVSE-Strom aufschlagen.""" evse_current = chargepoint.data.get.evse_current - if evse_current and chargepoint.data.set.current != chargepoint.data.set.current_prev: + if evse_current is not None and chargepoint.data.set.current != chargepoint.data.set.current_prev: offset = evse_current - get_medium_charging_current(chargepoint.data.get.currents) offset = min(offset, MAX_EVSE_CURRENT_CHANGE) current_with_offset = chargepoint.data.set.current + offset diff --git a/packages/control/counter.py b/packages/control/counter.py index f64eaa3600..e7351b3cef 100644 --- a/packages/control/counter.py +++ b/packages/control/counter.py @@ -78,7 +78,7 @@ class Set: error_timer: Optional[float] = field(default=None, metadata={"topic": "set/error_timer"}) reserved_surplus: float = field(default=0, metadata={"topic": "set/reserved_surplus"}) released_surplus: float = field(default=0, metadata={"topic": "set/released_surplus"}) - raw_power_left: float = 0 + raw_power_left: Optional[float] = 0 raw_currents_left: List[float] = field(default_factory=currents_list_factory) surplus_power_left: float = 0 @@ -198,14 +198,14 @@ def update_values_left(self, diffs, cp_voltage: float) -> None: # Mittelwert der Spannungen verwenden, um Phasenverdrehung zu kompensieren # (Probleme bei einphasig angeschlossenen Wallboxen) self.data.set.raw_currents_left = list(map(operator.sub, self.data.set.raw_currents_left, diffs)) - if self.data.set.raw_power_left: + if self.data.set.raw_power_left is not None: self.data.set.raw_power_left -= sum([c * cp_voltage for c in diffs]) log.debug(f'Zähler {self.num}: {self.data.set.raw_currents_left}A verbleibende Ströme, ' f'{self.data.set.raw_power_left}W verbleibende Leistung') def update_surplus_values_left(self, diffs, cp_voltage: float) -> None: self.data.set.raw_currents_left = list(map(operator.sub, self.data.set.raw_currents_left, diffs)) - if self.data.set.surplus_power_left: + if self.data.set.surplus_power_left is not None: self.data.set.surplus_power_left -= sum([c * cp_voltage for c in diffs]) log.debug(f'Zähler {self.num}: {self.data.set.raw_currents_left}A verbleibende Ströme, ' f'{self.data.set.surplus_power_left}W verbleibender Überschuss') diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index 1a235c1422..8f5ec84df4 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -161,7 +161,7 @@ def time_charging(self, current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current phases = plan.phases_to_use id = plan.id - if plan.limit.selected == "soc" and soc and soc >= plan.limit.soc: + if plan.limit.selected == "soc" and soc is not None and soc >= plan.limit.soc: # SoC-Limit erreicht current = 0 sub_mode = "stop" @@ -204,7 +204,7 @@ def instant_charging(self, else: current = instant_charging.dc_current - if instant_charging.limit.selected == "soc" and soc and soc >= instant_charging.limit.soc: + if instant_charging.limit.selected == "soc" and soc is not None and soc >= instant_charging.limit.soc: current = 0 sub_mode = "stop" message = self.SOC_REACHED @@ -236,7 +236,7 @@ def pv_charging(self, phases = pv_charging.phases_to_use min_pv_current = (pv_charging.min_current if charging_type == ChargingType.AC.value else pv_charging.dc_min_current) - if pv_charging.limit.selected == "soc" and soc and soc >= pv_charging.limit.soc: + if pv_charging.limit.selected == "soc" and soc is not None and soc >= pv_charging.limit.soc: current = 0 sub_mode = "stop" message = self.SOC_REACHED @@ -282,7 +282,7 @@ def eco_charging(self, phases = eco_charging.phases_to_use current = eco_charging.current if charging_type == ChargingType.AC.value else eco_charging.dc_current - if eco_charging.limit.selected == "soc" and soc and soc >= eco_charging.limit.soc: + if eco_charging.limit.selected == "soc" and soc is not None and soc >= eco_charging.limit.soc: current = 0 sub_mode = "stop" message = self.SOC_REACHED diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index 59a56d174c..2816572e4a 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -102,8 +102,10 @@ def _limit_by_power(self, # (Probleme bei einphasig angeschlossenen Wallboxen) currents = available_currents.copy() limit = LoadmanagementLimit(None, None) - if raw_power_left: - if feed_in: + if raw_power_left is None: + return currents, limit + elif raw_power_left > 0: + if feed_in is not None: raw_power_left = raw_power_left - feed_in log.debug(f"Verbleibende Leistung unter Berücksichtigung der Einspeisegrenze: {raw_power_left}W") if sum([c * cp_voltage for c in available_currents]) > raw_power_left: @@ -117,7 +119,10 @@ def _limit_by_power(self, log.debug(f"Leistungsüberschreitung auf {raw_power_left}W korrigieren: {available_currents}") limit = LoadmanagementLimit(LimitingValue.POWER.value.format(get_component_name_by_id(counter.num)), LimitingValue.POWER) - return currents, limit + return currents, limit + else: + return [0]*3, LoadmanagementLimit(LimitingValue.POWER.value.format(get_component_name_by_id(counter.num)), + LimitingValue.POWER) # tested def _limit_by_current(self, @@ -152,7 +157,7 @@ def _limit_by_dimming(self, available_currents: List[float], cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: dimming_power_left, limit = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num}) - if dimming_power_left: + if dimming_power_left is not None: if sum(available_currents)*230 > dimming_power_left: phases = 3-available_currents.count(0) overload_per_phase = (sum(available_currents) - dimming_power_left/230)/phases diff --git a/packages/control/loadmanagement_test.py b/packages/control/loadmanagement_test.py index 04a3d2a8dd..48de9d41e9 100644 --- a/packages/control/loadmanagement_test.py +++ b/packages/control/loadmanagement_test.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional, Tuple from unittest.mock import Mock import pytest @@ -12,28 +12,31 @@ @pytest.mark.parametrize( - "available_currents, raw_power_left, expected_currents", + "available_currents, raw_power_left, expected_ret", [ pytest.param([5, 10, 15], 6900, ([5, 10, 15], LoadmanagementLimit(None, None))), pytest.param([5, 10, 25], 1000, ([0.5434782608695652, 1.0869565217391304, 2.717391304347826], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), LimitingValue.POWER))), + pytest.param([5, 10, 25], 0, ([0, 0, 0], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), + LimitingValue.POWER))), + pytest.param([5, 10, 25], None, ([5, 10, 25], LoadmanagementLimit(None, None))), pytest.param([5, 10, 25], 5000, ([2.717391304347826, 5.434782608695652, 13.58695652173913], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), LimitingValue.POWER))), ]) def test_limit_by_power(available_currents: List[float], - raw_power_left: float, - expected_currents: List[float], + raw_power_left: Optional[float], + expected_ret: Tuple[List[float], LoadmanagementLimit], monkeypatch): # setup counter_name_mock = Mock(return_value=COUNTER_NAME) monkeypatch.setattr(loadmanagement, "get_component_name_by_id", counter_name_mock) # evaluation - currents = Loadmanagement()._limit_by_power(Counter(0), available_currents, 230, raw_power_left, None) + ret = Loadmanagement()._limit_by_power(Counter(0), available_currents, 230, raw_power_left, None) # assertion - assert currents == expected_currents + assert ret == expected_ret @pytest.mark.parametrize(