model_train_dm/backend/apps/cloud_terminal/scheduler.py
2026-07-27 17:51:49 +08:00

80 lines
2.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""离线检测定时调度器
使用 APScheduler 在 Django 进程中启动后台线程,每 30 秒检测一次终端心跳超时。
"""
import logging
from apscheduler.schedulers.background import BackgroundScheduler
from django.utils.timezone import now
logger = logging.getLogger(__name__)
CHECK_INTERVAL_SECONDS = 30 # 检测间隔30 秒
_scheduler: BackgroundScheduler | None = None
def _check_offline_terminals():
"""检测超时心跳的终端(3 分钟内未心跳),标记为离线并推送 WebSocket 通知"""
from datetime import timedelta
from apps.core.models import AiSmartTerminal
HEARTBEAT_TIMEOUT = timedelta(minutes=3)
threshold = now() - HEARTBEAT_TIMEOUT
offline_terminals = AiSmartTerminal.objects.filter(
online_status=1,
last_heartbeat_time__isnull=False,
last_heartbeat_time__lt=threshold,
)
if not offline_terminals.exists():
return
offline_list = list(offline_terminals.values("stcd", "stnm"))
count = offline_terminals.update(online_status=0)
logger.warning("标记 %d 个终端为离线:", count)
for t in offline_list:
logger.warning(" - %s (%s)", t["stcd"], t["stnm"])
# WebSocket 推送离线通知
try:
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
channel_layer = get_channel_layer()
if channel_layer is not None:
async_to_sync(channel_layer.group_send)(
"terminal_status",
{
"type": "terminal.status",
"data": {
"event": "offline",
"count": count,
"terminals": offline_list,
},
},
)
except Exception:
logger.exception("WebSocket 推送失败")
def start_offline_checker():
"""启动离线检测后台调度器(仅在主进程中启动一次)"""
global _scheduler
if _scheduler is not None:
return # 已启动,避免重复
_scheduler = BackgroundScheduler(daemon=True)
_scheduler.add_job(
_check_offline_terminals,
"interval",
seconds=CHECK_INTERVAL_SECONDS,
id="check_terminal_offline",
replace_existing=True,
)
_scheduler.start()
logger.info("离线检测调度器已启动,每 %d 秒检测一次", CHECK_INTERVAL_SECONDS)