fix: drift_monitor.py 정시읽기 절전 모드 대응 및 예외 안정성 개선

- 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 <noreply@anthropic.com>
This commit is contained in:
insulee
2026-02-13 09:06:41 +09:00
parent c4a71ea5fe
commit 0f1ce38e64
2 changed files with 40 additions and 26 deletions

View File

@@ -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,

View File

@@ -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):
"""즉시 한 번 읽기 (별도 스레드)."""