Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f1ce38e64 | ||
|
|
c4a71ea5fe |
14
.gitattributes
vendored
Normal file
14
.gitattributes
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Auto-detect text files and normalize line endings
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Force LF for scripts and source files (Linux에서 실행되는 파일)
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.py text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.env* text eol=lf
|
||||||
|
Dockerfile text eol=lf
|
||||||
|
|
||||||
|
# Force CRLF for Windows-only files
|
||||||
|
*.bat text eol=crlf
|
||||||
|
*.ps1 text eol=crlf
|
||||||
52
.gitmessage
Normal file
52
.gitmessage
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# <type>: <파일/컴포넌트> <제목> (70자 이내)
|
||||||
|
#
|
||||||
|
# <본문 - 변경 사항>
|
||||||
|
# - 항목1
|
||||||
|
# - 항목2
|
||||||
|
# - 항목3
|
||||||
|
#
|
||||||
|
# <파일별 상세 (필수)>
|
||||||
|
# 파일: <경로>
|
||||||
|
# - 변경 내용 1
|
||||||
|
# - 변경 내용 2
|
||||||
|
#
|
||||||
|
# <추가 컨텍스트/이유 (선택)>
|
||||||
|
#
|
||||||
|
# --- 커밋 규칙 ---
|
||||||
|
# Type:
|
||||||
|
# feat: 새 기능
|
||||||
|
# fix: 버그 수정
|
||||||
|
# docs: 문서 변경
|
||||||
|
# refactor: 리팩토링
|
||||||
|
# chore: 빌드/설정
|
||||||
|
# perf: 성능 개선
|
||||||
|
# test: 테스트
|
||||||
|
#
|
||||||
|
# 제목:
|
||||||
|
# - 70자 이내
|
||||||
|
# - 파일/컴포넌트 명시 (예: "admin.html", "logging_service.py", "관리자 페이지")
|
||||||
|
# - 명령형 ("추가", "수정")
|
||||||
|
# - 마침표 없음
|
||||||
|
# - 구체적으로
|
||||||
|
#
|
||||||
|
# 본문:
|
||||||
|
# - 불릿 포인트 사용
|
||||||
|
# - 무엇을, 왜 변경했는지
|
||||||
|
# - 파일별 상세 섹션 필수 (어떤 함수/클래스를 추가/수정했는지)
|
||||||
|
#
|
||||||
|
# 좋은 예시:
|
||||||
|
# feat: admin.html 세션 목록에 읽음 상태 뱃지 추가
|
||||||
|
#
|
||||||
|
# - localStorage 기반 세션 읽음 상태 관리
|
||||||
|
# - 읽지 않음 뱃지(빨강): 한 번도 클릭하지 않은 세션
|
||||||
|
# - 신규 메시지 뱃지(주황): 마지막 확인 이후 추가된 메시지 수
|
||||||
|
#
|
||||||
|
# 파일: apps/user_chatbot/static/admin.html
|
||||||
|
# - getSessionReadStatus() 함수 추가
|
||||||
|
# - markSessionAsRead() 함수 추가
|
||||||
|
# - getSessionBadges() 함수 추가
|
||||||
|
# - CSS: .badge-unread, .badge-new 스타일 추가
|
||||||
|
#
|
||||||
|
# 나쁜 예시:
|
||||||
|
# feat: 세션 목록에 뱃지 추가 (어느 파일인지 불명확)
|
||||||
|
# fix: 버그 수정 (무엇을 고쳤는지 불명확)
|
||||||
@@ -22,7 +22,7 @@ exe = EXE(
|
|||||||
a.binaries,
|
a.binaries,
|
||||||
a.datas,
|
a.datas,
|
||||||
[],
|
[],
|
||||||
name='DabitTimeManager_v1.0',
|
name='DabitTimeManager_v1.1',
|
||||||
debug=False,
|
debug=False,
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
strip=False,
|
strip=False,
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ class DriftMonitor:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._timer: threading.Timer | None = None
|
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._running = False
|
||||||
self._hourly_running = False
|
self._hourly_running = False
|
||||||
self._interval = DEFAULT_MONITOR_INTERVAL
|
self._interval = DEFAULT_MONITOR_INTERVAL
|
||||||
@@ -72,36 +73,49 @@ class DriftMonitor:
|
|||||||
if self._hourly_running:
|
if self._hourly_running:
|
||||||
return
|
return
|
||||||
self._hourly_running = True
|
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):
|
def stop_hourly(self):
|
||||||
"""매시 정시 읽기 중지."""
|
"""매시 정시 읽기 중지."""
|
||||||
self._hourly_running = False
|
self._hourly_running = False
|
||||||
if self._hourly_timer:
|
if self._hourly_stop_event:
|
||||||
self._hourly_timer.cancel()
|
self._hourly_stop_event.set()
|
||||||
self._hourly_timer = None
|
self._hourly_thread = None
|
||||||
|
|
||||||
def _schedule_hourly(self):
|
def _hourly_loop(self):
|
||||||
"""다음 정시까지 대기 후 읽기 예약."""
|
"""매시 정시 읽기 루프 (절전 복귀 대응)."""
|
||||||
if not self._hourly_running:
|
while self._hourly_running:
|
||||||
return
|
now = datetime.now()
|
||||||
now = datetime.now()
|
next_hour = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
|
||||||
# 다음 정시: 현재 시 + 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_tick(self):
|
if self._on_error:
|
||||||
"""정시 도달 시 읽기 실행 후 다음 정시 예약."""
|
delay = (next_hour - now).total_seconds()
|
||||||
if not self._hourly_running:
|
self._on_error(f"정시 읽기 예약: {next_hour.strftime('%H:%M:%S')} ({delay:.0f}초 후)")
|
||||||
return
|
|
||||||
self._do_read(schedule_next=False)
|
# 30초 간격 폴링으로 정시 대기 (절전 복귀 시 즉시 감지)
|
||||||
self._schedule_hourly()
|
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):
|
def read_once(self):
|
||||||
"""즉시 한 번 읽기 (별도 스레드)."""
|
"""즉시 한 번 읽기 (별도 스레드)."""
|
||||||
|
|||||||
Reference in New Issue
Block a user