diff --git a/packages/helpermodules/utils/_exit_after.py b/packages/helpermodules/utils/_exit_after.py index 717915b0ca..ece94ffc40 100644 --- a/packages/helpermodules/utils/_exit_after.py +++ b/packages/helpermodules/utils/_exit_after.py @@ -1,26 +1,33 @@ -import _thread as thread from threading import Timer -import sys +import threading -def quit_function(fn_name): - sys.stderr.flush() # Python 3 stderr is likely buffered. - thread.interrupt_main() # raises KeyboardInterrupt +class TimeoutException(Exception): + pass def exit_after(s): - ''' https://stackoverflow.com/questions/492519/timeout-on-a-function-call - use as decorator to exit process if - function takes longer than s seconds + ''' + Nutze als Decorator, um einen Timeout für eine Funktion zu erzwingen. + Wirft TimeoutException, wenn die Funktion länger als s Sekunden benötigt. + Basiert auf https://stackoverflow.com/questions/492519/timeout-on-a-function-call. ''' def outer(fn): def inner(*args, **kwargs): - timer = Timer(s, quit_function, args=[fn.__name__]) + timer = Timer( + s, + lambda: thread_raise(TimeoutException(f"Timeout nach {s} Sekunden in {fn.__name__}"))) timer.start() try: - result = fn(*args, **kwargs) + return fn(*args, **kwargs) finally: timer.cancel() - return result return inner return outer + + +def thread_raise(ex): + # Raise Exception im aktuellen Thread (nur für Hauptthread zuverlässig) + import ctypes + tid = threading.get_ident() + ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(ex)) diff --git a/packages/main.py b/packages/main.py index 34538fd566..365b1f791a 100755 --- a/packages/main.py +++ b/packages/main.py @@ -3,10 +3,11 @@ """ # flake8: noqa: E402 import logging -from helpermodules import logger -from helpermodules.utils import run_command, thread_handler import threading import sys +from helpermodules import logger +from helpermodules.utils import run_command, thread_handler +from helpermodules.utils._exit_after import TimeoutException # als erstes logging initialisieren, damit auch ImportError geloggt werden logger.setup_logging() @@ -45,9 +46,9 @@ def __init__(self): def handler10Sec(self): """ führt den Algorithmus durch. """ - try: - @exit_after(data.data.general_data.data.control_interval) - def handler_with_control_interval(): + @exit_after(data.data.general_data.data.control_interval) + def handler_with_control_interval(): + try: if (data.data.general_data.data.control_interval / 10) == self.interval_counter: data.data.copy_data() loadvars_.get_values() @@ -69,6 +70,10 @@ def handler_with_control_interval(): self.interval_counter = 1 else: self.interval_counter = self.interval_counter + 1 + except Exception: + log.exception("Fehler im Main-Modul 10s-Handler") + + try: log.info("# ***Start*** ") log.debug(run_command.run_shell_command("top -b -n 1 | head -n 20")) log.debug(f'Drosselung: {run_command.run_shell_command("if which vcgencmd >/dev/null; then vcgencmd get_throttled; else echo not found; fi")}') @@ -86,7 +91,7 @@ def handler_with_control_interval(): logging.debug(line.strip()) Pub().pub("openWB/set/system/time", timecheck.create_timestamp()) handler_with_control_interval() - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") @@ -104,7 +109,7 @@ def handler5MinAlgorithm(self): data.data.general_data.grid_protection() data.data.optional_data.ocpp_transfer_meter_values() data.data.counter_all_data.validate_hierarchy() - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") @@ -139,7 +144,7 @@ def handler5Min(self): general_internal_chargepoint_handler.internal_chargepoint_handler.heartbeat = False with ChangedValuesContext(loadvars_.event_module_update_completed): sub.system_data["system"].update_ip_address() - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") @@ -151,7 +156,7 @@ def handler_midnight(self): thread_errors_path = Path(Path(__file__).resolve().parents[1]/"ramdisk"/"thread_errors.log") with thread_errors_path.open("w") as f: f.write("") - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") @@ -160,7 +165,7 @@ def handler_midnight(self): def handler_random_nightly(self): try: data.data.system_data["system"].thread_backup_and_send_to_cloud() - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") @@ -172,7 +177,7 @@ def handler_hour(self): for cp in data.data.cp_data.values(): calculate_charge_cost(cp) data.data.optional_data.et_get_prices() - except KeyboardInterrupt: + except TimeoutException: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul")