diff --git a/src-python/config.py b/src-python/config.py index e5ac086e..2aa8ac28 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -109,6 +109,14 @@ class Config: def MAX_SPEAKER_THRESHOLD(self): return self._MAX_SPEAKER_THRESHOLD + @property + def WATCHDOG_TIMEOUT(self): + return self._WATCHDOG_TIMEOUT + + @property + def WATCHDOG_INTERVAL(self): + return self._WATCHDOG_INTERVAL + # Read Write @property def ENABLE_SPEAKER2CHATBOX(self): @@ -993,6 +1001,8 @@ class Config: self._MAX_MIC_THRESHOLD = 2000 self._MAX_SPEAKER_THRESHOLD = 4000 + self._WATCHDOG_TIMEOUT = 60 + self._WATCHDOG_INTERVAL = 20 # Read Write self._ENABLE_TRANSLATION = False diff --git a/src-python/model.py b/src-python/model.py index d2ed7d70..fa22b622 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -31,6 +31,7 @@ from models.translation.translation_utils import checkCTranslate2Weight, downloa from models.transcription.transcription_whisper import checkWhisperWeight, downloadWhisperWeight from models.overlay.overlay import Overlay from models.overlay.overlay_image import OverlayImage +from models.watchdog.watchdog import Watchdog class threadFnc(Thread): def __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs): @@ -102,6 +103,7 @@ class Model: self.mic_mute_status = None self.mic_mute_status_check = None self.kks = kakasi() + self.watchdog = Watchdog(config.WATCHDOG_TIMEOUT, config.WATCHDOG_INTERVAL) def checkCTranslatorCTranslate2ModelWeight(self): return checkCTranslate2Weight(config.PATH_LOCAL, config.CTRANSLATE2_WEIGHT_TYPE) @@ -723,4 +725,21 @@ class Model: def shutdownOverlay(self): self.overlay.shutdownOverlay() + def startWatchdog(self): + self.th_watchdog = threadFnc(self.watchdog.start) + self.th_watchdog.daemon = True + self.th_watchdog.start() + + def feedWatchdog(self): + self.watchdog.feed() + + def setWatchdogCallback(self, callback): + self.watchdog.setCallback(callback) + + def stopWatchdog(self): + if isinstance(self.th_watchdog, threadFnc): + self.th_watchdog.stop() + self.th_watchdog.join() + self.th_watchdog = None + model = Model() \ No newline at end of file diff --git a/src-python/models/watchdog/watchdog.py b/src-python/models/watchdog/watchdog.py new file mode 100644 index 00000000..089e8680 --- /dev/null +++ b/src-python/models/watchdog/watchdog.py @@ -0,0 +1,22 @@ +from typing import Callable +import time +from utils import printLog + +class Watchdog: + def __init__(self, timeout:int=60, interval:int=20): + self.timeout = timeout + self.interval = interval + self.last_feed_time = time.time() + + def feed(self): + self.last_feed_time = time.time() + + def setCallback(self, callback): + self.callback = callback + + def start(self): + if time.time() - self.last_feed_time > self.timeout: + printLog("Watchdog timeout! Shutting down...") + if isinstance(self.callback, Callable): + self.callback() + time.sleep(self.interval) \ No newline at end of file diff --git a/src-python/webui_controller.py b/src-python/webui_controller.py index 387d34c8..6632e3a0 100644 --- a/src-python/webui_controller.py +++ b/src-python/webui_controller.py @@ -1535,6 +1535,25 @@ class Controller: th_download.daemon = True th_download.start() + @staticmethod + def startWatchdog(*args, **kwargs) -> dict: + model.startWatchdog() + return {"status":200, "result":True} + + @staticmethod + def feedWatchdog(*args, **kwargs) -> dict: + model.feedWatchdog() + return {"status":200, "result":True} + + @staticmethod + def setWatchdogCallback(callback) -> dict: + model.setWatchdogCallback(callback) + + @staticmethod + def stopWatchdog(*args, **kwargs) -> dict: + model.stopWatchdog() + return {"status":200, "result":True} + def init(self, *args, **kwargs) -> None: printLog("Start Initialization") @@ -1598,4 +1617,6 @@ class Controller: if config.AUTO_SPEAKER_SELECT is True: self.setEnableAutoSpeakerSelect() - printLog("End Initialization") \ No newline at end of file + printLog("End Initialization") + + self.startWatchdog() \ No newline at end of file diff --git a/src-python/webui_mainloop.py b/src-python/webui_mainloop.py index e61a3c17..7af0cd89 100644 --- a/src-python/webui_mainloop.py +++ b/src-python/webui_mainloop.py @@ -305,11 +305,16 @@ mapping = { "/set/data/osc_port": {"status": True, "variable":controller.setOscPort}, "/run/open_filepath_config_file": {"status": True, "variable":controller.openFilepathConfigFile}, + + # "/run/start_watchdog": {"status": True, "variable":controller.startWatchdog}, + "/run/feed_watchdog": {"status": True, "variable":controller.feedWatchdog}, + # "/run/stop_watchdog": {"status": True, "variable":controller.stopWatchdog}, } class Main: def __init__(self) -> None: self.queue = Queue() + self.main_loop = True def receiver(self) -> None: while True: @@ -328,7 +333,7 @@ class Main: th_receiver.daemon = True th_receiver.start() - def handleRequest(self, endpoint, data=None): + def handleRequest(self, endpoint, data=None) -> tuple: handler = mapping.get(endpoint) if handler is None: response = "Invalid endpoint" @@ -371,15 +376,19 @@ class Main: th_handler.daemon = True th_handler.start() - def loop(self) -> None: - while True: + def start(self) -> None: + while self.main_loop: time.sleep(1) + def stop(self) -> None: + self.main_loop = False + if __name__ == "__main__": main = Main() main.startReceiver() main.startHandler() + controller.setWatchdogCallback(main.stop) controller.init() # mappingのすべてのstatusをTrueにする @@ -389,7 +398,7 @@ if __name__ == "__main__": process = "main" match process: case "main": - main.loop() + main.start() case "test": for _ in range(100): diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 8c8fb406..64e56bd7 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -91,6 +91,8 @@ const StartPythonFacadeComponent = () => { useEffect(() => { if (!hasRunRef.current) { asyncStartPython().then((result) => { + startFeedingToWatchDog(); + getUiLanguage(); getUiScaling(); getMessageLogUiScaling(); @@ -245,4 +247,12 @@ const TransparencyController = () => { }, [currentTransparency.data]); return null; +}; + +import { useStdoutToPython } from "@logics/useStdoutToPython"; +const startFeedingToWatchDog = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + setInterval(() => { + asyncStdoutToPython("/run/feed_watchdog"); + }, 20000); // 20000ミリ秒 = 20秒 }; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index c2cabe2c..06887605 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -82,6 +82,7 @@ export const useReceiveRoutes = () => { const routes = { + "/run/feed_watchdog": () => {}, // Main Page // Page Controls "/get/data/main_window_sidebar_compact_mode": updateIsMainPageCompactMode,