feat: 没测过的MAA调度方案

This commit is contained in:
DLmaster361
2025-08-09 01:50:38 +08:00
parent ba1fcd1f26
commit 9a87a62353
73 changed files with 4081 additions and 358 deletions

View File

@@ -20,242 +20,30 @@
import uuid
from datetime import datetime
from packaging import version
from typing import Dict, Union
import asyncio
from fastapi import WebSocket
from typing import Dict, Optional
from .config import Config, MaaConfig, GeneralConfig
from utils import get_logger
from task import *
from .config import Config, MaaConfig, GeneralConfig, QueueConfig
from app.models.schema import TaskMessage
from app.utils import get_logger
from app.task import *
logger = get_logger("业务调度")
class Task:
"""业务线程"""
check_maa_version = Signal(str)
push_info_bar = Signal(str, str, str, int)
play_sound = Signal(str)
question = Signal(str, str)
question_response = Signal(bool)
update_maa_user_info = Signal(str, dict)
update_general_sub_info = Signal(str, dict)
create_task_list = Signal(list)
create_user_list = Signal(list)
update_task_list = Signal(list)
update_user_list = Signal(list)
update_log_text = Signal(str)
accomplish = Signal(list)
def __init__(
self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]]
):
super(Task, self).__init__()
self.setObjectName(f"Task-{mode}-{name}")
self.mode = mode
self.name = name
self.info = info
self.logs = []
self.question_response.connect(lambda: print("response"))
@logger.catch
def run(self):
if "设置MAA" in self.mode:
logger.info(f"任务开始:设置{self.name}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "设置MAA", self.name, 3000)
self.task = MaaManager(
self.mode,
Config.script_dict[self.name],
(None if "全局" in self.mode else self.info["SetMaaInfo"]["Path"]),
)
self.task.check_maa_version.connect(self.check_maa_version.emit)
self.task.push_info_bar.connect(self.push_info_bar.emit)
self.task.play_sound.connect(self.play_sound.emit)
self.task.accomplish.connect(lambda: self.accomplish.emit([]))
try:
self.task.run()
except Exception as e:
logger.exception(
f"任务异常:{self.name},错误信息:{e}", module=f"业务 {self.name}"
)
self.push_info_bar.emit("error", "任务异常", self.name, -1)
elif self.mode == "设置通用脚本":
logger.info(f"任务开始:设置{self.name}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "设置通用脚本", self.name, 3000)
self.task = GeneralManager(
self.mode,
Config.script_dict[self.name],
self.info["SetSubInfo"]["Path"],
)
self.task.push_info_bar.connect(self.push_info_bar.emit)
self.task.play_sound.connect(self.play_sound.emit)
self.task.accomplish.connect(lambda: self.accomplish.emit([]))
try:
self.task.run()
except Exception as e:
logger.exception(
f"任务异常:{self.name},错误信息:{e}", module=f"业务 {self.name}"
)
self.push_info_bar.emit("error", "任务异常", self.name, -1)
else:
logger.info(f"任务开始:{self.name}", module=f"业务 {self.name}")
self.task_list = [
[
(
value
if Config.script_dict[value]["Config"].get_name() == ""
else f"{value} - {Config.script_dict[value]["Config"].get_name()}"
),
"等待",
value,
]
for _, value in sorted(
self.info["Queue"].items(), key=lambda x: int(x[0][7:])
)
if value != "禁用"
]
self.create_task_list.emit(self.task_list)
for task in self.task_list:
if self.isInterruptionRequested():
break
task[1] = "运行"
self.update_task_list.emit(self.task_list)
# 检查任务是否在运行列表中
if task[2] in Config.running_list:
task[1] = "跳过"
self.update_task_list.emit(self.task_list)
logger.info(
f"跳过任务:{task[0]},该任务已在运行列表中",
module=f"业务 {self.name}",
)
self.push_info_bar.emit("info", "跳过任务", task[0], 3000)
continue
# 标记为运行中
Config.running_list.append(task[2])
logger.info(f"任务开始:{task[0]}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "任务开始", task[0], 3000)
if Config.script_dict[task[2]]["Type"] == "Maa":
self.task = MaaManager(
self.mode[0:4],
Config.script_dict[task[2]],
)
self.task.check_maa_version.connect(self.check_maa_version.emit)
self.task.question.connect(self.question.emit)
self.question_response.disconnect()
self.question_response.connect(self.task.question_response.emit)
self.task.push_info_bar.connect(self.push_info_bar.emit)
self.task.play_sound.connect(self.play_sound.emit)
self.task.create_user_list.connect(self.create_user_list.emit)
self.task.update_user_list.connect(self.update_user_list.emit)
self.task.update_log_text.connect(self.update_log_text.emit)
self.task.update_user_info.connect(self.update_maa_user_info.emit)
self.task.accomplish.connect(
lambda log: self.task_accomplish(task[2], log)
)
elif Config.script_dict[task[2]]["Type"] == "General":
self.task = GeneralManager(
self.mode[0:4],
Config.script_dict[task[2]],
)
self.task.question.connect(self.question.emit)
self.question_response.disconnect()
self.question_response.connect(self.task.question_response.emit)
self.task.push_info_bar.connect(self.push_info_bar.emit)
self.task.play_sound.connect(self.play_sound.emit)
self.task.create_user_list.connect(self.create_user_list.emit)
self.task.update_user_list.connect(self.update_user_list.emit)
self.task.update_log_text.connect(self.update_log_text.emit)
self.task.update_sub_info.connect(self.update_general_sub_info.emit)
self.task.accomplish.connect(
lambda log: self.task_accomplish(task[2], log)
)
try:
self.task.run() # 运行任务业务
task[1] = "完成"
self.update_task_list.emit(self.task_list)
logger.info(f"任务完成:{task[0]}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "任务完成", task[0], 3000)
except Exception as e:
self.task_accomplish(
task[2],
{
"Time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"History": f"任务异常,异常简报:{e}",
},
)
task[1] = "异常"
self.update_task_list.emit(self.task_list)
logger.exception(
f"任务异常:{task[0]},错误信息:{e}",
module=f"业务 {self.name}",
)
self.push_info_bar.emit("error", "任务异常", task[0], -1)
# 任务结束后从运行列表中移除
Config.running_list.remove(task[2])
self.accomplish.emit(self.logs)
def task_accomplish(self, name: str, log: dict):
"""
销毁任务线程并保存任务结果
:param name: 任务名称
:param log: 任务日志记录
"""
logger.info(
f"任务完成:{name},日志记录:{list(log.values())}",
module=f"业务 {self.name}",
)
self.logs.append([name, log])
self.task.deleteLater()
class _TaskManager:
"""业务调度器"""
def __init__(self):
super(_TaskManager, self).__init__()
super().__init__()
self.task_dict: Dict[str, Task] = {}
self.task_dict: Dict[uuid.UUID, asyncio.Task] = {}
self.connection_events: Dict[uuid.UUID, asyncio.Event] = {}
self.websocket_dict: Dict[uuid.UUID, WebSocket] = {}
def add_task(self, mode: str, uid: str):
async def add_task(self, mode: str, uid: str) -> uuid.UUID:
"""
添加任务
@@ -268,6 +56,7 @@ class _TaskManager:
if mode == "设置脚本":
if actual_id in Config.ScriptConfig:
task_id = actual_id
actual_id = None
else:
for script_id, script in Config.ScriptConfig.items():
if (
@@ -280,128 +69,192 @@ class _TaskManager:
raise ValueError(
f"The task corresponding to UID {uid} could not be found."
)
elif actual_id in Config.QueueConfig or actual_id in Config.ScriptConfig:
elif actual_id in Config.QueueConfig:
task_id = actual_id
actual_id = None
elif actual_id in Config.ScriptConfig:
task_id = uuid.uuid4()
else:
raise ValueError(f"The task corresponding to UID {uid} could not be found.")
if name in Config.running_list or name in self.task_dict:
if task_id in self.task_dict:
logger.warning(f"任务已存在:{name}")
MainInfoBar.push_info_bar("warning", "任务已存在", name, 5000)
return None
raise RuntimeError(f"The task {task_id} is already running.")
logger.info(f"任务开始{name},模式:{mode}", module="业务调度")
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
SoundPlayer.play("任务开始")
# 标记任务为运行中
Config.running_list.append(name)
logger.info(f"创建任务:{task_id},模式:{mode}")
# 创建任务实例并连接信号
self.task_dict[name] = Task(mode, name, info)
self.task_dict[name].check_maa_version.connect(self.check_maa_version)
self.task_dict[name].question.connect(
lambda title, content: self.push_dialog(name, title, content)
if task_id in self.connection_events:
self.connection_events[task_id].clear()
else:
self.connection_events[task_id] = asyncio.Event()
await self.connection_events[task_id].wait()
if task_id not in self.websocket_dict:
raise RuntimeError(f"The task {task_id} is not connected to a WebSocket.")
logger.info(f"开始运行任务:{task_id},模式:{mode}")
self.task_dict[task_id] = asyncio.create_task(
self.run_task(mode, task_id, actual_id)
)
self.task_dict[name].push_info_bar.connect(MainInfoBar.push_info_bar)
self.task_dict[name].play_sound.connect(SoundPlayer.play)
self.task_dict[name].update_maa_user_info.connect(Config.change_maa_user_info)
self.task_dict[name].update_general_sub_info.connect(
Config.change_general_sub_info
)
self.task_dict[name].accomplish.connect(
lambda logs: self.remove_task(mode, name, logs)
self.task_dict[task_id].add_done_callback(
lambda t: asyncio.create_task(self.remove_task(t, mode, task_id))
)
# 向UI发送信号以创建或连接GUI
if "新调度台" in mode:
self.create_gui.emit(self.task_dict[name])
return task_id
elif "主调度台" in mode:
self.connect_gui.emit(self.task_dict[name])
@logger.catch
async def run_task(
self, mode: str, task_id: uuid.UUID, actual_id: Optional[uuid.UUID]
):
# 启动任务线程
self.task_dict[name].start()
websocket = self.websocket_dict[task_id]
def stop_task(self, name: str) -> None:
if mode == "设置脚本":
if isinstance(Config.ScriptConfig[task_id], MaaConfig):
task_item = MaaManager(mode, task_id, actual_id, websocket)
# elif isinstance(Config.ScriptConfig[task_id], GeneralConfig):
# task_item = GeneralManager(mode, task_id, actual_id, websocket)
else:
logger.error(
f"不支持的脚本类型:{Config.ScriptConfig[task_id].__class__.__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "脚本类型不支持"}
).model_dump()
)
return
task = asyncio.create_task(task_item.run())
task.add_done_callback(
lambda t: asyncio.create_task(task_item.final_task(t))
)
else:
if task_id in Config.QueueConfig:
queue = Config.QueueConfig[task_id]
if not isinstance(queue, QueueConfig):
logger.error(
f"不支持的队列类型:{Config.QueueConfig[task_id].__class__.__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "队列类型不支持"}
).model_dump()
)
return
task_list = []
for queue_item in queue.QueueItem.values():
uid = uuid.UUID(queue_item.get("Info", "ScriptId"))
task_list.append(
{
"script_id": str(uid),
"status": "等待",
"name": Config.ScriptConfig[uid].get("Info", "Name"),
}
)
elif actual_id is not None and actual_id in Config.ScriptConfig:
task_list = [{"script_id": str(actual_id), "status": "等待"}]
for task in task_list:
script_id = uuid.UUID(task["script_id"])
# 检查任务是否在运行列表中
if script_id in self.task_dict:
task["status"] = "跳过"
await websocket.send_json(
TaskMessage(
type="Update", data={"task_list": task_list}
).model_dump()
)
logger.info(f"跳过任务:{script_id},该任务已在运行列表中")
continue
# 标记为运行中
task["status"] = "运行"
await websocket.send_json(
TaskMessage(
type="Update", data={"task_list": task_list}
).model_dump()
)
logger.info(f"任务开始:{script_id}")
if isinstance(Config.ScriptConfig[script_id], MaaConfig):
task_item = MaaManager(mode, script_id, None, websocket)
# elif isinstance(Config.ScriptConfig[task_id], GeneralConfig):
# task_item = GeneralManager(mode, task_id, actual_id, websocket)
else:
logger.error(
f"不支持的脚本类型:{Config.ScriptConfig[script_id].__class__.__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "脚本类型不支持"}
).model_dump()
)
continue
task = asyncio.create_task(task_item.run())
task.add_done_callback(
lambda t: asyncio.create_task(task_item.final_task(t))
)
async def stop_task(self, task_id: uuid.UUID) -> None:
"""
中止任务
:param name: 任务名称
:param task_id: 任务ID
"""
logger.info(f"中止任务:{name}", module="业务调度")
MainInfoBar.push_info_bar("info", "中止任务", name, 3000)
logger.info(f"中止任务:{task_id}")
if name == "ALL":
if task_id not in self.task_dict:
raise ValueError(f"The task {task_id} is not running.")
for name in self.task_dict:
self.task_dict[task_id].cancel()
self.task_dict[name].task.requestInterruption()
self.task_dict[name].requestInterruption()
self.task_dict[name].quit()
self.task_dict[name].wait()
elif name in self.task_dict:
self.task_dict[name].task.requestInterruption()
self.task_dict[name].requestInterruption()
self.task_dict[name].quit()
self.task_dict[name].wait()
def remove_task(self, mode: str, name: str, logs: list) -> None:
async def remove_task(
self, task: asyncio.Task, mode: str, task_id: uuid.UUID
) -> None:
"""
处理任务结束后的收尾工作
:param mode: 任务模式
:param name: 任务名称
:param logs: 任务日志
Parameters
----------
task : asyncio.Task
任务对象
mode : str
任务模式
task_id : uuid.UUID
任务ID
"""
logger.info(f"任务结束:{name}", module="业务调度")
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
SoundPlayer.play("任务结束")
logger.info(f"任务结束:{task_id}")
# 删除任务线程,移除运行中标记
self.task_dict[name].deleteLater()
self.task_dict.pop(name)
Config.running_list.remove(name)
# 从任务字典中移除任务
try:
await task
except asyncio.CancelledError:
logger.info(f"任务 {task_id} 已结束")
self.task_dict.pop(task_id)
if "调度队列" in name and "人工排查" not in mode:
# 保存调度队列历史记录
if len(logs) > 0:
time = logs[0][1]["Time"]
history = ""
for log in logs:
history += f"任务名称:{log[0]}{log[1]["History"].replace("\n","\n ")}\n"
Config.save_history(name, {"Time": time, "History": history})
else:
Config.save_history(
name,
{
"Time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"History": "没有任务被执行",
},
)
# 根据调度队列情况设置电源状态
if (
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].QueueSet_AfterAccomplish
)
!= "NoAction"
and Config.power_sign == "NoAction"
):
Config.set_power_sign(
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].QueueSet_AfterAccomplish
)
)
if Config.args.mode == "cli" and Config.power_sign == "NoAction":
Config.set_power_sign("KillSelf")
websocket = self.websocket_dict.pop(task_id, None)
if websocket:
await websocket.send_json(
TaskMessage(type="Signal", data={"Accomplish": "无描述"}).model_dump()
)
await websocket.close()
TaskManager = _TaskManager()