diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 0b0e31d959..35c616eb1a 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -1016,6 +1016,8 @@ def process_system_topic(self, msg: mqtt.MQTTMessage): self.__unknown_topic(msg) elif "/config" in msg.topic: self._validate_value(msg, "json") + elif "/error_timestamp" in msg.topic: + self._validate_value(msg, float, [(0, float("inf"))]) elif "/get/fault_state" in msg.topic: self._validate_value(msg, int, [(0, 2)]) elif "/get/fault_str" in msg.topic: diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index 2a7d8d7a72..95e5c1a157 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -863,12 +863,16 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes var["device"+index] = (dev.Device if hasattr(dev, "Device") else dev.create_device)(config) # Durch das erneute Subscribe werden die Komponenten mit dem aktualisierten TCP-Client angelegt. client.subscribe(f"openWB/system/device/{index}/component/+/config", 2) + client.subscribe(f"openWB/system/device/{index}/error_timestamp", 2) elif re.search("^.+/device/[0-9]+/component/[0-9]+/simulation$", msg.topic) is not None: index = get_index(msg.topic) index_second = get_second_index(msg.topic) var["device"+index].components["component"+index_second].sim_counter.data = dataclass_from_dict( SimCounterState, decode_payload(msg.payload)) + elif re.search("^.+/device/[0-9]+/error_timestamp$", msg.topic) is not None: + index = get_index(msg.topic) + var["device"+index].client_error_context.error_timestamp = decode_payload(msg.payload) elif re.search("^.+/device/[0-9]+/component/[0-9]+/config$", msg.topic) is not None: index = get_index(msg.topic) index_second = get_second_index(msg.topic) diff --git a/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py b/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py index 20734da31f..a596633ce1 100644 --- a/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py +++ b/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py @@ -1,10 +1,12 @@ #!/usr/bin/python3 import logging +from pathlib import Path import time from typing import Optional from control import data from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext +from helpermodules.utils.run_command import run_command from modules.chargepoints.openwb_series2_satellit.config import OpenWBseries2Satellit from modules.common import modbus from modules.common.abstract_chargepoint import AbstractChargepoint @@ -74,34 +76,36 @@ def _validate_version(self): def get_values(self) -> None: with SingleComponentUpdateContext(self.fault_state): if self.version is not None: - with self.client_error_context: - try: - self.delay_second_cp(self.CP1_DELAY) - with self._client.client: - self._client.check_hardware(self.fault_state) - if self.version is False: - self._validate_version() - currents = self._client.meter_client.get_currents() - phases_in_use = sum(1 for current in currents if current > 3) - plug_state, charge_state, _ = self._client.evse_client.get_plug_charge_state() - - chargepoint_state = ChargepointState( - power=self._client.meter_client.get_power()[1], - currents=currents, - imported=self._client.meter_client.get_imported(), - exported=0, - voltages=self._client.meter_client.get_voltages(), - plug_state=plug_state, - charge_state=charge_state, - phases_in_use=phases_in_use, - serial_number=self._client.meter_client.get_serial_number(), - max_evse_current=self.max_evse_current - ) + try: + self.delay_second_cp(self.CP1_DELAY) + with self._client.client, self.client_error_context: + self._client.check_hardware(self.fault_state) + if self.version is False: + self._validate_version() + currents = self._client.meter_client.get_currents() + phases_in_use = sum(1 for current in currents if current > 3) + plug_state, charge_state, _ = self._client.evse_client.get_plug_charge_state() + chargepoint_state = ChargepointState( + power=self._client.meter_client.get_power()[1], + currents=currents, + imported=self._client.meter_client.get_imported(), + exported=0, + voltages=self._client.meter_client.get_voltages(), + plug_state=plug_state, + charge_state=charge_state, + phases_in_use=phases_in_use, + serial_number=self._client.meter_client.get_serial_number(), + max_evse_current=self.max_evse_current + ) self.store.set(chargepoint_state) self.client_error_context.reset_error_counter() - except AttributeError: - self._create_client() - self._validate_version() + except Exception: + if self.client_error_context.error_counter_exceeded(): + run_command(f"{Path(__file__).resolve().parents[3]}/modules/chargepoints/" + "openwb_series2_satellit/restart_protoss_satellite") + except AttributeError: + self._create_client() + self._validate_version() else: self._create_client() self._validate_version() diff --git a/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite b/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite new file mode 100755 index 0000000000..99dbb6250c Binary files /dev/null and b/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite differ diff --git a/packages/modules/common/component_context.py b/packages/modules/common/component_context.py index cad05f8d63..2201407db9 100644 --- a/packages/modules/common/component_context.py +++ b/packages/modules/common/component_context.py @@ -1,6 +1,6 @@ import logging from threading import local -from typing import Callable, Optional, List, Union, Any, Dict +from typing import Optional, List, Union, Any, Dict from helpermodules.constants import NO_ERROR from modules.common.fault_state import ComponentInfo, FaultState, FaultStateLevel @@ -19,13 +19,11 @@ class SingleComponentUpdateContext: def __init__(self, fault_state: FaultState, - error_handler: Callable = None, update_always: bool = True, reraise: bool = False): self.__fault_state = fault_state self.update_always = update_always self.reraise = reraise - self.error_handler = error_handler def __enter__(self): log.debug("Update Komponente ['"+self.__fault_state.component_info.name+"']") @@ -35,9 +33,7 @@ def __enter__(self): def __exit__(self, exception_type, exception, exception_traceback) -> bool: MultiComponentUpdateContext.override_subcomponent_state(self.__fault_state, exception, self.update_always) - if isinstance(exception, Exception) and self.error_handler is not None: - self.error_handler() - if self.reraise is False: + if self.reraise is False or exception is None: return True else: return False diff --git a/packages/modules/common/configurable_device.py b/packages/modules/common/configurable_device.py index 6db817accd..ca9f7081c6 100644 --- a/packages/modules/common/configurable_device.py +++ b/packages/modules/common/configurable_device.py @@ -3,6 +3,8 @@ from typing import TypeVar, Generic, Dict, Any, Callable, Iterable, List from dataclass_utils import dataclass_from_dict +from helpermodules import timecheck +from helpermodules.pub import Pub from modules.common.abstract_device import AbstractDevice from modules.common.component_context import SingleComponentUpdateContext, MultiComponentUpdateContext from modules.common.fault_state import ComponentInfo, FaultState @@ -22,9 +24,16 @@ def __init__(self, updater: Callable[[T_COMPONENT], None]): self.__updater = updater def __call__(self, components: Iterable[T_COMPONENT], error_handler: Callable) -> None: + # error_handler nur einmal ausführen, da er für das ganze Gerät gilt + run_error_handler = False for component in components: - with SingleComponentUpdateContext(component.fault_state, error_handler): - self.__updater(component) + try: + with SingleComponentUpdateContext(component.fault_state, reraise=True): + self.__updater(component) + except Exception: + run_error_handler = True + if run_error_handler: + error_handler() class MultiComponentUpdater: @@ -66,27 +75,32 @@ def __init__(self, device_config: T_DEVICE_CONFIG, component_factory: ComponentFactory[Any, T_COMPONENT], component_updater: ComponentUpdater[T_COMPONENT], - initializer: Callable = lambda: None) -> None: + initializer: Callable = lambda: None, + error_handler: Callable = lambda: None) -> None: self.__initializer = initializer + self.__error_handler = error_handler self.__component_factory = component_factory self.__component_updater = component_updater self.device_config = device_config self.components: Dict[str, T_COMPONENT] = {} - + self.error_timestamp = None try: self.__initializer() except Exception: log.exception(f"Initialisierung von Gerät {self.device_config.name} fehlgeschlagen") - def error_handler(self): - pass - # self.__initializer() - # for component in self.components.values(): - # component.initialize() + def error_handler(self) -> None: + if self.error_timestamp is None: + self.error_timestamp = timecheck.create_timestamp() + Pub().pub(f"openWB/set/system/device/{self.device_config.id}/error_timestamp", self.error_timestamp) + log.debug( + f"Fehler bei Gerät {self.device_config.name} aufgetreten, Fehlerzeitstempel: {self.error_timestamp}") + if timecheck.check_timestamp(self.error_timestamp, 60) is False: + self.__error_handler() + self.error_timestamp = None + Pub().pub(self.topic, self.error_timestamp) def add_component(self, component_config: T_COMPONENT_CONFIG) -> None: - # with SingleComponentUpdateContext(FaultState(ComponentInfo.from_component_config(component_config)), - # self.__initializer): with SingleComponentUpdateContext(FaultState(ComponentInfo.from_component_config(component_config))): component = self.__component_factory(component_config) component.initialized = False diff --git a/packages/modules/common/restart_protoss_admin b/packages/modules/common/restart_protoss_admin new file mode 100755 index 0000000000..5acf807caa Binary files /dev/null and b/packages/modules/common/restart_protoss_admin differ diff --git a/packages/modules/devices/alpha_ess/alpha_ess/device.py b/packages/modules/devices/alpha_ess/alpha_ess/device.py index e55bc3acaf..de391e7f1f 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/device.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.alpha_ess.alpha_ess.config import ( AlphaEss, AlphaEssBatSetup, AlphaEssCounterSetup, AlphaEssInverterSetup) @@ -56,9 +58,13 @@ def initializer(): client = modbus.ModbusTcpClient_( device_config.configuration.ip_address, device_config.configuration.port) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/growatt/growatt/device.py b/packages/modules/devices/growatt/growatt/device.py index 783223047f..d72a054344 100644 --- a/packages/modules/devices/growatt/growatt/device.py +++ b/packages/modules/devices/growatt/growatt/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.common.modbus import ModbusTcpClient_ @@ -48,9 +50,13 @@ def initializer(): nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/huawei/huawei/device.py b/packages/modules/devices/huawei/huawei/device.py index d077710648..c857ded96c 100644 --- a/packages/modules/devices/huawei/huawei/device.py +++ b/packages/modules/devices/huawei/huawei/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common.modbus import ModbusTcpClient_ @@ -58,9 +60,13 @@ def initializer(): client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/openwb/openwb_bat_kit/device.py b/packages/modules/devices/openwb/openwb_bat_kit/device.py index f3e5e5c8f3..8bd46922b3 100644 --- a/packages/modules/devices/openwb/openwb_bat_kit/device.py +++ b/packages/modules/devices/openwb/openwb_bat_kit/device.py @@ -1,6 +1,8 @@ import logging +from pathlib import Path from typing import Iterable +from helpermodules.utils.run_command import run_command from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater @@ -27,9 +29,13 @@ def initializer(): nonlocal client client = modbus.ModbusTcpClient_("192.168.193.19", 8899) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, ), diff --git a/packages/modules/devices/openwb/openwb_evu_kit/device.py b/packages/modules/devices/openwb/openwb_evu_kit/device.py index f3aae3d429..877a9fa6c9 100644 --- a/packages/modules/devices/openwb/openwb_evu_kit/device.py +++ b/packages/modules/devices/openwb/openwb_evu_kit/device.py @@ -1,7 +1,9 @@ import logging +from pathlib import Path import time from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater @@ -40,9 +42,13 @@ def initializer(): nonlocal client client = modbus.ModbusTcpClient_("192.168.193.15", 8899) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/openwb/openwb_flex/device.py b/packages/modules/devices/openwb/openwb_flex/device.py index 2733df5c77..699d90a86a 100644 --- a/packages/modules/devices/openwb/openwb_flex/device.py +++ b/packages/modules/devices/openwb/openwb_flex/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.common.modbus import ModbusTcpClient_ @@ -46,9 +48,13 @@ def initializer(): nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, consumption_counter=create_consumption_counter_component, diff --git a/packages/modules/devices/openwb/openwb_pv_kit/device.py b/packages/modules/devices/openwb/openwb_pv_kit/device.py index c410efd6b9..177815fa39 100644 --- a/packages/modules/devices/openwb/openwb_pv_kit/device.py +++ b/packages/modules/devices/openwb/openwb_pv_kit/device.py @@ -1,6 +1,8 @@ import logging +from pathlib import Path from typing import Iterable +from helpermodules.utils.run_command import run_command from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater @@ -27,9 +29,13 @@ def initializer(): nonlocal client client = modbus.ModbusTcpClient_("192.168.193.13", 8899) + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( inverter=create_inverter_component, ),