From 2d67b16120a0f1f61d8229cd20abcbd4e23ad2c2 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Fri, 13 Jun 2025 14:23:49 +0200 Subject: [PATCH] scheduled charging: set next plan as active if current plan is fulfilled --- packages/control/ev/charge_template.py | 10 +++++++-- packages/control/ev/charge_template_test.py | 23 +++++++++++++++++++ packages/helpermodules/timecheck.py | 6 +++-- packages/helpermodules/timecheck_test.py | 25 ++++++++++++--------- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index 399d08e4a9..9d7fe4ff91 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -325,9 +325,15 @@ def scheduled_charging_recent_plan(self, raise ValueError("Um Zielladen mit SoC-Ziel nutzen zu können, bitte ein SoC-Modul konfigurieren " f"oder im Plan {p.name} als Begrenzung Energie einstellen.") try: + if ((p.limit.selected == "amount" and used_amount >= p.limit.amount) or + (p.limit.selected == "soc" and soc >= p.limit.soc_scheduled)): + plan_fulfilled = True + else: + plan_fulfilled = False plans_diff_end_date.append( - {p.id: timecheck.check_end_time(p, chargemode_switch_timestamp)}) - log.debug("Verbleibende Zeit bis zum Zieltermin [s]: "+str(plans_diff_end_date)) + {p.id: timecheck.check_end_time(p, chargemode_switch_timestamp, plan_fulfilled)}) + log.debug(f"Verbleibende Zeit bis zum Zieltermin [s]: {plans_diff_end_date}, " + f"Plan erfüllt: {plan_fulfilled}") except Exception: log.exception("Fehler im ev-Modul "+str(self.ct_num)) if plans_diff_end_date: diff --git a/packages/control/ev/charge_template_test.py b/packages/control/ev/charge_template_test.py index 08f88c8fc9..9e9cc4b260 100644 --- a/packages/control/ev/charge_template_test.py +++ b/packages/control/ev/charge_template_test.py @@ -212,6 +212,29 @@ def test_scheduled_charging_recent_plan(end_time_mock, selected_plan = None +def test_scheduled_charging_recent_plan_fulfilled(monkeypatch): + # setup + # der erste PLan ist erfüllt, der zweite wird ausgewählt + calculate_duration_mock = Mock(return_value=(100, 3000, 3, 500)) + monkeypatch.setattr(ChargeTemplate, "_calc_remaining_time", calculate_duration_mock) + check_end_time_mock = Mock(side_effect=[None, 1500]) + monkeypatch.setattr(timecheck, "check_end_time", check_end_time_mock) + ct = ChargeTemplate() + plan_mock_0 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=0, limit=Limit(selected="amount")) + plan_mock_1 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=1, limit=Limit(selected="amount")) + ct.data.chargemode.scheduled_charging.plans = {"0": plan_mock_0, "1": plan_mock_1} + + # execution + selected_plan = ct.scheduled_charging_recent_plan( + 60, EvTemplate(), 3, 1200, 3, True, ChargingType.AC.value, 1652688000, Mock(spec=ControlParameter)) + + # evaluation + if selected_plan: + assert selected_plan.plan.id == 1 + else: + selected_plan = None + + @pytest.mark.parametrize( "plan_data, soc, used_amount, selected, expected", [ diff --git a/packages/helpermodules/timecheck.py b/packages/helpermodules/timecheck.py index d3767fdfb5..c68855bc6d 100644 --- a/packages/helpermodules/timecheck.py +++ b/packages/helpermodules/timecheck.py @@ -112,7 +112,9 @@ def is_timeframe_valid(now: datetime.datetime, begin: datetime.datetime, end: da return state -def check_end_time(plan: ScheduledChargingPlan, chargemode_switch_timestamp: Optional[float]) -> Optional[float]: +def check_end_time(plan: ScheduledChargingPlan, + chargemode_switch_timestamp: Optional[float], + plan_fulfilled: bool) -> Optional[float]: """ prüft, ob der in angegebene Zeitpunkt abzüglich der Dauer jetzt ist. Um etwas Puffer zu haben, werden bei Überschreiten des Zeitpunkts die nachfolgenden 20 Min auch noch als Ladezeit zurückgegeben. @@ -151,7 +153,7 @@ def missed_date_still_active(remaining_time: float) -> bool: remaining_time = end - now else: raise TypeError(f'Unbekannte Häufigkeit {plan.frequency.selected}') - if chargemode_switch_timestamp and end.timestamp() < chargemode_switch_timestamp: + if chargemode_switch_timestamp and (end.timestamp() < chargemode_switch_timestamp or plan_fulfilled): # Als auf Zielladen umgeschaltet wurde, war der Termin schon vorbei return None else: diff --git a/packages/helpermodules/timecheck_test.py b/packages/helpermodules/timecheck_test.py index c42e76c279..72d72c5e34 100644 --- a/packages/helpermodules/timecheck_test.py +++ b/packages/helpermodules/timecheck_test.py @@ -19,25 +19,28 @@ def __init__(self, name: str, self.second_time = second_time -@pytest.mark.parametrize("time, selected, date, expected_remaining_time", - [pytest.param("9:00", "once", "2022-05-16", 1148, id="once"), - pytest.param("7:55", "once", "2022-05-16", None, id="missed date"), - pytest.param("8:05", "once", "2022-05-16", -2152, id="once missed date"), - pytest.param("12:00", "daily", [], 11948, id="daily today"), - pytest.param("2:00", "daily", [], 62348, id="daily missed today, use next day"), +@pytest.mark.parametrize("time, selected, date, plan_fulfilled, expected_remaining_time", + [pytest.param("9:00", "once", "2022-05-16", False, 1148, id="once"), + pytest.param("7:55", "once", "2022-05-16", False, None, id="missed date, plugged before"), + pytest.param("8:05", "once", "2022-05-16", False, - + 2152, id="once missed date, plugged after"), + pytest.param("12:00", "daily", [], False, 11948, id="daily today"), + pytest.param("2:00", "daily", [], False, 62348, id="daily missed today, use next day"), pytest.param("7:55", "weekly", [True, False, False, False, - False, False, False], 602048, id="weekly missed today"), + False, False, False], False, 602048, id="weekly missed today"), pytest.param("2:00", "weekly", [False, False, True, False, False, False, False], - 148748, + False, 148748, id="weekly missed today's date, no date on next day"), pytest.param("2:00", "weekly", [True, True, False, False, False, False, False], - 62348, + False, 62348, id="weekly missed today's date, date on next day"), + pytest.param("8:05", "once", "2022-05-16", True, None, id="once missed date, plugged after"), ] ) def test_check_end_time(time: str, selected: str, date: List, + plan_fulfilled: bool, expected_remaining_time: float): # setup plan = Mock(spec=ScheduledChargingPlan, time=time, frequency=Mock(spec=Frequency, selected=selected,)) @@ -46,7 +49,9 @@ def test_check_end_time(time: str, # execution remaining_time = timecheck.check_end_time( - plan, chargemode_switch_timestamp=datetime.datetime.strptime("5/16/2022 8:00", "%m/%d/%Y %H:%M").timestamp()) + plan, + chargemode_switch_timestamp=datetime.datetime.strptime("5/16/2022 8:00", "%m/%d/%Y %H:%M").timestamp(), + plan_fulfilled=plan_fulfilled) # evaluation assert remaining_time == expected_remaining_time