1052 lines
44 KiB
Python
Raw 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.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED.task 模块 - 任务系统集成
"""
import json
from typing import Dict, Any, Optional, List
from utils.logger import get_logger
from utils.json_parser import safe_parse_dict, safe_parse_list
class VWEDTaskModule:
"""VWED.task 模块 - 任务系统集成"""
def __init__(self, script_id: str):
self.script_id = script_id
self.logger = get_logger(f"script.{script_id}.task")
async def _get_auth_token(self, auto_get_token: bool = True) -> Optional[str]:
"""获取认证token的通用方法
Args:
auto_get_token: 是否自动获取登录token默认true
Returns:
Optional[str]: 认证令牌获取失败返回None
"""
if not auto_get_token:
return None
try:
from services.sync_service import refresh_token_if_needed
token = await refresh_token_if_needed()
if token:
self.logger.info(f"脚本 {self.script_id} 成功获取到登录token")
return token
else:
self.logger.warning(f"脚本 {self.script_id} 获取登录token失败继续使用空token")
return None
except Exception as e:
self.logger.warning(f"脚本 {self.script_id} 获取token异常: {str(e)}继续使用空token")
return None
async def create_task(
self,
label: str,
task_type: int = 1,
remark: Optional[str] = None,
period: Optional[int] = None,
delay: Optional[int] = 3000,
release_sites: Optional[bool] = True,
tenant_id: str = "default",
map_id: Optional[str] = None,
auto_get_token: bool = True
) -> Dict[str, Any]:
"""创建VWED任务
Args:
label: 任务名称
task_type: 任务类型1-普通任务2-定时任务
remark: 任务备注
period: 周期时间(毫秒),定时任务必填
delay: 延迟时间毫秒默认3000
release_sites: 是否释放站点默认true
tenant_id: 租户ID默认"default"
map_id: 相关地图ID
auto_get_token: 是否自动获取登录token默认true
Returns:
Dict[str, Any]: 创建结果
"""
try:
from services.task_service import TaskService, TaskNameExistsError
from data.session import get_db
# 获取登录token
token = await self._get_auth_token(auto_get_token)
# 获取数据库会话
db = next(get_db())
try:
# 调用TaskService.create_task方法
result = TaskService.create_task(
db=db,
label=label,
task_type=task_type,
remark=remark,
period=period,
delay=delay,
release_sites=release_sites,
token=token, # 使用获取到的token
tenant_id=tenant_id,
map_id=map_id
)
self.logger.info(f"脚本 {self.script_id} 成功创建任务: {label} (ID: {result['id']})")
return {
"success": True,
"message": "任务创建成功",
"data": result
}
except TaskNameExistsError as e:
self.logger.warning(f"脚本 {self.script_id} 创建任务失败: {str(e)}")
return {
"success": False,
"message": f"任务名称已存在: {str(e)}",
"error_type": "name_exists"
}
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 创建任务失败: {str(e)}")
return {
"success": False,
"message": f"创建任务失败: {str(e)}",
"error_type": "create_error"
}
finally:
db.close()
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 任务创建异常: {str(e)}")
return {
"success": False,
"message": f"任务创建异常: {str(e)}",
"error_type": "system_error"
}
async def execute_task(
self,
task_id: str,
parameters: Optional[List[Dict[str, Any]]] = None,
source_system: str = "script",
source_device: Optional[str] = None,
auto_get_token: bool = True
) -> Dict[str, Any]:
"""执行VWED任务
Args:
task_id: 任务ID
parameters: 任务输入参数列表
source_system: 来源系统,默认"script"
source_device: 来源设备标识
auto_get_token: 是否自动获取登录token默认true
Returns:
Dict[str, Any]: 执行结果
"""
try:
from services.task_edit_service import TaskEditService
from routes.model.task_edit_model import TaskEditRunRequest, TaskInputParam
from data.enum.task_record_enum import SourceType
# 获取登录token
token = await self._get_auth_token(auto_get_token)
# 构建运行请求
input_params = []
if parameters:
for param in parameters:
if isinstance(param, dict) and "name" in param and "defaultValue" in param:
input_params.append(TaskInputParam(
name=param["name"],
defaultValue=param["defaultValue"]
))
run_request = TaskEditRunRequest(
taskId=task_id,
params=input_params,
source_type=SourceType.SYSTEM_SCHEDULING,
source_system=source_system,
source_device=source_device or f"script_{self.script_id}"
)
# 调用TaskEditService.run_task方法
result = await TaskEditService.run_task(
run_request=run_request,
client_ip="127.0.0.1", # 本地脚本调用
client_info=f'{{"script_id": "{self.script_id}", "source": "VWED.task.execute_task"}}',
tf_api_token=token # 使用获取到的token
)
if result and result.get("success"):
self.logger.info(f"脚本 {self.script_id} 成功启动任务: {task_id}")
return {
"success": True,
"message": "任务启动成功",
"data": result
}
else:
error_msg = result.get("message", "任务启动失败") if result else "任务启动失败"
self.logger.error(f"脚本 {self.script_id} 启动任务失败: {error_msg}")
return {
"success": False,
"message": error_msg,
"error_type": "execute_error"
}
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 执行任务异常: {str(e)}")
return {
"success": False,
"message": f"执行任务异常: {str(e)}",
"error_type": "system_error"
}
async def execute_task_with_record_id(
self,
task_id: str,
task_record_id: Optional[str] = None,
parameters: Optional[List[Dict[str, Any]]] = None,
source_system: str = "script",
source_device: Optional[str] = None,
auto_get_token: bool = True
) -> Dict[str, Any]:
"""执行VWED任务支持指定task_record_id
Args:
task_id: 任务ID
task_record_id: 指定的任务记录ID为空时自动生成
parameters: 任务输入参数列表
source_system: 来源系统,默认"script"
source_device: 来源设备标识
auto_get_token: 是否自动获取登录token默认true
Returns:
Dict[str, Any]: 执行结果
"""
try:
from services.task_edit_service import TaskEditService
from routes.model.task_edit_model import TaskEditRunRequest, TaskInputParamNew
from data.enum.task_record_enum import SourceType
# 获取登录token
token = await self._get_auth_token(auto_get_token)
# 构建运行请求
input_params = []
if parameters:
for param in parameters:
if isinstance(param, dict) and "name" in param and "defaultValue" in param:
input_params.append(TaskInputParamNew(
name=param["name"],
type="STRING", # 默认字符串类型
label=param["name"],
defaultValue=param["defaultValue"]
))
run_request = TaskEditRunRequest(
taskId=task_id,
params=input_params,
source_type=SourceType.SYSTEM_SCHEDULING,
source_system=source_system,
source_device=source_device or f"script_{self.script_id}",
task_record_id=task_record_id # 传递指定的task_record_id
)
# 调用TaskEditService.run_task方法
result = await TaskEditService.run_task(
run_request=run_request,
client_ip="127.0.0.1", # 本地脚本调用
client_info=f'{{"script_id": "{self.script_id}", "source": "VWED.task.execute_task_with_record_id"}}',
tf_api_token=token # 使用获取到的token
)
if result and result.get("success"):
self.logger.info(f"脚本 {self.script_id} 成功启动任务: {task_id} (记录ID: {result.get('taskRecordId', task_record_id)})")
return {
"success": True,
"message": "任务启动成功",
"data": result
}
else:
error_msg = result.get("message", "任务启动失败") if result else "任务启动失败"
self.logger.error(f"脚本 {self.script_id} 启动任务失败: {error_msg}")
return {
"success": False,
"message": error_msg,
"error_type": "execute_error"
}
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 执行任务异常: {str(e)}")
return {
"success": False,
"message": f"执行任务异常: {str(e)}",
"error_type": "system_error"
}
async def get_task_status(self, task_record_id: str) -> Dict[str, Any]:
"""获取任务执行状态
Args:
task_record_id: 任务记录ID不是任务定义ID
Returns:
Dict[str, Any]: 任务状态信息
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id)
)
task_record = result.scalar_one_or_none()
if not task_record:
return {
"success": False,
"message": "任务记录不存在",
"error_type": "not_found"
}
# 构建状态信息
status_info = {
"task_record_id": task_record.id,
"task_def_id": task_record.task_def_id,
"status": task_record.status,
"progress": task_record.progress,
"start_time": task_record.start_time.isoformat() if task_record.start_time else None,
"end_time": task_record.end_time.isoformat() if task_record.end_time else None,
"error_message": task_record.error_message,
"variables": task_record.variables
}
self.logger.info(f"脚本 {self.script_id} 获取任务状态: {task_record_id}")
return {
"success": True,
"message": "获取任务状态成功",
"data": status_info
}
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 获取任务状态异常: {str(e)}")
return {
"success": False,
"message": f"获取任务状态异常: {str(e)}",
"error_type": "system_error"
}
async def check_task_param(self, param: str) -> bool:
"""校验任务参数
校验输入参数中的 task_id 和 task_label 字段对应的数据是否在数据库中存在。
Args:
param: JSON字符串或Python字典字符串包含task_id和task_label字段
Returns:
bool: True表示数据存在False表示数据不存在
"""
try:
# 使用通用解析方法
data = safe_parse_dict(param, self.script_id)
if data is None:
return False
task_id = data.get("task_id", "")
task_label = data.get("task_label", "")
# 如果task_id和task_label都为空返回False
if not task_id and not task_label:
self.logger.warning(f"脚本 {self.script_id} task_id和task_label都为空")
return False
from data.session import get_async_session
from data.models.taskdef import VWEDTaskDef
from sqlalchemy import select, or_
async with get_async_session() as session:
# 构建查询条件task_id或task_label匹配即可
conditions = []
if task_id:
conditions.append(VWEDTaskDef.id == task_id)
if task_label:
conditions.append(VWEDTaskDef.label == task_label)
# 查询数据库
query = select(VWEDTaskDef.id).where(
or_(*conditions),
VWEDTaskDef.is_deleted == False
)
result = await session.execute(query)
task_def = result.scalar_one_or_none()
exists = task_def is not None
self.logger.info(f"脚本 {self.script_id} 任务参数校验结果: task_id={task_id}, task_label={task_label}, exists={exists}")
return exists
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 校验任务参数异常: {str(e)}")
return False
async def create_wind_task(self, param: str) -> str:
"""异步创建并运行一个任务
本方法是非阻塞方法,将在脚本中创建一个线程,来执行天风任务的实例。
Args:
param: JSON字符串包含任务创建参数
Returns:
str: JSON格式的创建结果
"""
try:
# 使用通用解析方法
data = safe_parse_dict(param, self.script_id)
if data is None:
return ""
task_id = data.get("task_id")
task_label = data.get("task_label")
task_record_id = data.get("task_record_id")
input_params = data.get("input_params", [])
# 验证必选参数
if not task_id and not task_label:
return json.dumps({"code": -1, "msg": "task_id和task_label至少需要提供一个"}, ensure_ascii=False)
# 解析输入参数
parameters = []
if input_params:
# 如果input_params是字符串尝试解析为JSON
if isinstance(input_params, str):
try:
input_params_data = json.loads(input_params)
except:
input_params_data = []
else:
# 如果input_params已经是列表直接使用
input_params_data = input_params
# 处理参数列表,保留所有字段信息
if isinstance(input_params_data, list):
# 如果是完整的参数配置列表:[{"name": "param1", "label": "参数1", "type": "STRING", "defaultValue": "", ...}, ...]
for param in input_params_data:
if isinstance(param, dict) and "name" in param:
# 保留完整的参数配置信息
param_config = {
"name": param["name"],
"defaultValue": str(param.get("defaultValue", ""))
}
# 如果有其他字段也保留
if "label" in param:
param_config["label"] = param["label"]
if "type" in param:
param_config["type"] = param["type"]
if "required" in param:
param_config["required"] = param["required"]
if "remark" in param:
param_config["remark"] = param["remark"]
if "checked" in param:
param_config["checked"] = param["checked"]
parameters.append(param_config)
elif isinstance(input_params_data, dict):
# 如果是简单的键值对格式:{"param1": "value1", "param2": "value2"}
for key, value in input_params_data.items():
parameters.append({
"name": key,
"defaultValue": str(value)
})
# 使用task_id或task_label查找任务并执行
target_task_id = task_id if task_id else None
if not target_task_id and task_label:
# 根据task_label查找task_id的逻辑
from data.session import get_async_session
from data.models.taskdef import VWEDTaskDef
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(select(VWEDTaskDef.id).where(
VWEDTaskDef.label == task_label,
VWEDTaskDef.is_deleted == False
))
task_def = result.scalar_one_or_none()
if task_def:
target_task_id = task_def
if not target_task_id:
return json.dumps({"code": -1, "msg": "未找到指定的任务"}, ensure_ascii=False)
# 如果提供了task_record_id检查是否已存在
if task_record_id and await self.is_task_record_id_exist(task_record_id):
return json.dumps({"code": -1, "msg": f"任务记录ID {task_record_id} 已存在"}, ensure_ascii=False)
# 执行任务
result = await self.execute_task_with_record_id(
task_id=target_task_id,
task_record_id=task_record_id,
parameters=parameters,
source_system="script",
source_device=f"script_{self.script_id}"
)
if result.get("success"):
return json.dumps({"code": 200, "msg": "任务创建成功"}, ensure_ascii=False)
else:
return json.dumps({"code": -1, "msg": result.get("message", "任务创建失败")}, ensure_ascii=False)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 创建任务异常: {str(e)}")
return json.dumps({"code": -1, "msg": f"创建任务异常: {str(e)}"}, ensure_ascii=False)
async def get_task_record_by_id(self, task_record_id: str) -> str:
"""根据任务实例ID查询任务实例
Args:
task_record_id: 任务实例的唯一ID
Returns:
str: JSON格式的任务实例信息不存在则返回"null"
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id)
)
task_record = result.scalar_one_or_none()
if not task_record:
return "null"
# 构建返回数据,按照文档格式
task_info = {
"id": task_record.id,
"def_id": task_record.def_id,
"def_label": task_record.def_label,
"status": task_record.status,
"created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] if task_record.created_at else None,
"ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] if task_record.ended_on else None,
"state_description": task_record.state_description or "",
"agv_id": task_record.agv_id or ""
}
return json.dumps(task_info, ensure_ascii=False)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 查询任务实例异常: {str(e)}")
raise e
# def get_task_record_list_by_out_order_no(self, out_order_no: str) -> str:
# """根据外部单号查询任务实例列表
#
# Args:
# out_order_no: 外部单号ID
#
# Returns:
# str: JSON数组格式的任务实例列表
# """
# try:
# # 暂时返回空数组,因为当前表结构中没有外部订单号字段
# # 需要在VWEDTaskRecord模型中添加out_order_no字段
# return "[]"
#
# except Exception as e:
# self.logger.error(f"脚本 {self.script_id} 根据外部单号查询任务异常: {str(e)}")
# raise e
async def get_task_record_by_agv_id(self, agv_id: str) -> str:
"""根据机器人ID查询任务实例列表
Args:
agv_id: 机器人ID
Returns:
str: JSON数组格式的任务实例列表
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskRecord).where(VWEDTaskRecord.agv_id == agv_id)
)
task_records = result.scalars().all()
if not task_records:
return "[]"
# 构建返回数据列表
task_list = []
for task_record in task_records:
task_info = {
"id": task_record.id,
"def_id": task_record.def_id,
"def_label": task_record.def_label,
"status": task_record.status,
"created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S") if task_record.created_at else None,
"ended_reason": task_record.ended_reason or "运行结束",
"ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S") if task_record.ended_on else None,
"state_description": task_record.state_description or "",
}
task_list.append(task_info)
return json.dumps(task_list, ensure_ascii=False)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 根据机器人ID查询任务异常: {str(e)}")
raise e
async def mark_complete(self, task_id: str, auto_get_token: bool = True) -> Dict[str, Any]:
"""运行任务封口
Args:
task_id: VWED任务系统任务ID
auto_get_token: 是否自动获取登录token默认true
Returns:
Dict[str, Any]: 封口结果
"""
try:
# 获取认证token
token = await self._get_auth_token(auto_get_token)
# 调用 closure_task 接口
from services.sync_service import closure_task
result = await closure_task(task_id=task_id, token=token)
if result and result.get("success"):
self.logger.info(f"脚本 {self.script_id} 成功封口任务: {task_id}")
return {
"success": True,
"message": "任务封口成功",
"data": result
}
else:
error_msg = result.get("message", "任务封口失败") if result else "任务封口失败"
self.logger.error(f"脚本 {self.script_id} 封口任务失败: {task_id}, 错误: {error_msg}")
return {
"success": False,
"message": error_msg,
"error_type": "closure_error"
}
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 标记运单完成异常: {str(e)}")
return {
"success": False,
"message": f"标记运单完成异常: {str(e)}",
"error_type": "system_error"
}
async def is_task_record_id_exist(self, task_record_id: str) -> bool:
"""校验任务实例ID是否存在
Args:
task_record_id: 任务实例id
Returns:
bool: True表示存在False表示不存在
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskRecord.id).where(VWEDTaskRecord.id == task_record_id)
)
task_record = result.scalar_one_or_none()
return task_record is not None
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 校验任务实例ID异常: {str(e)}")
raise e
async def is_task_label_exist(self, task_name: str) -> bool:
"""校验任务名称是否存在
Args:
task_name: 任务名称
Returns:
bool: True表示存在False表示不存在
"""
try:
from data.session import get_async_session
from data.models.taskdef import VWEDTaskDef
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskDef.id).where(
VWEDTaskDef.label == task_name,
VWEDTaskDef.is_deleted == False
)
)
task_def = result.scalar_one_or_none()
return task_def is not None
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 校验任务名称异常: {str(e)}")
raise e
async def query_task_record(self, param: str) -> str:
"""根据查询条件获取任务实例
Args:
param: JSON字符串包含查询条件
Returns:
str: JSON数组格式的任务实例列表
"""
try:
data = safe_parse_dict(param, self.script_id)
if data is None:
return "[]"
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select, and_
from datetime import datetime
async with get_async_session() as session:
# 构建查询条件
conditions = []
# 任务ID条件
if data.get("task_id"):
conditions.append(VWEDTaskRecord.def_id == data["task_id"])
# 任务实例ID条件
if data.get("task_record_id"):
conditions.append(VWEDTaskRecord.id == data["task_record_id"])
# 状态条件
if data.get("status"):
conditions.append(VWEDTaskRecord.status == int(data["status"]))
# 机器人ID条件
if data.get("agv_id"):
conditions.append(VWEDTaskRecord.agv_id == data["agv_id"])
# 时间范围条件
if data.get("start_date"):
start_time = datetime.strptime(data["start_date"], "%Y-%m-%d %H:%M:%S")
conditions.append(VWEDTaskRecord.created_at >= start_time)
if data.get("end_date"):
end_time = datetime.strptime(data["end_date"], "%Y-%m-%d %H:%M:%S")
conditions.append(VWEDTaskRecord.created_at <= end_time)
# 执行查询
query = select(VWEDTaskRecord)
if conditions:
query = query.where(and_(*conditions))
result = await session.execute(query)
task_records = result.scalars().all()
if not task_records:
return "[]"
# 构建返回数据列表
task_list = []
for task_record in task_records:
task_info = {
"id": task_record.id,
"def_id": task_record.def_id,
"def_label": task_record.def_label,
"status": task_record.status,
"created_on": task_record.created_at.strftime("%Y-%m-%d %H:%M:%S") if task_record.created_at else None,
"ended_reason": task_record.ended_reason or "运行结束",
"ended_on": task_record.ended_on.strftime("%Y-%m-%d %H:%M:%S") if task_record.ended_on else None,
"state_description": task_record.state_description or "",
}
task_list.append(task_info)
return json.dumps(task_list, ensure_ascii=False)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 查询任务实例异常: {str(e)}")
raise e
# def receive_third_order(self, order_param: str) -> bool:
# """接收并持久化第三方系统任务请求
# Args:
# order_param: JSON字符串第三方请求参数
# Returns:
# bool: True表示成功False表示失败
# """
# try:
# # 这里需要实现第三方订单表的创建和插入逻辑
# # 暂时返回True表示接收成功
# self.logger.info(f"脚本 {self.script_id} 接收第三方订单: {order_param}")
# return True
# except Exception as e:
# self.logger.error(f"脚本 {self.script_id} 接收第三方订单异常: {str(e)}")
# return False
def save_task_log(self, log_param: str) -> None:
"""保存任务运行时日志
新增RDS任务实例的运行日志该日志将显示在任务实例的监控界面中。
Args:
log_param: JSON字符串包含日志信息
"""
try:
data = safe_parse_dict(log_param, self.script_id)
if data is None:
return
# 记录到系统日志
level = data.get("level", 1)
message = data.get("message", "")
task_id = data.get("task_id", "")
task_record_id = data.get("task_record_id", "")
block_id = data.get("block_id", "")
log_msg = f"任务日志 [任务ID:{task_id}] [实例ID:{task_record_id}] [块ID:{block_id}] {message}"
if level == 1: # 正常运行
self.logger.info(log_msg)
elif level == 2: # 正常停止
self.logger.warning(log_msg)
elif level == 3: # 异常中断
self.logger.error(log_msg)
else:
self.logger.info(log_msg)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 保存任务日志异常: {str(e)}")
async def sync_create_wind_task(self, task_param: str) -> str:
"""创建并运行一个任务
本方法是阻塞方法,可以在脚本中创建一个天风任务的实例,并在任务执行结束后返回。
Args:
task_param: JSON字符串包含任务创建参数
Returns:
str: "9013"表示任务正常结束,"9014"表示任务异常结束
"""
try:
# 使用create_wind_task的逻辑创建任务
result_json = await self.create_wind_task(task_param)
result = json.loads(result_json)
if result.get("code") != 200:
return "9014"
# 解析参数获取task_record_id用于监控
data = safe_parse_dict(task_param, self.script_id)
if data is None:
return "9014"
task_record_id = data.get("task_record_id")
if not task_record_id:
self.logger.warning(f"脚本 {self.script_id} 无法获取task_record_id无法监控任务状态")
return "9014"
# 等待任务完成
return await self._wait_for_task_completion(task_record_id)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 同步创建任务异常: {str(e)}")
return "9014"
async def _wait_for_task_completion(self, task_record_id: str, check_interval: int = 1, timeout: int = 3600) -> str:
"""等待任务完成
Args:
task_record_id: 任务记录ID
check_interval: 检查间隔(秒)
timeout: 超时时间(秒)
Returns:
str: "9013"表示任务正常结束,"9014"表示任务异常结束
"""
import asyncio
from data.enum.task_record_enum import TaskStatus
start_time = asyncio.get_event_loop().time()
while True:
try:
# 检查是否超时
if asyncio.get_event_loop().time() - start_time > timeout:
self.logger.warning(f"脚本 {self.script_id} 等待任务完成超时: {task_record_id}")
return "9014"
# 获取任务状态
status_result = await self.get_task_status(task_record_id)
if not status_result.get("success"):
self.logger.error(f"脚本 {self.script_id} 获取任务状态失败: {task_record_id}")
return "9014"
task_data = status_result.get("data", {})
status = task_data.get("status")
# 检查任务状态
if status == TaskStatus.COMPLETED:
self.logger.info(f"脚本 {self.script_id} 任务正常完成: {task_record_id}")
return "9013"
elif status in [TaskStatus.FAILED, TaskStatus.CANCELED]:
self.logger.warning(f"脚本 {self.script_id} 任务异常结束: {task_record_id}, status: {status}")
return "9014"
elif status in [TaskStatus.RUNNING, TaskStatus.QUEUED, TaskStatus.PAUSED]:
# 任务还在执行中,继续等待
self.logger.debug(f"脚本 {self.script_id} 任务执行中: {task_record_id}, status: {status}")
await asyncio.sleep(check_interval)
else:
# 未知状态
self.logger.warning(f"脚本 {self.script_id} 任务状态未知: {task_record_id}, status: {status}")
return "9014"
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 监控任务状态异常: {task_record_id}, 错误: {str(e)}")
return "9014"
async def terminate_tasks(self, param: str) -> None:
"""终止任务
本方法是非阻塞方法,可用于终止多个正在运行的天风任务实例。
Args:
param: JSON字符串包含要终止的任务实例ID列表
"""
try:
data = safe_parse_list(param, self.script_id)
if data is None:
return
# 获取认证token
token = await self._get_auth_token(auto_get_token=True)
for task_info in data:
task_record_id = task_info.get("task_record_id")
if task_record_id:
try:
# 1. 调用调度器取消本地任务
from services.enhanced_scheduler import scheduler
scheduler_result = await scheduler.cancel_task(task_record_id)
# 2. 调用天风系统接口设置任务为终止状态
from services.sync_service import set_task_terminated
tf_result = await set_task_terminated(task_record_id, token)
# 记录结果
if scheduler_result.get("success"):
self.logger.info(f"脚本 {self.script_id} 成功在调度器中终止任务: {task_record_id}")
else:
self.logger.warning(f"脚本 {self.script_id} 调度器中终止任务失败: {task_record_id}")
if tf_result and tf_result.get("success"):
self.logger.info(f"脚本 {self.script_id} 成功在天风系统中设置任务终止状态: {task_record_id}")
else:
self.logger.warning(f"脚本 {self.script_id} 天风系统中设置任务终止状态失败: {task_record_id}")
# 只要任一操作成功就认为整体成功
if scheduler_result.get("success") or (tf_result and tf_result.get("success")):
self.logger.info(f"脚本 {self.script_id} 任务终止操作完成: {task_record_id}")
else:
self.logger.error(f"脚本 {self.script_id} 任务终止操作全部失败: {task_record_id}")
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 终止任务异常: {task_record_id}, 错误: {str(e)}")
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 终止任务异常: {str(e)}")
raise e
async def get_children_task_record_id(self, record_id: str) -> str:
"""查询子任务的任务id列表
本方法是非阻塞方法可根据父任务的id查询所有子任务的id。
Args:
record_id: 父任务的record_id
Returns:
str: 包含子任务id的JSON字符串
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select
async with get_async_session() as session:
result = await session.execute(
select(VWEDTaskRecord.id).where(VWEDTaskRecord.parent_task_record_id == record_id)
)
child_ids = [row[0] for row in result.fetchall()]
return json.dumps(child_ids, ensure_ascii=False)
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 查询子任务ID列表异常: {str(e)}")
raise e
async def set_task_priority(self, task_record_id: str, priority: int) -> None:
"""设置任务优先级
本方法是非阻塞方法,可根据设置任务的优先级。
Args:
task_record_id: 任务的实例id
priority: 任务的优先级
Raises:
Exception: 当任务记录ID不存在时抛出异常
"""
try:
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from sqlalchemy import select, update
async with get_async_session() as session:
# 首先查询任务记录是否存在
result = await session.execute(
select(VWEDTaskRecord.id).where(VWEDTaskRecord.id == task_record_id)
)
task_record = result.scalar_one_or_none()
if not task_record:
error_msg = f"任务记录ID不存在: {task_record_id}"
self.logger.error(f"脚本 {self.script_id} {error_msg}")
raise Exception(error_msg)
# 更新任务优先级
await session.execute(
update(VWEDTaskRecord)
.where(VWEDTaskRecord.id == task_record_id)
.values(priority=priority)
)
await session.commit()
self.logger.info(f"脚本 {self.script_id} 设置任务优先级成功: {task_record_id} -> {priority}")
except Exception as e:
self.logger.error(f"脚本 {self.script_id} 设置任务优先级异常: {str(e)}")
raise e