2025-12-11 13:41:07 +08:00
|
|
|
|
# main.py
|
2025-11-26 13:55:04 +08:00
|
|
|
|
# 主程序入口
|
|
|
|
|
|
import os
|
|
|
|
|
|
import signal
|
|
|
|
|
|
import threading
|
2025-12-11 15:08:28 +08:00
|
|
|
|
import time
|
|
|
|
|
|
import schedule
|
2025-11-26 13:55:04 +08:00
|
|
|
|
import torch
|
|
|
|
|
|
from log import logger
|
2025-12-11 13:41:07 +08:00
|
|
|
|
from global_data import gd
|
2025-11-26 13:55:04 +08:00
|
|
|
|
from server import socketio, app
|
2025-12-11 13:41:07 +08:00
|
|
|
|
from task_manager import task_manager
|
2025-11-26 13:55:04 +08:00
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
# 初始化全局数据
|
2025-11-26 13:55:04 +08:00
|
|
|
|
gd._init()
|
|
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
# 设置全局变量
|
|
|
|
|
|
gd.set_value('task_manager', task_manager) # 直接在这里初始化
|
2025-11-26 13:55:04 +08:00
|
|
|
|
gd.set_value('latest_drone_data', None)
|
|
|
|
|
|
gd.set_value('mqtt_data_lock', threading.Lock())
|
|
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
# 初始化任务管理器
|
|
|
|
|
|
logger.info("任务管理器初始化...")
|
|
|
|
|
|
logger.info(f"任务管理器实例: {task_manager}")
|
|
|
|
|
|
|
2025-12-11 15:08:28 +08:00
|
|
|
|
|
|
|
|
|
|
def cleanup_stopped_tasks():
|
|
|
|
|
|
"""定期清理已停止的任务"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not task_manager:
|
|
|
|
|
|
return
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# 取得所有任务
|
2025-12-11 15:08:28 +08:00
|
|
|
|
all_tasks = task_manager.get_all_tasks()
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# 计数
|
2025-12-11 15:08:28 +08:00
|
|
|
|
cleaned_count = 0
|
|
|
|
|
|
|
|
|
|
|
|
for task in all_tasks:
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# 取得任务ID
|
2025-12-11 15:08:28 +08:00
|
|
|
|
task_id = task.get('task_id')
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# 取得任务状态
|
2025-12-11 15:08:28 +08:00
|
|
|
|
status = task.get('status', 'unknown')
|
|
|
|
|
|
|
|
|
|
|
|
# 清理已停止、失败或错误的任务
|
|
|
|
|
|
if status in ['stopped', 'failed', 'error']:
|
|
|
|
|
|
# 检查任务线程是否真的已经停止
|
|
|
|
|
|
thread = task_manager.tasks.get(task_id, {}).get('thread')
|
|
|
|
|
|
if thread and hasattr(thread, 'is_alive'):
|
|
|
|
|
|
if not thread.is_alive():
|
|
|
|
|
|
if task_manager.cleanup_task(task_id):
|
|
|
|
|
|
cleaned_count += 1
|
|
|
|
|
|
else:
|
|
|
|
|
|
if task_manager.cleanup_task(task_id):
|
|
|
|
|
|
cleaned_count += 1
|
|
|
|
|
|
|
|
|
|
|
|
if cleaned_count > 0:
|
|
|
|
|
|
logger.info(f"定时清理已完成,清理了 {cleaned_count} 个任务")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"定时清理任务失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def start_cleanup_scheduler():
|
|
|
|
|
|
"""启动定时清理调度器"""
|
2026-01-09 09:43:51 +08:00
|
|
|
|
if not schedule:
|
|
|
|
|
|
logger.warning("schedule模块不可用,跳过定时清理调度器")
|
|
|
|
|
|
return
|
2025-12-11 15:08:28 +08:00
|
|
|
|
|
2026-01-09 09:43:51 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 每5分钟清理一次
|
|
|
|
|
|
schedule.every(5).minutes.do(cleanup_stopped_tasks)
|
|
|
|
|
|
|
|
|
|
|
|
# 每分钟检查一次待清理任务
|
|
|
|
|
|
schedule.every(1).minutes.do(check_pending_cleanup)
|
|
|
|
|
|
|
|
|
|
|
|
def run_scheduler():
|
|
|
|
|
|
while True:
|
|
|
|
|
|
try:
|
|
|
|
|
|
schedule.run_pending()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"定时任务执行失败: {str(e)}")
|
|
|
|
|
|
#time.sleep(60) # 每分钟检查一次
|
|
|
|
|
|
time.sleep(1) # 缩短为1秒检查频率
|
|
|
|
|
|
|
|
|
|
|
|
scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
|
|
|
|
|
|
scheduler_thread.start()
|
|
|
|
|
|
logger.info("定时清理调度器已启动")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"启动定时清理调度器失败: {str(e)}")
|
2025-12-11 15:08:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_pending_cleanup():
|
|
|
|
|
|
"""检查待清理的任务"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not task_manager:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否有长时间处于停止状态但未清理的任务
|
|
|
|
|
|
for task_id, task_info in task_manager.tasks.items():
|
|
|
|
|
|
status = task_info.get('status', 'unknown')
|
|
|
|
|
|
last_updated = task_info.get('last_updated', 0)
|
|
|
|
|
|
|
|
|
|
|
|
if status == 'stopped' and time.time() - last_updated > 60: # 停止超过60秒
|
|
|
|
|
|
logger.info(f"发现长时间停止未清理的任务: {task_id}")
|
|
|
|
|
|
task_manager.cleanup_task(task_id)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"检查待清理任务失败: {str(e)}")
|
|
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
|
|
|
|
|
|
# main.py 修改部分
|
|
|
|
|
|
|
2025-11-26 13:55:04 +08:00
|
|
|
|
if __name__ == '__main__':
|
2025-12-11 13:41:07 +08:00
|
|
|
|
logger.info("启动多任务版YOLOv8服务")
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# 打印PyTorch版本和CUDA可用性
|
2025-11-26 13:55:04 +08:00
|
|
|
|
logger.info(f"PyTorch版本: {torch.__version__}, CUDA可用: {torch.cuda.is_available()}")
|
2025-12-11 13:41:07 +08:00
|
|
|
|
|
2025-12-11 15:08:28 +08:00
|
|
|
|
# 启动定时清理
|
|
|
|
|
|
start_cleanup_scheduler()
|
|
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
# 初始化任务推流管理器
|
|
|
|
|
|
try:
|
|
|
|
|
|
from task_stream_manager import task_stream_manager
|
|
|
|
|
|
|
|
|
|
|
|
task_stream_manager.start_health_monitor()
|
|
|
|
|
|
logger.info("任务推流管理器初始化完成")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"初始化推流管理器失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-26 13:55:04 +08:00
|
|
|
|
# 退出服务
|
|
|
|
|
|
def graceful_exit(signum, frame):
|
2025-12-11 13:41:07 +08:00
|
|
|
|
logger.info("收到退出信号,停止所有服务...")
|
|
|
|
|
|
|
|
|
|
|
|
# 停止所有任务推流
|
|
|
|
|
|
try:
|
|
|
|
|
|
from task_stream_manager import task_stream_manager
|
2026-01-09 09:43:51 +08:00
|
|
|
|
if 'task_stream_manager' in globals():
|
|
|
|
|
|
task_stream_manager.cleanup_all()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"清理推流管理器失败: {str(e)}")
|
2025-12-11 13:41:07 +08:00
|
|
|
|
|
|
|
|
|
|
# 停止所有任务
|
2026-01-09 09:43:51 +08:00
|
|
|
|
try:
|
|
|
|
|
|
if task_manager:
|
|
|
|
|
|
cleaned_count = task_manager.cleanup_all_tasks()
|
|
|
|
|
|
logger.info(f"清理了 {cleaned_count} 个任务")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"清理任务管理器失败: {str(e)}")
|
2025-12-11 13:41:07 +08:00
|
|
|
|
|
2025-11-26 13:55:04 +08:00
|
|
|
|
logger.info("服务已退出")
|
|
|
|
|
|
os._exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-09 09:43:51 +08:00
|
|
|
|
# Windows平台特殊处理信号
|
|
|
|
|
|
try:
|
|
|
|
|
|
if os.name != 'nt': # Unix/Linux/Mac
|
|
|
|
|
|
signal.signal(signal.SIGINT, graceful_exit)
|
|
|
|
|
|
signal.signal(signal.SIGTERM, graceful_exit)
|
|
|
|
|
|
else: # Windows
|
|
|
|
|
|
# Windows下使用键盘中断处理
|
|
|
|
|
|
import atexit
|
|
|
|
|
|
|
|
|
|
|
|
atexit.register(lambda: graceful_exit(None, None))
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"注册退出处理函数失败: {str(e)}")
|
2025-11-26 13:55:04 +08:00
|
|
|
|
|
2025-12-11 13:41:07 +08:00
|
|
|
|
# 启动服务
|
2026-01-09 09:43:51 +08:00
|
|
|
|
try:
|
|
|
|
|
|
socketio.run(app,
|
|
|
|
|
|
host='0.0.0.0',
|
|
|
|
|
|
port=9309,
|
|
|
|
|
|
debug=True,
|
|
|
|
|
|
use_reloader=False,
|
|
|
|
|
|
allow_unsafe_werkzeug=True)
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
logger.info("收到键盘中断信号")
|
|
|
|
|
|
graceful_exit(None, None)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"启动服务失败: {str(e)}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
|
|
|
|
logger.error(traceback.format_exc())
|