From 0f1ce38e6445368c967003743e977a080c588962 Mon Sep 17 00:00:00 2001 From: insulee Date: Fri, 13 Feb 2026 09:06:41 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20drift=5Fmonitor.py=20=EC=A0=95=EC=8B=9C?= =?UTF-8?q?=EC=9D=BD=EA=B8=B0=20=EC=A0=88=EC=A0=84=20=EB=AA=A8=EB=93=9C=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91=20=EB=B0=8F=20=EC=98=88=EC=99=B8=20=EC=95=88?= =?UTF-8?q?=EC=A0=95=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - services/drift_monitor.py - threading.Timer 체인 방식 → 폴링 루프 스레드로 교체 - 30초 간격 폴링으로 절전 복귀 시 즉시 감지 - _do_read()를 try/except로 감싸 예외 시에도 루프 유지 - threading.Event로 깔끔한 중지 처리 - _schedule_hourly(), _hourly_tick() 삭제 → _hourly_loop() 신규 - Dabit Time Manager.spec - 빌드 출력 파일명 v1.0 → v1.1 업데이트 Co-Authored-By: Claude Opus 4.6 --- Dabit Time Manager.spec | 2 +- services/drift_monitor.py | 64 ++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/Dabit Time Manager.spec b/Dabit Time Manager.spec index 3bd3ebb..0d96777 100644 --- a/Dabit Time Manager.spec +++ b/Dabit Time Manager.spec @@ -22,7 +22,7 @@ exe = EXE( a.binaries, a.datas, [], - name='DabitTimeManager_v1.0', + name='DabitTimeManager_v1.1', debug=False, bootloader_ignore_signals=False, strip=False, diff --git a/services/drift_monitor.py b/services/drift_monitor.py index e75c37b..780e615 100644 --- a/services/drift_monitor.py +++ b/services/drift_monitor.py @@ -16,7 +16,8 @@ class DriftMonitor: def __init__(self): self._timer: threading.Timer | None = None - self._hourly_timer: threading.Timer | None = None + self._hourly_thread: threading.Thread | None = None + self._hourly_stop_event: threading.Event | None = None self._running = False self._hourly_running = False self._interval = DEFAULT_MONITOR_INTERVAL @@ -72,36 +73,49 @@ class DriftMonitor: if self._hourly_running: return self._hourly_running = True - self._schedule_hourly() + self._hourly_stop_event = threading.Event() + self._hourly_thread = threading.Thread(target=self._hourly_loop, daemon=True) + self._hourly_thread.start() def stop_hourly(self): """매시 정시 읽기 중지.""" self._hourly_running = False - if self._hourly_timer: - self._hourly_timer.cancel() - self._hourly_timer = None + if self._hourly_stop_event: + self._hourly_stop_event.set() + self._hourly_thread = None - def _schedule_hourly(self): - """다음 정시까지 대기 후 읽기 예약.""" - if not self._hourly_running: - return - now = datetime.now() - # 다음 정시: 현재 시 + 1, 분/초 = 0 - next_hour = now.replace(minute=0, second=0, microsecond=0) - next_hour = next_hour + timedelta(hours=1) - delay = (next_hour - now).total_seconds() - if self._on_error: - self._on_error(f"정시 읽기 예약: {next_hour.strftime('%H:%M:%S')} ({delay:.0f}초 후)") - self._hourly_timer = threading.Timer(delay, self._hourly_tick) - self._hourly_timer.daemon = True - self._hourly_timer.start() + def _hourly_loop(self): + """매시 정시 읽기 루프 (절전 복귀 대응).""" + while self._hourly_running: + now = datetime.now() + next_hour = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) - def _hourly_tick(self): - """정시 도달 시 읽기 실행 후 다음 정시 예약.""" - if not self._hourly_running: - return - self._do_read(schedule_next=False) - self._schedule_hourly() + if self._on_error: + delay = (next_hour - now).total_seconds() + self._on_error(f"정시 읽기 예약: {next_hour.strftime('%H:%M:%S')} ({delay:.0f}초 후)") + + # 30초 간격 폴링으로 정시 대기 (절전 복귀 시 즉시 감지) + while self._hourly_running: + now = datetime.now() + if now >= next_hour: + break + remaining = (next_hour - now).total_seconds() + wait = min(remaining, 30) + if self._hourly_stop_event.wait(timeout=wait): + return # 중지 요청 + + if not self._hourly_running: + break + + # 정시 읽기 실행 + try: + self._do_read(schedule_next=False) + except Exception as e: + if self._on_error: + try: + self._on_error(f"정시 읽기 오류: {e}") + except: + pass def read_once(self): """즉시 한 번 읽기 (별도 스레드)."""