From 297189f6b314f4acebb47ef5e984af762d1339b4 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Mon, 27 Jan 2025 16:10:09 +0100 Subject: [PATCH 01/13] ids in charge and ev template config --- .../algorithm/additional_current_test.py | 2 +- .../algorithm/surplus_controlled_test.py | 2 +- packages/control/chargepoint/chargepoint.py | 6 +++--- packages/control/counter_test.py | 2 +- packages/control/ev/charge_template.py | 10 +++++----- packages/control/ev/charge_template_test.py | 20 +++++++++---------- packages/control/ev/ev.py | 2 +- packages/control/ev/ev_template.py | 2 +- packages/helpermodules/subdata.py | 6 +++--- packages/helpermodules/update_config.py | 19 ++++++++++++++---- 10 files changed, 41 insertions(+), 30 deletions(-) diff --git a/packages/control/algorithm/additional_current_test.py b/packages/control/algorithm/additional_current_test.py index 89c552aeff..4a8904a082 100644 --- a/packages/control/algorithm/additional_current_test.py +++ b/packages/control/algorithm/additional_current_test.py @@ -30,7 +30,7 @@ def test_set_loadmangement_message(set_current, limit, expected_msg, monkeypatch): # setup ev = Ev(0) - ev.charge_template = ChargeTemplate(0) + ev.charge_template = ChargeTemplate() cp1 = Chargepoint(1, None) cp1.data = ChargepointData(set=Set(current=set_current), control_parameter=ControlParameter(required_currents=[8]*3)) diff --git a/packages/control/algorithm/surplus_controlled_test.py b/packages/control/algorithm/surplus_controlled_test.py index 6e50210534..c36f671959 100644 --- a/packages/control/algorithm/surplus_controlled_test.py +++ b/packages/control/algorithm/surplus_controlled_test.py @@ -41,7 +41,7 @@ def test_filter_by_feed_in_limit(feed_in_limit_1: bool, # setup def setup_cp(cp: Chargepoint, feed_in_limit: bool) -> Chargepoint: ev = Ev(0) - ev.charge_template = ChargeTemplate(0) + ev.charge_template = ChargeTemplate() ev.charge_template.data.chargemode.pv_charging.feed_in_limit = feed_in_limit cp.data = ChargepointData(set=Set(charging_ev_data=ev)) return cp diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index f3f909d332..2473da5738 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -797,8 +797,8 @@ def _pub_connected_vehicle(self, vehicle: Ev): else: current_plan = None config_obj = ConnectedConfig( - charge_template=vehicle.charge_template.ct_num, - ev_template=vehicle.ev_template.et_num, + charge_template=vehicle.charge_template.data.id, + ev_template=vehicle.ev_template.data.id, chargemode=vehicle.charge_template.data.chargemode.selected, priority=vehicle.charge_template.data.prio, current_plan=current_plan, @@ -806,7 +806,7 @@ def _pub_connected_vehicle(self, vehicle: Ev): time_charging_in_use=True if (self.data.control_parameter.submode == "time_charging") else False) if soc_obj != self.data.get.connected_vehicle.soc: - Pub().pub("openWB/chargepoint/"+str(self.num) + + Pub().pub("openWB/chargepoint/"+str(self.data.id) + "/get/connected_vehicle/soc", dataclasses.asdict(soc_obj)) if info_obj != self.data.get.connected_vehicle.info: Pub().pub("openWB/chargepoint/"+str(self.num) + diff --git a/packages/control/counter_test.py b/packages/control/counter_test.py index 4b10d1335f..4b3e59466a 100644 --- a/packages/control/counter_test.py +++ b/packages/control/counter_test.py @@ -140,7 +140,7 @@ def test_switch_on_threshold_reached(params: Params, caplog, general_data_fixtur cp.data.control_parameter.phases = 1 cp.data.control_parameter.state = params.state cp.data.control_parameter.timestamp_switch_on_off = params.timestamp_switch_on_off - ev.data.charge_template = ChargeTemplate(0) + ev.data.charge_template = ChargeTemplate() ev.data.charge_template.data.chargemode.pv_charging.feed_in_limit = params.feed_in_limit cp.data.set.charging_ev_data = ev mock_calc_switch_on_power = Mock(return_value=[params.surplus, params.threshold]) diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index ed0493350c..97214b5a1e 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -97,6 +97,7 @@ def et_factory() -> Et: @dataclass class ChargeTemplateData: + id: int = 0 name: str = "Lade-Profil" prio: bool = False load_default: bool = False @@ -124,7 +125,6 @@ class SelectedPlan: class ChargeTemplate: """ Klasse der Lade-Profile """ - ct_num: int data: ChargeTemplateData = field(default_factory=charge_template_data_factory, metadata={ "topic": ""}) @@ -175,7 +175,7 @@ def time_charging(self, log.debug(message) return 0, "stop", message, None except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) + log.exception("Fehler im ev-Modul "+str(self.data.id)) return 0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), None INSTANT_CHARGING_SOC_REACHED = "Kein Sofortladen, da der Soc bereits erreicht wurde." @@ -215,7 +215,7 @@ def instant_charging(self, else: raise TypeError(f'{instant_charging.limit.selected} unbekanntes Sofortladen-Limit.') except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) + log.exception("Fehler im ev-Modul "+str(self.data.id)) return 0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc() PV_CHARGING_SOC_REACHED = "Keine Ladung, da der maximale Soc bereits erreicht wurde." @@ -250,7 +250,7 @@ def pv_charging(self, soc: Optional[float], min_current: int, charging_type: str else: return 0, "stop", self.PV_CHARGING_SOC_REACHED except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) + log.exception("Fehler im ev-Modul "+str(self.data.id)) return 0, "stop", "Keine Ladung, da ein interner Fehler aufgetreten ist: "+traceback.format_exc() def scheduled_charging_recent_plan(self, @@ -347,7 +347,7 @@ def _search_plan(self, log.debug(f"Plan-Nr. {plan.id}: Differenz zum Start {remaining_time}s, Dauer {duration/3600}h, " f"Termin heute verpasst: {missed_date_today}") except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) + log.exception("Fehler im ev-Modul "+str(self.data.id)) return plan_data def _calculate_duration(self, diff --git a/packages/control/ev/charge_template_test.py b/packages/control/ev/charge_template_test.py index a036ffed0a..c1572c6f68 100644 --- a/packages/control/ev/charge_template_test.py +++ b/packages/control/ev/charge_template_test.py @@ -7,7 +7,7 @@ from control import optional from control.ev.charge_template import SelectedPlan from control.chargepoint.charging_type import ChargingType -from control.ev.ev import ChargeTemplate +from control.ev.charge_template import ChargeTemplate from control.ev.ev_template import EvTemplate, EvTemplateData from control.general import General from helpermodules import timecheck @@ -57,7 +57,7 @@ def test_time_charging(plans: Dict[int, TimeChargingPlan], soc: float, used_amou expected: Tuple[int, str, Optional[str], Optional[str]], monkeypatch): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.time_charging.plans = plans check_plans_timeframe_mock = Mock(return_value=plan_found) monkeypatch.setattr(timecheck, "check_plans_timeframe", check_plans_timeframe_mock) @@ -85,7 +85,7 @@ def test_instant_charging(selected: str, current_soc: float, used_amount: float, expected: Tuple[int, str, Optional[str]]): # setup data.data.optional_data.data.et.active = False - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.chargemode.instant_charging.limit.selected = selected # execution @@ -109,7 +109,7 @@ def test_instant_charging(selected: str, current_soc: float, used_amount: float, def test_pv_charging(min_soc: int, min_current: int, current_soc: float, expected: Tuple[int, str, Optional[str]]): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.chargemode.pv_charging.min_soc = min_soc ct.data.chargemode.pv_charging.min_current = min_current data.data.bat_all_data.data.config.configured = True @@ -147,7 +147,7 @@ def test_pv_charging(min_soc: int, min_current: int, current_soc: float, @pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) def test_scheduled_charging_recent_plan(params: Params, monkeypatch): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() get_phases_chargemode_mock = Mock(return_value=params.chargemode_phases) monkeypatch.setattr(data.data.general_data, "get_phases_chargemode", get_phases_chargemode_mock) search_plan_mock = Mock(return_value=params.search_plan) @@ -172,7 +172,7 @@ def test_scheduled_charging_recent_plan(params: Params, monkeypatch): ]) def test_calculate_duration(selected: str, phases: int, expected_duration: float, expected_missing_amount: float): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() plan = ScheduledChargingPlan() plan.limit.selected = selected # execution @@ -201,7 +201,7 @@ def test_search_plan(check_duration_return1: Tuple[Optional[float], bool], monkeypatch.setattr(ChargeTemplate, "_calculate_duration", calculate_duration_mock) check_duration_mock = Mock(side_effect=[check_duration_return1, check_duration_return2]) monkeypatch.setattr(timecheck, "check_duration", check_duration_mock) - ct = ChargeTemplate(0) + 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} @@ -252,7 +252,7 @@ def test_scheduled_charging_calc_current(plan_data: SelectedPlan, selected: str, expected: Tuple[float, str, str, int]): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() plan = ScheduledChargingPlan(active=True, id=0) plan.limit.selected = selected # json verwandelt Keys in strings @@ -267,7 +267,7 @@ def test_scheduled_charging_calc_current(plan_data: SelectedPlan, def test_scheduled_charging_calc_current_no_plans(): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() # execution ret = ct.scheduled_charging_calc_current(None, 63, 5, 3, 6, 0) @@ -284,7 +284,7 @@ def test_scheduled_charging_calc_current_no_plans(): ]) def test_scheduled_charging_calc_current_electricity_tariff(loading_hour, expected, monkeypatch): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() plan = ScheduledChargingPlan(active=True) plan.limit.selected = "soc" ct.data.chargemode.scheduled_charging.plans = {"0": plan} diff --git a/packages/control/ev/ev.py b/packages/control/ev/ev.py index 2f31fd1425..c5d48aad7d 100644 --- a/packages/control/ev/ev.py +++ b/packages/control/ev/ev.py @@ -86,7 +86,7 @@ class Ev: def __init__(self, index: int): try: self.ev_template: EvTemplate = EvTemplate() - self.charge_template: ChargeTemplate = ChargeTemplate(0) + self.charge_template: ChargeTemplate = ChargeTemplate() self.soc_module: ConfigurableVehicle = None self.chargemode_changed = False self.submode_changed = False diff --git a/packages/control/ev/ev_template.py b/packages/control/ev/ev_template.py index 8da234a1ff..a8e8fbe02d 100644 --- a/packages/control/ev/ev_template.py +++ b/packages/control/ev/ev_template.py @@ -5,6 +5,7 @@ class EvTemplateData: dc_min_current: int = 0 dc_max_current: int = 0 + id: int = 0 name: str = "Fahrzeug-Profil" max_current_multi_phases: int = 16 max_phases: int = 3 @@ -33,4 +34,3 @@ class EvTemplate: data: EvTemplateData = field(default_factory=ev_template_data_factory, metadata={ "topic": "config"}) - et_num: int = 0 diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index f834771322..ecc099e140 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -304,7 +304,7 @@ def process_vehicle_topic(self, client: mqtt.Client, var: Dict[str, ev.Ev], msg: except Exception: log.exception("Fehler im subdata-Modul") - def process_vehicle_charge_template_topic(self, var: Dict[str, ev.ChargeTemplate], msg: mqtt.MQTTMessage): + def process_vehicle_charge_template_topic(self, var: Dict[str, ChargeTemplate], msg: mqtt.MQTTMessage): """ Handler für die EV-Topics Parameter @@ -322,7 +322,7 @@ def process_vehicle_charge_template_topic(self, var: Dict[str, ev.ChargeTemplate var.pop("ct"+index) else: if "ct"+index not in var: - var["ct"+index] = ev.ChargeTemplate(int(index)) + var["ct"+index] = ChargeTemplate() if re.search("/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/plans/[0-9]+$", msg.topic) is not None: index_second = get_second_index(msg.topic) @@ -378,7 +378,7 @@ def process_vehicle_ev_template_topic(self, var: Dict[str, EvTemplate], msg: mqt var.pop("et"+index) else: if "et"+index not in var: - var["et"+index] = EvTemplate(et_num=int(index)) + var["et"+index] = EvTemplate() var["et" + index].data = dataclass_from_dict(EvTemplateData, decode_payload(msg.payload)) self.event_ev_template.set() except Exception: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 59f5387e4e..9447533652 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -51,7 +51,7 @@ class UpdateConfig: - DATASTORE_VERSION = 75 + DATASTORE_VERSION = 76 valid_topic = [ "^openWB/bat/config/configured$", "^openWB/bat/config/power_limit_mode$", @@ -452,10 +452,10 @@ class UpdateConfig: ("openWB/counter/config/home_consumption_source_id", counter_all.Config().home_consumption_source_id), ("openWB/vehicle/0/name", "Standard-Fahrzeug"), ("openWB/vehicle/0/info", {"manufacturer": None, "model": None}), - ("openWB/vehicle/0/charge_template", ev.Ev(0).charge_template.ct_num), + ("openWB/vehicle/0/charge_template", ev.Ev(0).charge_template.data.id), ("openWB/vehicle/0/soc_module/config", NO_MODULE), ("openWB/vehicle/0/soc_module/general_config", dataclass_utils.asdict(GeneralVehicleConfig())), - ("openWB/vehicle/0/ev_template", ev.Ev(0).ev_template.et_num), + ("openWB/vehicle/0/ev_template", ev.Ev(0).ev_template.data.id), ("openWB/vehicle/0/tag_id", ev.Ev(0).data.tag_id), ("openWB/vehicle/0/get/soc", ev.Ev(0).data.get.soc), ("openWB/vehicle/template/ev_template/0", asdict(EvTemplateData(name="Standard-Fahrzeug-Profil", @@ -853,7 +853,7 @@ def upgrade(topic: str, payload) -> Optional[dict]: if re.search("openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None: payload = decode_payload(payload) if "keep_charge_active_duration" not in payload: - payload["keep_charge_active_duration"] = ev.EvTemplateData().keep_charge_active_duration + payload["keep_charge_active_duration"] = EvTemplateData().keep_charge_active_duration return {topic: payload} self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 8) @@ -1957,3 +1957,14 @@ def upgrade(topic: str, payload) -> None: Pub().pub(topic, payload) self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 75) + + def upgrade_datastore_75(self) -> None: + def upgrade(topic: str, payload) -> None: + if (re.search("openWB/vehicle/template/charge_template/[0-9]+", topic) is not None or + re.search("openWB/vehicle/template/ev_template/[0-9]+", topic) is not None): + payload = decode_payload(payload) + index = get_index(topic) + payload.update({"id", index}) + Pub().pub(topic, payload) + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 76) From a77a9e2edb7e715c60f8b87d01aa90de922600ae Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 23 Oct 2024 08:32:50 +0200 Subject: [PATCH 02/13] dashboard temporary settings --- .../control/algorithm/surplus_controlled.py | 6 +-- .../algorithm/surplus_controlled_test.py | 6 +-- packages/control/chargepoint/chargepoint.py | 46 ++++++++++++------- .../control/chargepoint/chargepoint_data.py | 5 ++ packages/control/counter.py | 8 ++-- packages/control/ev/ev.py | 45 +++++++----------- packages/helpermodules/update_config.py | 1 - 7 files changed, 60 insertions(+), 57 deletions(-) diff --git a/packages/control/algorithm/surplus_controlled.py b/packages/control/algorithm/surplus_controlled.py index 51e0aaa1c7..0f3eca4a83 100644 --- a/packages/control/algorithm/surplus_controlled.py +++ b/packages/control/algorithm/surplus_controlled.py @@ -96,9 +96,9 @@ def _set_loadmangement_message(self, # tested def filter_by_feed_in_limit(self, chargepoints: List[Chargepoint]) -> Tuple[List[Chargepoint], List[Chargepoint]]: - cp_with_feed_in = list(filter(lambda cp: cp.data.set.charging_ev_data.charge_template.data.chargemode. + cp_with_feed_in = list(filter(lambda cp: cp.data.set.charge_template.data.chargemode. pv_charging.feed_in_limit is True, chargepoints)) - cp_without_feed_in = list(filter(lambda cp: cp.data.set.charging_ev_data.charge_template.data.chargemode. + cp_without_feed_in = list(filter(lambda cp: cp.data.set.charge_template.data.chargemode. pv_charging.feed_in_limit is False, chargepoints)) return cp_with_feed_in, cp_without_feed_in @@ -153,7 +153,7 @@ def check_submode_pv_charging(self) -> None: def phase_switch_necessary() -> bool: return cp.cp_ev_chargemode_support_phase_switch() and cp.data.get.phases_in_use != 1 control_parameter = cp.data.control_parameter - if cp.data.set.charging_ev_data.chargemode_changed or cp.data.set.charging_ev_data.submode_changed: + if cp.chargemode_changed or cp.submode_changed: if control_parameter.state == ChargepointState.CHARGING_ALLOWED: if (cp.data.set.charging_ev_data.ev_template.data.prevent_charge_stop is False and phase_switch_necessary() is False): diff --git a/packages/control/algorithm/surplus_controlled_test.py b/packages/control/algorithm/surplus_controlled_test.py index c36f671959..235a327a71 100644 --- a/packages/control/algorithm/surplus_controlled_test.py +++ b/packages/control/algorithm/surplus_controlled_test.py @@ -40,10 +40,8 @@ def test_filter_by_feed_in_limit(feed_in_limit_1: bool, expected_sorted: int): # setup def setup_cp(cp: Chargepoint, feed_in_limit: bool) -> Chargepoint: - ev = Ev(0) - ev.charge_template = ChargeTemplate() - ev.charge_template.data.chargemode.pv_charging.feed_in_limit = feed_in_limit - cp.data = ChargepointData(set=Set(charging_ev_data=ev)) + cp.data = ChargepointData() + cp.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit = feed_in_limit return cp cp1 = setup_cp(mock_cp1, feed_in_limit_1) diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index 2473da5738..3494d7809d 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -14,6 +14,7 @@ Tag-Liste: Tags, mit denen der Ladepunkt freigeschaltet werden kann. Ist diese leer, kann mit jedem Tag der Ladepunkt freigeschaltet werden. """ +import copy from dataclasses import asdict import dataclasses import logging @@ -70,6 +71,16 @@ def __init__(self, index: int, event: Optional[threading.Event]): self.template: CpTemplate = None self.chargepoint_module: AbstractChargepoint = None self.num = index + + +<< << << < HEAD +== == == = + # set current aus dem vorherigen Zyklus, um zu wissen, ob am Ende des Zyklus die Ladung freigegeben wird + # (für Control-Pilot-Unterbrechung) + self.set_current_prev = 0.0 + self.chargemode_changed = False + self.submode_changed = False +>>>>>> > 5fb37bee2(dashboard temporary settings) # bestehende Daten auf dem Broker nicht zurücksetzen, daher nicht veröffentlichen self.data: ChargepointData = ChargepointData() self.data.set_event(event) @@ -226,7 +237,7 @@ def _process_charge_stop(self) -> None: if not self.data.get.plug_state: self.data.control_parameter = control_parameter_factory() # Standardprofil nach Abstecken laden - if data.data.ev_data["ev"+str(self.data.set.charging_ev_prev)].charge_template.data.load_default: + if self.data.set.charge_template.data.load_default: self.data.config.ev = 0 Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/config/ev", 0) # Ladepunkt nach Abstecken sperren @@ -235,6 +246,8 @@ def _process_charge_stop(self) -> None: Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/manual_lock", True) log.debug("/set/manual_lock True") # Ev wurde noch nicht aktualisiert. + # Ladeprofil aus den Einstellungen laden. + self.data.set.charge_template = copy.deepcopy(self.data.set.charging_ev_data.charge_template) chargelog.save_and_reset_data(self, data.data.ev_data["ev"+str(self.data.set.charging_ev_prev)]) self.data.set.charging_ev_prev = -1 Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charging_ev_prev", @@ -280,8 +293,8 @@ def set_control_parameter(self, submode: str, required_current: float): self.data.control_parameter.chargemode = Chargemode.TIME_CHARGING else: self.data.control_parameter.chargemode = Chargemode( - self.data.set.charging_ev_data.charge_template.data.chargemode.selected) - self.data.control_parameter.prio = self.data.set.charging_ev_data.charge_template.data.prio + self.data.set.charge_template.data.chargemode.selected) + self.data.control_parameter.prio = self.data.set.charge_template.data.prio self.data.control_parameter.required_current = required_current if self.template.data.charging_type == ChargingType.AC.value: self.data.control_parameter.min_current = self.data.set.charging_ev_data.ev_template.data.min_current @@ -509,7 +522,7 @@ def get_phases_by_selected_chargemode(self) -> int: if self.data.control_parameter.submode == "time_charging": mode = "time_charging" else: - mode = charging_ev.charge_template.data.chargemode.selected + mode = self.data.set.charge_template.data.chargemode.selected chargemode = data.data.general_data.get_phases_chargemode(mode, self.data.control_parameter.submode) if (chargemode is None or @@ -666,8 +679,8 @@ def update(self, ev_list: Dict[str, Ev]) -> None: required_current = self.check_min_max_current( required_current, self.data.control_parameter.phases) required_current = self.chargepoint_module.add_conversion_loss_to_current(required_current) - charging_ev.set_chargemode_changed(self.data.control_parameter, submode) - charging_ev.set_submode_changed(self.data.control_parameter, submode) + self.set_chargemode_changed(self.data.control_parameter, submode) + self.set_submode_changed(self.data.control_parameter, submode) self.set_control_parameter(submode, required_current) self.set_required_currents(required_current) self.check_phase_switch_completed() @@ -679,7 +692,7 @@ def update(self, ev_list: Dict[str, Ev]) -> None: message = message_ev if message_ev else message # Ein Eintrag muss nur erstellt werden, wenn vorher schon geladen wurde und auch danach noch # geladen werden soll. - if charging_ev.chargemode_changed and self.data.set.log.imported_since_mode_switch != 0 and state: + if self.chargemode_changed and self.data.set.log.imported_since_mode_switch != 0 and state: chargelog.save_interim_data(self, charging_ev) # Wenn die Nachrichten gesendet wurden, EV wieder löschen, wenn das EV im Algorithmus nicht @@ -695,7 +708,7 @@ def update(self, ev_list: Dict[str, Ev]) -> None: str(self.num)+"/set/charging_ev", -1) log.debug(f'LP {self.num}, EV: {self.data.set.charging_ev_data.data.name}' f' (EV-Nr.{vehicle}): Lademodus ' - f'{charging_ev.charge_template.data.chargemode.selected}, Submodus: ' + f'{self.data.set.charge_template.data.chargemode.selected}, Submodus: ' f'{self.data.control_parameter.submode}') else: if (self.data.control_parameter.state == ChargepointState.SWITCH_ON_DELAY and @@ -705,10 +718,10 @@ def update(self, ev_list: Dict[str, Ev]) -> None: log.info( f"LP {self.num}, EV: {self.data.set.charging_ev_data.data.name} (EV-Nr.{vehicle}): " f"Theoretisch benötigter Strom {required_current}A, Lademodus " - f"{charging_ev.charge_template.data.chargemode.selected}, Submodus: " + f"{self.data.set.charge_template.data.chargemode.selected}, Submodus: " f"{self.data.control_parameter.submode}, Phasen: " f"{self.data.control_parameter.phases}" - f", Priorität: {charging_ev.charge_template.data.prio}" + f", Priorität: {self.data.control_parameter.prio}" f", max. Ist-Strom: {max(self.data.get.currents)}") except Exception: log.exception("Fehler im Prepare-Modul für Ladepunkt "+str(self.num)) @@ -761,6 +774,7 @@ def _get_charging_ev(self, vehicle: int, ev_list: Dict[str, Ev]) -> Ev: if self.data.set.charging_ev != vehicle and self.data.set.charging_ev_prev != vehicle: Pub().pub(f"openWB/set/vehicle/{charging_ev.num}/get/force_soc_update", True) log.debug("SoC nach EV-Wechsel") + self.data.set.charge_template = copy.deepcopy(charging_ev.charge_template) self.data.set.charging_ev_data = charging_ev self.data.set.charging_ev = vehicle Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charging_ev", vehicle) @@ -791,16 +805,16 @@ def _pub_connected_vehicle(self, vehicle: Ev): soc_obj.range = vehicle.data.get.range info_obj = ConnectedInfo(id=vehicle.num, name=vehicle.data.name) - if (vehicle.charge_template.data.chargemode.selected == "time_charging" or - vehicle.charge_template.data.chargemode.selected == "scheduled_charging"): + if (self.data.set.charge_template.data.chargemode.selected == "time_charging" or + self.data.set.charge_template.data.chargemode.selected == "scheduled_charging"): current_plan = self.data.control_parameter.current_plan else: current_plan = None config_obj = ConnectedConfig( - charge_template=vehicle.charge_template.data.id, - ev_template=vehicle.ev_template.data.id, - chargemode=vehicle.charge_template.data.chargemode.selected, - priority=vehicle.charge_template.data.prio, + charge_template=self.data.set.charge_template.data.id, + ev_template=vehicle.ev_template.et_num, + chargemode=self.data.set.charge_template.data.chargemode.selected, + priority=self.data.set.charge_template.data.prio, current_plan=current_plan, average_consumption=vehicle.ev_template.data.average_consump, time_charging_in_use=True if (self.data.control_parameter.submode == diff --git a/packages/control/chargepoint/chargepoint_data.py b/packages/control/chargepoint/chargepoint_data.py index 9d2f9bb02c..b624d056d4 100644 --- a/packages/control/chargepoint/chargepoint_data.py +++ b/packages/control/chargepoint/chargepoint_data.py @@ -117,6 +117,10 @@ class Get: voltages: List[float] = field(default_factory=voltages_list_factory) +def charge_template_factory() -> ChargeTemplate: + return ChargeTemplate(None) + + def ev_factory() -> Ev: return Ev(0) @@ -144,6 +148,7 @@ class Set: current_prev: float = 0.0 target_current: float = 0 # Soll-Strom aus fest vorgegebener Stromstärke charging_ev_data: Ev = field(default_factory=ev_factory) + charge_template: ChargeTemplate = field(default_factory=charge_template_factory) ocpp_transaction_id: Optional[int] = None diff --git a/packages/control/counter.py b/packages/control/counter.py index fb98a789ee..47dde30a72 100644 --- a/packages/control/counter.py +++ b/packages/control/counter.py @@ -265,7 +265,7 @@ def calc_switch_on_power(self, chargepoint: Chargepoint) -> Tuple[float, float]: control_parameter = chargepoint.data.control_parameter pv_config = data.data.general_data.data.chargemode_config.pv_charging - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: threshold = pv_config.feed_in_yield else: threshold = pv_config.switch_on_threshold*control_parameter.phases @@ -275,7 +275,7 @@ def switch_on_threshold_reached(self, chargepoint: Chargepoint) -> None: try: message = None control_parameter = chargepoint.data.control_parameter - feed_in_limit = chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.\ + feed_in_limit = chargepoint.data.set.charge_template.data.chargemode.pv_charging.\ feed_in_limit pv_config = data.data.general_data.data.chargemode_config.pv_charging timestamp_switch_on_off = control_parameter.timestamp_switch_on_off @@ -337,7 +337,7 @@ def switch_on_timer_expired(self, chargepoint: Chargepoint) -> None: msg = self.SWITCH_ON_EXPIRED.format(pv_config.switch_on_threshold) control_parameter.state = ChargepointState.CHARGING_ALLOWED - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: feed_in_yield = pv_config.feed_in_yield else: feed_in_yield = 0 @@ -385,7 +385,7 @@ def switch_off_check_timer(self, chargepoint: Chargepoint) -> None: def calc_switch_off_threshold(self, chargepoint: Chargepoint) -> Tuple[float, float]: pv_config = data.data.general_data.data.chargemode_config.pv_charging control_parameter = chargepoint.data.control_parameter - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: # Der EVU-Überschuss muss ggf um die Einspeisegrenze bereinigt werden. # Wnn die Leistung nicht Einspeisegrenze + Einschaltschwelle erreicht, darf die Ladung nicht pulsieren. # Abschaltschwelle um Einschaltschwelle reduzieren. diff --git a/packages/control/ev/ev.py b/packages/control/ev/ev.py index c5d48aad7d..8baf194900 100644 --- a/packages/control/ev/ev.py +++ b/packages/control/ev/ev.py @@ -11,10 +11,10 @@ from typing import List, Optional, Tuple from control import data +from control.ev.charge_template import ChargeTemplate from control.chargepoint.chargepoint_state import ChargepointState, PHASE_SWITCH_STATES from control.chargepoint.charging_type import ChargingType from control.chargepoint.control_parameter import ControlParameter -from control.ev.charge_template import ChargeTemplate from control.ev.ev_template import EvTemplate from control.limiting_value import LimitingValue from dataclass_utils.factories import empty_list_factory @@ -88,8 +88,6 @@ def __init__(self, index: int): self.ev_template: EvTemplate = EvTemplate() self.charge_template: ChargeTemplate = ChargeTemplate() self.soc_module: ConfigurableVehicle = None - self.chargemode_changed = False - self.submode_changed = False self.num = index self.data = EvData() except Exception: @@ -114,6 +112,7 @@ def soc_interval_expired(self, vehicle_update_data: VehicleUpdateData) -> bool: return request_soc def get_required_current(self, + charge_template: ChargeTemplate, control_parameter: ControlParameter, imported: float, max_phases_hw: int, @@ -142,11 +141,11 @@ def get_required_current(self, message = None state = True try: - if self.charge_template.data.chargemode.selected == "scheduled_charging": + if charge_template.data.chargemode.selected == "scheduled_charging": if control_parameter.imported_at_plan_start is None: control_parameter.imported_at_plan_start = imported used_amount = imported - control_parameter.imported_at_plan_start - plan_data = self.charge_template.scheduled_charging_recent_plan( + plan_data = charge_template.scheduled_charging_recent_plan( self.data.get.soc, self.ev_template, control_parameter.phases, @@ -170,7 +169,7 @@ def get_required_current(self, control_parameter.current_plan = plan_data.id else: control_parameter.current_plan = None - required_current, submode, message, phases = self.charge_template.scheduled_charging_calc_current( + required_current, submode, message, phases = charge_template.scheduled_charging_calc_current( plan_data, self.data.get.soc, used_amount, @@ -180,7 +179,7 @@ def get_required_current(self, # Wenn Zielladen auf Überschuss wartet, prüfen, ob Zeitladen aktiv ist. if (submode != "instant_charging" and - self.charge_template.data.time_charging.active): + charge_template.data.time_charging.active): if control_parameter.imported_at_plan_start is None: control_parameter.imported_at_plan_start = imported used_amount = imported - control_parameter.imported_at_plan_start @@ -199,28 +198,28 @@ def get_required_current(self, required_current = tmp_current submode = tmp_submode if (required_current == 0) or (required_current is None): - if self.charge_template.data.chargemode.selected == "instant_charging": + if charge_template.data.chargemode.selected == "instant_charging": # Wenn der Submode auf stop gestellt wird, wird auch die Energiemenge seit Wechsel des Modus # zurückgesetzt, dann darf nicht die Energiemenge erneute geladen werden. if control_parameter.imported_instant_charging is None: control_parameter.imported_instant_charging = imported used_amount = imported - control_parameter.imported_instant_charging - required_current, submode, message = self.charge_template.instant_charging( + required_current, submode, message = charge_template.instant_charging( self.data.get.soc, used_amount, charging_type) - elif self.charge_template.data.chargemode.selected == "pv_charging": - required_current, submode, message = self.charge_template.pv_charging( + elif charge_template.data.chargemode.selected == "pv_charging": + required_current, submode, message = charge_template.pv_charging( self.data.get.soc, control_parameter.min_current, charging_type) - elif self.charge_template.data.chargemode.selected == "standby": + elif charge_template.data.chargemode.selected == "standby": # Text von Zeit-und Zielladen nicht überschreiben. if message is None: - required_current, submode, message = self.charge_template.standby() + required_current, submode, message = charge_template.standby() else: - required_current, submode, _ = self.charge_template.standby() - elif self.charge_template.data.chargemode.selected == "stop": - required_current, submode, message = self.charge_template.stop() - if submode == "stop" or submode == "standby" or (self.charge_template.data.chargemode.selected == "stop"): + required_current, submode, _ = charge_template.standby() + elif charge_template.data.chargemode.selected == "stop": + required_current, submode, message = charge_template.stop() + if submode == "stop" or submode == "standby" or (charge_template.data.chargemode.selected == "stop"): state = False if phases is None: phases = control_parameter.phases @@ -230,18 +229,6 @@ def get_required_current(self, return (False, f"Kein Ladevorgang, da ein Fehler aufgetreten ist: {' '.join(e.args)}", "stop", 0, control_parameter.phases) - def set_chargemode_changed(self, control_parameter: ControlParameter, submode: str) -> None: - if ((submode == "time_charging" and control_parameter.chargemode != "time_charging") or - (submode != "time_charging" and - control_parameter.chargemode != self.charge_template.data.chargemode.selected)): - self.chargemode_changed = True - log.debug("Änderung des Lademodus") - else: - self.chargemode_changed = False - - def set_submode_changed(self, control_parameter: ControlParameter, submode: str) -> None: - self.submode_changed = (submode != control_parameter.submode) - def check_min_max_current(self, control_parameter: ControlParameter, required_current: float, diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 9447533652..1d02d87a69 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -8,7 +8,6 @@ import time from typing import List, Optional from paho.mqtt.client import Client as MqttClient, MQTTMessage - import dataclass_utils from control.chargepoint.chargepoint_template import get_chargepoint_template_default From 78821ed87703bf7904c1af8654ac054d840a5809 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Mon, 28 Oct 2024 16:01:27 +0100 Subject: [PATCH 03/13] topics --- .../control/chargepoint/chargepoint_data.py | 1 + packages/helpermodules/setdata.py | 23 +++++-- packages/helpermodules/subdata.py | 69 ++++++++++--------- .../web_themes/standard_legacy/web/index.html | 40 +++++------ .../standard_legacy/web/setupMqttServices.js | 7 +- 5 files changed, 81 insertions(+), 59 deletions(-) diff --git a/packages/control/chargepoint/chargepoint_data.py b/packages/control/chargepoint/chargepoint_data.py index b624d056d4..b665f122aa 100644 --- a/packages/control/chargepoint/chargepoint_data.py +++ b/packages/control/chargepoint/chargepoint_data.py @@ -4,6 +4,7 @@ from control.chargepoint.chargepoint_template import CpTemplate from control.chargepoint.control_parameter import ControlParameter, control_parameter_factory +from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev from dataclass_utils.factories import currents_list_factory, empty_dict_factory, voltages_list_factory from helpermodules.constants import NO_ERROR diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index dec04bf379..051d7b2085 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -73,11 +73,17 @@ def on_message(self, client: mqtt.Client, userdata, msg: mqtt.MQTTMessage): if "openWB/set/vehicle/" in msg.topic: if "openWB/set/vehicle/template/ev_template/" in msg.topic: self.event_ev_template.wait(5) + self.process_vehicle_ev_template_topic(msg) elif "openWB/set/vehicle/template/charge_template/" in msg.topic: self.event_charge_template.wait(5) - self.process_vehicle_topic(msg) + self.process_vehicle_charge_template_topic(msg) + else: + self.process_vehicle_topic(msg) elif "openWB/set/chargepoint/" in msg.topic: - self.process_chargepoint_topic(msg) + if "openWB/set/chargepoint/set/charge_template" in msg.topic: + self.process_vehicle_charge_template_topic(msg) + else: + self.process_vehicle_topic(msg) elif "openWB/set/pv/" in msg.topic: self.process_pv_topic(msg) elif "openWB/set/bat/" in msg.topic: @@ -402,8 +408,6 @@ def process_vehicle_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, str) elif "/info" in msg.topic: self._validate_value(msg, "json") - elif "openWB/set/vehicle/template" in msg.topic: - self._subprocess_vehicle_chargemode_topic(msg) elif "openWB/set/vehicle/set/vehicle_update_completed" in msg.topic: self._validate_value(msg, bool) elif "/set/soc_error_counter" in msg.topic: @@ -437,7 +441,7 @@ def process_vehicle_topic(self, msg: mqtt.MQTTMessage): except Exception: log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") - def _subprocess_vehicle_chargemode_topic(self, msg: mqtt.MQTTMessage): + def process_vehicle_charge_template_topic(self, msg: mqtt.MQTTMessage): """ Handler für die Lade-Profil-Topics Parameters ---------- @@ -503,6 +507,15 @@ def _subprocess_vehicle_chargemode_topic(self, msg: mqtt.MQTTMessage): except Exception: log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") + def process_vehicle_ev_template_topic(self, msg: mqtt.MQTTMessage): + try: + if "ev_template" in msg.topic: + self._validate_value(msg, "json") + else: + self.__unknown_topic(msg) + except Exception: + log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") + def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): """ Handler für die Ladepunkt-Topics diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index ecc099e140..8e6132c7ac 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -322,44 +322,51 @@ def process_vehicle_charge_template_topic(self, var: Dict[str, ChargeTemplate], var.pop("ct"+index) else: if "ct"+index not in var: - var["ct"+index] = ChargeTemplate() - if re.search("/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/plans/[0-9]+$", - msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var["ct"+index].data.chargemode.scheduled_charging.plans.pop(index_second) - except KeyError: - log.error("Es konnte kein Zielladen-Plan mit der ID " + - str(index_second)+" in dem Lade-Profil "+str(index)+" gefunden werden.") - else: - var["ct"+index].data.chargemode.scheduled_charging.plans[ - index_second] = dataclass_from_dict(ScheduledChargingPlan, decode_payload(msg.payload)) + var["ct"+index] = ev.ChargeTemplate() + self.process_charge_template_topic(var["ct"+index]) + if re.search("/chargemode/scheduled_charging/plans/[0-9]+$", msg.topic) is not None: self.event_scheduled_charging_plan.set() - elif re.search("/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", - msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var["ct"+index].data.time_charging.plans.pop(index_second) - except KeyError: - log.error("Es konnte kein Zeitladen-Plan mit der ID " + - str(index_second)+" in dem Lade-Profil "+str(index)+" gefunden werden.") - else: - var["ct"+index].data.time_charging.plans[ - index_second] = dataclass_from_dict(TimeChargingPlan, decode_payload(msg.payload)) + elif re.search("/time_charging/plans/[0-9]+$", msg.topic) is not None: self.event_time_charging_plan.set() else: - # Pläne unverändert übernehmen - scheduled_charging_plans = var["ct" + index].data.chargemode.scheduled_charging.plans - time_charging_plans = var["ct" + index].data.time_charging.plans - var["ct" + index].data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) - var["ct"+index].data.time_charging.plans = time_charging_plans - var["ct"+index].data.chargemode.scheduled_charging.plans = scheduled_charging_plans self.event_charge_template.set() except Exception: log.exception("Fehler im subdata-Modul") + def process_charge_template_topic(self, var: ChargeTemplate, msg: mqtt.MQTTMessage): + try: + if re.search("/chargemode/scheduled_charging/plans/[0-9]+$", msg.topic) is not None: + index_second = get_second_index(msg.topic) + if decode_payload(msg.payload) == "": + try: + var.data.chargemode.scheduled_charging.plans.pop(index_second) + except KeyError: + log.error(f"Es konnte kein Zielladen-Plan mit der ID {index_second} " + "in dem Lade-Profil gefunden werden.") + else: + var.data.chargemode.scheduled_charging.plans[ + index_second] = dataclass_from_dict(ScheduledChargingPlan, decode_payload(msg.payload)) + elif re.search("/time_charging/plans/[0-9]+$", msg.topic) is not None: + index_second = get_second_index(msg.topic) + if decode_payload(msg.payload) == "": + try: + var.data.time_charging.plans.pop(index_second) + except KeyError: + log.error("Es konnte kein Zeitladen-Plan mit der ID " + + str(index_second)+" in dem Lade-Profil gefunden werden.") + else: + var.data.time_charging.plans[ + index_second] = dataclass_from_dict(TimeChargingPlan, decode_payload(msg.payload)) + else: + # Pläne unverändert übernehmen + scheduled_charging_plans = var.data.chargemode.scheduled_charging.plans + time_charging_plans = var.data.time_charging.plans + var.data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) + var.data.time_charging.plans = time_charging_plans + var.data.chargemode.scheduled_charging.plans = scheduled_charging_plans + except Exception: + log.exception("Fehler im subdata-Modul") + def process_vehicle_ev_template_topic(self, var: Dict[str, EvTemplate], msg: mqtt.MQTTMessage): """ Handler für die EV-Topics diff --git a/packages/modules/web_themes/standard_legacy/web/index.html b/packages/modules/web_themes/standard_legacy/web/index.html index 2b2fe2e0d1..686f319b5b 100644 --- a/packages/modules/web_themes/standard_legacy/web/index.html +++ b/packages/modules/web_themes/standard_legacy/web/index.html @@ -496,7 +496,7 @@
+ data-topic="openWB/set/chargepoint//set/charge_template/chargemode/selected"> @@ -521,7 +521,7 @@
@@ -539,7 +539,7 @@
@@ -566,7 +566,7 @@
@@ -609,7 +609,7 @@

Termine Zeitladen

@@ -627,7 +627,7 @@

Einstellungen für "Sofortladen"