From 17482d44e181c8dcad2cb7359ad88a55b9881548 Mon Sep 17 00:00:00 2001 From: 0O0o0oOoO00 <11174151+0O0o0oOoO00@users.noreply.github.com> Date: Sat, 13 Sep 2025 14:41:52 +0800 Subject: [PATCH] add: add instance watcher support --- module/instance_watcher.py | 86 +++++++++++++++++++++++++++ module/webui/app.py | 116 +++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 module/instance_watcher.py diff --git a/module/instance_watcher.py b/module/instance_watcher.py new file mode 100644 index 000000000..6647b7f44 --- /dev/null +++ b/module/instance_watcher.py @@ -0,0 +1,86 @@ +import threading +import time +from dataclasses import dataclass +from typing import Dict + +from module.config.config import AzurLaneConfig +from module.counter import MaxCounter +from module.notify import handle_notify +from module.webui.process_manager import ProcessManager + + +@dataclass +class InstanceSetting: + counter: MaxCounter + enable_notify: bool + notify_config: str + + +class InstanceWatcher: + instance = None + + def __init__(self): + self.watcher: threading.Thread = None + self.instances: Dict[str, InstanceSetting] = dict() + + def start(self): + if self.watcher is not None: + if self.watcher.is_alive(): + return + self.watcher = threading.Thread(target=self.watcher_thread, daemon=True) + self.watcher.start() + + def check_instances(self): + ins_has_triggered = [] + for name, setting in self.instances.items(): + ins = ProcessManager.get_manager(name) + if not ins.alive: + if setting.counter.count_once(throw=False): + ins.start("alas") + if setting.enable_notify: + handle_notify( + setting.notify_config, + title=f"Alas <{ins.config_name}> instance auto restarted", + content=f"Critical error occurred, instance restarted", + ) + else: + ins_has_triggered.append(name) + handle_notify( + setting.notify_config, + title=f"Alas <{ins.config_name}> instance restarted too many times", + content=f"Too many critical error occurred, instance restarted too many times", + ) + + for i in ins_has_triggered: + self.remove_instance(i) + + def try_add_instance(self, name): + config = AzurLaneConfig(name) + full_config = config.full_config + if full_config.Restart_InstanceRestart_Enable: + setting = InstanceSetting( + counter=MaxCounter(full_config.Restart_InstanceRestart_MaxRetryTimes), + enable_notify=full_config.Restart_InstanceRestart_Notify, + notify_config=full_config.Alas_Error_OnePushConfig + ) + self.instances[name] = setting + + def remove_instance(self, name): + try: + self.instances.pop(name) + except Exception: + ... + + def watcher_thread(self): + while 1: + time.sleep(60) + try: + self.check_instances() + except Exception: + ... + + @staticmethod + def get_instance() -> "InstanceWatcher": + if InstanceWatcher.instance is None: + InstanceWatcher.instance = InstanceWatcher() + return InstanceWatcher.instance diff --git a/module/webui/app.py b/module/webui/app.py index fb3464643..0f30af394 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -1525,3 +1525,119 @@ def app(): ) return app + + +from module.instance_watcher import InstanceWatcher + + +class AlasGUI(AlasGUI): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.instance_watcher = InstanceWatcher.get_instance() + + def on_start_on(self): + self.instance_watcher.remove_instance(self.alas.config_name) + self.alas.stop() + + def on_start_off(self): + self.alas.start(None, updater.event) + self.instance_watcher.try_add_instance(self.alas.config_name) + + @use_scope("content", clear=True) + def alas_overview(self) -> None: + self.init_menu(name="Overview") + self.set_title(t(f"Gui.MenuAlas.Overview")) + + put_scope("overview", [put_scope("schedulers"), put_scope("logs")]) + + with use_scope("schedulers"): + put_scope( + "scheduler-bar", + [ + put_text(t("Gui.Overview.Scheduler")).style( + "font-size: 1.25rem; margin: auto .5rem auto;" + ), + put_scope("scheduler_btn"), + ], + ) + put_scope( + "running", + [ + put_text(t("Gui.Overview.Running")), + put_html('