784 lines
33 KiB
Python
784 lines
33 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
机器人调度处理器模块
|
||
提供与机器人调度操作相关的各种处理器
|
||
"""
|
||
|
||
import json
|
||
import asyncio
|
||
import aiohttp
|
||
import uuid
|
||
from typing import Dict, Any, List, Optional
|
||
from services.execution.task_context import TaskContext
|
||
from .base import BlockHandler, register_handler
|
||
from config.settings import settings
|
||
from utils.logger import get_logger
|
||
from .model.block_name import RobotBlockName
|
||
from typing import Tuple
|
||
|
||
# 获取日志记录器
|
||
logger = get_logger("services.execution.handlers.robot_scheduling")
|
||
|
||
# 提取公共的API调用函数
|
||
async def call_robot_api(api_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
调用机器人调度服务API的通用函数
|
||
|
||
Args:
|
||
api_name: API名称,对应API_ENDPOINTS中的键
|
||
params: API参数
|
||
|
||
Returns:
|
||
API响应结果
|
||
"""
|
||
# 获取API端点和方法
|
||
endpoint = settings.ROBOT_API_ENDPOINTS.get(api_name)
|
||
method = settings.ROBOT_API_METHODS.get(api_name)
|
||
|
||
if not endpoint or not method:
|
||
logger.error(f"未找到API端点或方法:{api_name}")
|
||
return {
|
||
"success": False,
|
||
"message": f"未找到API配置: {api_name}"
|
||
}
|
||
|
||
# 检查是否启用测试模式
|
||
if settings.ROBOT_API_MOCK_MODE:
|
||
logger.info(f"[测试模式] 模拟调用API: {api_name}, 参数: {params}")
|
||
# 构造测试模式下的响应
|
||
test_response = generate_mock_response(api_name, params)
|
||
return test_response
|
||
|
||
# 构建完整的URL
|
||
url = f"{settings.ROBOT_API_BASE_URL}{endpoint}"
|
||
|
||
# 准备请求头
|
||
headers = {"Content-Type": "application/json"}
|
||
if settings.ROBOT_API_TOKEN:
|
||
headers["Authorization"] = f"Bearer {settings.ROBOT_API_TOKEN}"
|
||
|
||
logger.info(f"调用外部API {api_name} - {method} {url}, 参数: {params}")
|
||
|
||
try:
|
||
async with aiohttp.ClientSession() as session:
|
||
# 根据HTTP方法选择相应的请求方式
|
||
if method == "GET":
|
||
# 对于GET请求,将params转换为URL参数
|
||
async with session.get(
|
||
url,
|
||
params=params,
|
||
headers=headers,
|
||
timeout=settings.ROBOT_API_TIMEOUT
|
||
) as response:
|
||
result = await response.json()
|
||
elif method == "POST":
|
||
# 对于POST请求,将params作为JSON数据发送
|
||
async with session.post(
|
||
url,
|
||
json=params,
|
||
headers=headers,
|
||
timeout=settings.ROBOT_API_TIMEOUT
|
||
) as response:
|
||
result = await response.json()
|
||
elif method == "PUT":
|
||
# 对于PUT请求,将params作为JSON数据发送
|
||
async with session.put(
|
||
url,
|
||
json=params,
|
||
headers=headers,
|
||
timeout=settings.ROBOT_API_TIMEOUT
|
||
) as response:
|
||
result = await response.json()
|
||
else:
|
||
logger.error(f"不支持的HTTP方法: {method}")
|
||
return {
|
||
"success": False,
|
||
"message": f"不支持的HTTP方法: {method}"
|
||
}
|
||
|
||
# 检查响应状态码
|
||
if response.status != 200:
|
||
logger.error(f"API调用失败: {url}, 状态码: {response.status}, 响应: {result}")
|
||
return {
|
||
"success": False,
|
||
"message": f"API调用失败, 状态码: {response.status}",
|
||
"data": result
|
||
}
|
||
|
||
logger.info(f"API调用成功: {url}, 响应: {result}")
|
||
return result
|
||
|
||
except aiohttp.ClientError as e:
|
||
logger.error(f"API调用客户端错误: {url}, 错误: {str(e)}")
|
||
return {
|
||
"success": False,
|
||
"message": f"API调用客户端错误: {str(e)}"
|
||
}
|
||
except asyncio.TimeoutError:
|
||
logger.error(f"API调用超时: {url}")
|
||
return {
|
||
"success": False,
|
||
"message": "API调用超时"
|
||
}
|
||
except json.JSONDecodeError:
|
||
logger.error(f"API响应解析失败: {url}")
|
||
return {
|
||
"success": False,
|
||
"message": "API响应格式错误,无法解析JSON"
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"API调用异常: {url}, 错误: {str(e)}")
|
||
return {
|
||
"success": False,
|
||
"message": f"API调用异常: {str(e)}"
|
||
}
|
||
|
||
def generate_mock_response(api_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
生成测试模式下的模拟响应
|
||
|
||
Args:
|
||
api_name: API名称
|
||
params: API参数
|
||
|
||
Returns:
|
||
模拟的API响应
|
||
"""
|
||
# 基本响应结构
|
||
response = {
|
||
"success": True,
|
||
"message": f"[测试模式] {api_name} 操作成功",
|
||
"data": {}
|
||
}
|
||
|
||
# 根据不同API返回不同的模拟数据
|
||
if api_name == "agv_operation":
|
||
# 机器人通用动作
|
||
response["data"] = {
|
||
"success": True,
|
||
"taskId": f"MOCK-OP-{str(uuid.uuid4())[:8]}"
|
||
}
|
||
elif api_name == "select_agv":
|
||
# 选择执行机器人
|
||
response["data"] = {
|
||
"agvId": f"MOCK-AGV-{str(uuid.uuid4())[:8]}"
|
||
}
|
||
elif api_name == "vehicle_station":
|
||
# 获取机器人位置
|
||
response["data"] = {
|
||
"station": f"MOCK-STATION-{str(uuid.uuid4())[:5]}",
|
||
"lastStation": f"MOCK-LAST-STATION-{str(uuid.uuid4())[:5]}"
|
||
}
|
||
elif api_name == "get_battery_level":
|
||
# 获取机器人电量
|
||
response["data"] = {
|
||
"batteryLevel": float(f"{(uuid.uuid4().int % 100) / 100:.2f}")
|
||
}
|
||
elif api_name == "get_pgv_code":
|
||
# 获取机器人PGV码
|
||
response["data"] = {
|
||
"codeInfo": True if uuid.uuid4().int % 2 == 0 else False
|
||
}
|
||
|
||
# 记录模拟调用结果
|
||
logger.info(f"[测试模式] 生成模拟响应: {api_name}, 响应: {response}")
|
||
return response
|
||
|
||
class RobotBlockHandler(BlockHandler):
|
||
"""机器人调度处理器基类,提供公共的API调用方法"""
|
||
|
||
async def _call_external_api(self, api_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""调用外部API的通用方法"""
|
||
return await call_robot_api(api_name, params)
|
||
|
||
def _analyze_affected_blocks(self, block: Dict[str, Any], current_block_id: str, current_block_name: str) -> List[Dict[str, Any]]:
|
||
"""
|
||
分析当前块的结构,找出受当前选择机器人块影响的所有下级块
|
||
|
||
Args:
|
||
block: 当前块的定义
|
||
current_block_id: 当前块的ID
|
||
current_block_name: 当前块的名称
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: 受影响的块列表,每个元素包含块的ID、名称、类型和与当前块的关系
|
||
"""
|
||
affected_blocks = []
|
||
|
||
# 检查当前块是否有子块
|
||
if "children" in block and "default" in block["children"]:
|
||
children = block["children"]["default"]
|
||
logger.info(f"块 {current_block_name}(ID:{current_block_id}) 有 {len(children)} 个子块")
|
||
|
||
# 分析每个子块
|
||
for child in children:
|
||
child_id = child.get("id", "unknown")
|
||
child_name = child.get("name", f"b{child_id}")
|
||
child_type = child.get("blockType", "unknown")
|
||
|
||
# 记录子块信息
|
||
affected_blocks.append({
|
||
"id": child_id,
|
||
"name": child_name,
|
||
"type": child_type,
|
||
"relation": "direct_child",
|
||
"parent_id": current_block_id,
|
||
"parent_name": current_block_name
|
||
})
|
||
|
||
# 如果子块不是CSelectAgvBp类型,则继续分析其子块
|
||
# 如果子块是CSelectAgvBp类型,则停止分析该分支,因为该分支将由子块的CSelectAgvBp负责
|
||
if child_type != "CSelectAgvBp" and "children" in child and "default" in child["children"]:
|
||
# 递归分析子块的子块
|
||
nested_affected = self._analyze_affected_blocks(child, child_id, child_name)
|
||
|
||
# 将所有嵌套子块添加到结果中
|
||
for nested_block in nested_affected:
|
||
# 添加到结果列表中,标记为嵌套关系
|
||
nested_block["relation"] = "nested_child"
|
||
affected_blocks.append(nested_block)
|
||
|
||
return affected_blocks
|
||
|
||
def _get_robot_id_for_block(self, block_id: str, block_name: str, context: TaskContext) -> Tuple[Optional[str], Optional[str]]:
|
||
"""
|
||
获取适用于当前块的机器人ID
|
||
|
||
首先检查是否有专门为该块设置的机器人ID变量
|
||
如果没有则尝试获取全局机器人ID
|
||
|
||
Args:
|
||
block_id: 当前块ID
|
||
block_name: 当前块名称
|
||
context: 任务上下文
|
||
|
||
Returns:
|
||
Optional[str]: 机器人ID,如果没有找到则返回None
|
||
"""
|
||
# 首先检查是否有专门为该块设置的机器人ID
|
||
robot_id = context.get_variable(f"agv_for_block_{block_id}")
|
||
agv_task_id = context.get_variable(f"agv_task_id_{block_id}")
|
||
if robot_id:
|
||
logger.info(f"找到块 {block_name}(ID:{block_id}) 专用的机器人ID: {robot_id}")
|
||
return robot_id, agv_task_id
|
||
|
||
# 检查是否有按名称设置的机器人ID
|
||
robot_id = context.get_variable(f"agv_for_{block_name}")
|
||
if robot_id:
|
||
logger.info(f"找到块 {block_name} 专用的机器人ID: {robot_id}")
|
||
return robot_id, agv_task_id
|
||
|
||
# 如果没有专用设置,尝试获取全局机器人ID
|
||
robot_id = context.get_variable("selectedAgvId")
|
||
if robot_id:
|
||
logger.info(f"使用全局机器人ID: {robot_id} 用于块 {block_name}(ID:{block_id})")
|
||
return robot_id, agv_task_id
|
||
|
||
# 如果仍未找到,尝试从所有块输出中查找最近的selectedAgvId
|
||
# 这是兜底策略,保持向后兼容
|
||
for out_block_name, outputs in context.block_outputs.items():
|
||
if isinstance(outputs, dict) and "selectedAgvId" in outputs:
|
||
robot_id = outputs.get("selectedAgvId")
|
||
logger.info(f"从块 {out_block_name} 输出中获取机器人ID: {robot_id} 用于块 {block_name}(ID:{block_id})")
|
||
return robot_id, agv_task_id
|
||
|
||
# 未找到任何机器人ID
|
||
logger.warning(f"未找到块 {block_name}(ID:{block_id}) 可用的机器人ID")
|
||
return None, None
|
||
|
||
async def _update_task_record_agv_id(self, task_record_id: str, amr_id: str) -> None:
|
||
"""
|
||
更新任务记录中的agv_id字段
|
||
|
||
Args:
|
||
task_record_id: 任务记录ID
|
||
amr_id: 机器人ID
|
||
|
||
Returns:
|
||
None
|
||
"""
|
||
try:
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from data.session import get_async_session
|
||
from data.models.taskrecord import VWEDTaskRecord
|
||
from sqlalchemy import select, update
|
||
|
||
if not amr_id:
|
||
logger.warning(f"未提供AMR ID,无法更新任务记录 {task_record_id}")
|
||
return
|
||
|
||
# 将多个AMR ID用逗号连接
|
||
logger.info(f"准备更新任务 {task_record_id} 的机器人ID: {amr_id}")
|
||
|
||
# 先查询当前任务记录中是否已有agv_id
|
||
async with get_async_session() as session:
|
||
session: AsyncSession = session
|
||
|
||
# 查询当前记录
|
||
stmt = select(VWEDTaskRecord.agv_id).where(VWEDTaskRecord.id == task_record_id)
|
||
result = await session.execute(stmt)
|
||
current_agv_id = result.scalar_one_or_none()
|
||
|
||
# 确定最终要存储的agv_id值
|
||
if current_agv_id:
|
||
# 如果已经有值,需要合并并去重
|
||
logger.info(f"任务 {task_record_id} 当前已存在的机器人IDs: {current_agv_id}")
|
||
current_ids = current_agv_id.split(",")
|
||
all_ids = current_ids + [amr_id]
|
||
# 去重并过滤空值
|
||
unique_ids = list(set([id for id in all_ids if id]))
|
||
final_agv_id = ",".join(unique_ids)
|
||
logger.info(f"合并后的机器人IDs: {final_agv_id}")
|
||
else:
|
||
final_agv_id = amr_id
|
||
|
||
# 更新记录
|
||
stmt = update(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id).values(agv_id=final_agv_id)
|
||
await session.execute(stmt)
|
||
await session.commit()
|
||
|
||
logger.info(f"成功更新任务记录 {task_record_id} 的agv_id字段: {final_agv_id}")
|
||
except Exception as e:
|
||
logger.error(f"更新任务记录 {task_record_id} 的agv_id字段时发生错误: {str(e)}")
|
||
|
||
|
||
# 机器人通用动作处理器
|
||
@register_handler(RobotBlockName.AGV_OPERATION)
|
||
class AgvOperationBlockHandler(RobotBlockHandler):
|
||
"""机器人通用动作处理器"""
|
||
|
||
async def execute(
|
||
self,
|
||
block: Dict[str, Any],
|
||
input_params: Dict[str, Any],
|
||
context: TaskContext
|
||
) -> Dict[str, Any]:
|
||
"""执行机器人通用动作操作"""
|
||
from services.sync_service import wait_for_task_block_action_completion
|
||
try:
|
||
# 获取关键参数用于验证
|
||
target_site_label = input_params.get("targetSiteLabel")
|
||
script_name = input_params.get("scriptName")
|
||
# 参数检查
|
||
if not target_site_label:
|
||
result = {
|
||
"success": False,
|
||
"message": "目标站点名不能为空"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取当前块信息
|
||
current_block_id = block.get("id", "unknown")
|
||
current_block_name = block.get("name", f"b{current_block_id}")
|
||
|
||
# 如果没有提供,尝试获取适用于当前块的机器人ID
|
||
vehicle, agv_task_id = self._get_robot_id_for_block(current_block_id, current_block_name, context)
|
||
if vehicle:
|
||
# 设置到输入参数中
|
||
input_params["vehicle"] = vehicle
|
||
|
||
# 记录使用的机器人ID
|
||
if vehicle:
|
||
logger.info(f"执行机器人通用动作,块 {current_block_name}(ID:{current_block_id}) 使用机器人: {vehicle}, 目标站点: {target_site_label}")
|
||
else:
|
||
error_msg = f"执行机器人通用动作失败:未指定机器人ID,目标站点: {target_site_label}"
|
||
logger.error(error_msg)
|
||
result = {
|
||
"success": False,
|
||
"message": error_msg
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
from services.sync_service import add_action
|
||
result = await add_action(
|
||
task_id=agv_task_id,
|
||
station_name=target_site_label,
|
||
action=script_name,
|
||
token=context.token
|
||
)
|
||
# 调用外部API执行机器人通用动作
|
||
if result.get("success", False):
|
||
# 获取任务ID
|
||
task_id = result.get("result", {}).get("id", "")
|
||
task_block_result = await wait_for_task_block_action_completion(task_id, context.token)
|
||
if task_block_result.get("success", False):
|
||
task_block_status = task_block_result.get("result", {}).get("status", "")
|
||
if task_block_status == 3:
|
||
result["message"] = f"机器人通用动作成功,目标站点: {target_site_label}"
|
||
elif task_block_status == 4:
|
||
result["message"] = f"机器人通用动作失败,目标站点: {target_site_label}:{task_block_result.get('message', '')}"
|
||
result["success"] = False
|
||
elif task_block_status == 5:
|
||
result["message"] = f"机器人通用动作终止,目标站点: {target_site_label}"
|
||
else:
|
||
result["message"] = f"机器人通用动作失败: {result.get('message', '未知错误')}"
|
||
|
||
# 记录执行结果
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
except Exception as e:
|
||
result = {
|
||
"success": False,
|
||
"message": f"机器人通用动作执行异常: {str(e)}"
|
||
}
|
||
# 记录异常
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取机器人位置处理器
|
||
@register_handler(RobotBlockName.VEHICLE_STATION)
|
||
class VehicleStationBlockHandler(RobotBlockHandler):
|
||
"""获取机器人位置处理器"""
|
||
|
||
async def execute(
|
||
self,
|
||
block: Dict[str, Any],
|
||
input_params: Dict[str, Any],
|
||
context: TaskContext
|
||
) -> Dict[str, Any]:
|
||
"""执行获取机器人位置操作"""
|
||
try:
|
||
# 获取关键参数用于验证
|
||
vehicle = input_params.get("vehicle")
|
||
|
||
# 参数检查
|
||
if not vehicle:
|
||
result = {
|
||
"success": False,
|
||
"message": "指定机器人不能为空"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 调用外部API获取机器人位置
|
||
result = await self._call_external_api("vehicle_station", input_params)
|
||
|
||
if result.get("success", False):
|
||
# 获取站点信息
|
||
station = result.get("data", {}).get("station", "")
|
||
last_station = result.get("data", {}).get("lastStation", "")
|
||
|
||
# 设置上下文变量
|
||
context.set_variable("station", station)
|
||
context.set_variable("lastStation", last_station)
|
||
context.set_block_output(block.get("name"), {
|
||
"station": station,
|
||
"lastStation": last_station
|
||
})
|
||
|
||
result["message"] = f"获取机器人 {vehicle} 位置成功,当前位置: {station}, 上次位置: {last_station}"
|
||
else:
|
||
result["message"] = f"获取机器人位置失败: {result.get('message', '未知错误')}"
|
||
|
||
# 记录执行结果
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
except Exception as e:
|
||
result = {
|
||
"success": False,
|
||
"message": f"获取机器人位置执行异常: {str(e)}"
|
||
}
|
||
# 记录异常
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取机器人电量处理器
|
||
@register_handler(RobotBlockName.GET_BATTERY_LEVEL)
|
||
class GetBatteryLevelBlockHandler(RobotBlockHandler):
|
||
"""获取机器人电量处理器"""
|
||
|
||
async def execute(
|
||
self,
|
||
block: Dict[str, Any],
|
||
input_params: Dict[str, Any],
|
||
context: TaskContext
|
||
) -> Dict[str, Any]:
|
||
"""执行获取机器人电量操作"""
|
||
try:
|
||
# 获取关键参数用于验证
|
||
vehicle = input_params.get("vehicle")
|
||
|
||
# 参数检查
|
||
if not vehicle:
|
||
result = {
|
||
"success": False,
|
||
"message": "机器人ID不能为空"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 调用外部API获取机器人电量
|
||
result = await self._call_external_api("get_battery_level", input_params)
|
||
|
||
if result.get("success", False):
|
||
# 获取电量信息
|
||
battery_level = result.get("data", {}).get("batteryLevel", 0.0)
|
||
|
||
# 设置上下文变量
|
||
context.set_variable("batteryLevel", battery_level)
|
||
context.set_block_output(block.get("name"), {"batteryLevel": battery_level})
|
||
|
||
# 格式化电量为百分比
|
||
battery_percent = f"{battery_level * 100:.1f}%"
|
||
result["message"] = f"获取机器人 {vehicle} 电量成功,当前电量: {battery_percent}"
|
||
else:
|
||
result["message"] = f"获取机器人电量失败: {result.get('message', '未知错误')}"
|
||
|
||
# 记录执行结果
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
except Exception as e:
|
||
result = {
|
||
"success": False,
|
||
"message": f"获取机器人电量执行异常: {str(e)}"
|
||
}
|
||
# 记录异常
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取机器人PGV码处理器
|
||
@register_handler(RobotBlockName.GET_PGV_CODE)
|
||
class GetPGVCodeBlockHandler(RobotBlockHandler):
|
||
"""获取机器人PGV码处理器"""
|
||
|
||
async def execute(
|
||
self,
|
||
block: Dict[str, Any],
|
||
input_params: Dict[str, Any],
|
||
context: TaskContext
|
||
) -> Dict[str, Any]:
|
||
"""执行获取机器人PGV码操作"""
|
||
try:
|
||
# 获取关键参数用于验证
|
||
vehicle = input_params.get("vehicle")
|
||
|
||
# 参数检查
|
||
if not vehicle:
|
||
result = {
|
||
"success": False,
|
||
"message": "机器人ID不能为空"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 调用外部API获取机器人PGV码
|
||
result = await self._call_external_api("get_pgv_code", input_params)
|
||
|
||
if result.get("success", False):
|
||
# 获取PGV码信息
|
||
code_info = result.get("data", {}).get("codeInfo", False)
|
||
|
||
# 设置上下文变量
|
||
context.set_variable("codeInfo", code_info)
|
||
context.set_block_output(block.get("name"), {"codeInfo": code_info})
|
||
|
||
code_status = "有效" if code_info else "无效"
|
||
result["message"] = f"获取机器人 {vehicle} PGV码成功,二维码信息: {code_status}"
|
||
else:
|
||
result["message"] = f"获取机器人PGV码失败: {result.get('message', '未知错误')}"
|
||
|
||
# 记录执行结果
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
except Exception as e:
|
||
result = {
|
||
"success": False,
|
||
"message": f"获取机器人PGV码执行异常: {str(e)}"
|
||
}
|
||
# 记录异常
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 选择执行机器人处理器
|
||
@register_handler(RobotBlockName.SELECT_AGV)
|
||
class SelectAgvBlockHandler(RobotBlockHandler):
|
||
"""选择执行机器人处理器"""
|
||
|
||
async def execute(
|
||
self,
|
||
block: Dict[str, Any],
|
||
input_params: Dict[str, Any],
|
||
context: TaskContext
|
||
) -> Dict[str, Any]:
|
||
"""执行选择机器人操作"""
|
||
try:
|
||
# 获取关键参数用于验证
|
||
key_route = input_params.get("keyRoute")
|
||
priority = input_params.get("priority", 1)
|
||
if isinstance(priority, str):
|
||
priority = int(priority)
|
||
|
||
# 参数检查
|
||
if not key_route:
|
||
result = {
|
||
"success": False,
|
||
"message": "关键路径不能为空"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 调用外部API选择执行机器人
|
||
# result = await self._call_external_api("select_agv", input_params)
|
||
from services.sync_service import create_choose_amr_task, wait_for_amr_selection
|
||
result = await create_choose_amr_task(
|
||
task_id=context.task_record_id,
|
||
key_station_name=key_route,
|
||
amr_name=input_params.get("vehicle", ""),
|
||
amr_group_name=input_params.get("group", ""),
|
||
token=context.token,
|
||
priority=priority
|
||
)
|
||
if result.get("success", False):
|
||
# 获取任务块ID
|
||
task_block_id = result.get("result", {}).get("id", "")
|
||
if not task_block_id:
|
||
result = {
|
||
"success": False,
|
||
"message": "创建选择AMR任务成功,但未返回任务块ID"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
logger.info(f"开始等待任务块 {task_block_id} 的AMR选择结果")
|
||
|
||
# 等待AMR选择完成
|
||
task_block_result = await wait_for_amr_selection(
|
||
task_block_id=task_block_id,
|
||
token=context.token
|
||
)
|
||
|
||
if not task_block_result or not task_block_result.get("success", False):
|
||
result = {
|
||
"success": False,
|
||
"message": f"等待AMR选择结果失败或超时,任务块ID: {task_block_id}"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取选出的机器人ID
|
||
agv_id = task_block_result.get("result", {}).get("amrId", "")
|
||
if not agv_id:
|
||
result = {
|
||
"success": False,
|
||
"message": f"未能获取到选择的AMR ID,任务块ID: {task_block_id}"
|
||
}
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
|
||
# 获取当前块ID和名称
|
||
current_block_id = block.get("id", "unknown")
|
||
current_block_name = block.get("name", f"b{current_block_id}")
|
||
# 更新任务记录中的agv_id字段
|
||
await self._update_task_record_agv_id(context.task_record_id, agv_id)
|
||
|
||
logger.info(f"选择机器人块 {current_block_name}(ID:{current_block_id}) 选择的机器人: {agv_id}")
|
||
|
||
# 分析块的层级结构并记录关联关系
|
||
affected_blocks = self._analyze_affected_blocks(block, current_block_id, current_block_name)
|
||
|
||
# 将分析结果记录到日志
|
||
logger.info(f"选择机器人块 {current_block_name} 影响的块ID: {[b['id'] for b in affected_blocks]}")
|
||
context.set_block_output(current_block_name, {"selectedAgvId": agv_id})
|
||
|
||
# 为每个受影响的块设置变量,记录它应该使用的机器人ID
|
||
for affected_block in affected_blocks:
|
||
affected_id = affected_block["id"]
|
||
affected_name = affected_block["name"]
|
||
context.set_variable(f"agv_for_block_{affected_id}", agv_id)
|
||
context.set_variable(f"agv_for_{affected_name}", agv_id)
|
||
context.set_variable(f"agv_task_id_{affected_id}", task_block_id)
|
||
context.set_block_output(affected_name, {"selectedAgvId": agv_id})
|
||
|
||
# 构造成功消息
|
||
vehicle = input_params.get("vehicle", "")
|
||
group = input_params.get("group", "")
|
||
tag = input_params.get("tag", "")
|
||
|
||
if vehicle:
|
||
result["message"] = f"指定机器人 {vehicle} 选择成功"
|
||
elif group:
|
||
result["message"] = f"从机器人组 {group} 选择机器人成功: {agv_id}"
|
||
elif tag:
|
||
result["message"] = f"根据标签 {tag} 选择机器人成功: {agv_id}"
|
||
else:
|
||
result["message"] = f"选择执行机器人成功: {agv_id}"
|
||
|
||
# 打印选择结果和影响的块
|
||
logger.info(f"选择机器人块 {current_block_name}(ID:{current_block_id}) 选择的机器人: {agv_id}")
|
||
logger.info(f"影响的块: {len(affected_blocks)} 个")
|
||
for i, b in enumerate(affected_blocks):
|
||
logger.info(f"{i+1}. {b['name']}(ID:{b['id']}, 类型:{b['type']}, 关系:{b['relation']})")
|
||
|
||
from services.execution.block_executor import BlockExecutor
|
||
executor = BlockExecutor(context)
|
||
|
||
# 检查是否有子块需要执行
|
||
has_children = "children" in block and "default" in block.get("children", {}) and len(block.get("children", {}).get("default", [])) > 0
|
||
|
||
if has_children:
|
||
# 执行子块
|
||
logger.info(f"开始执行选择机器人块 {current_block_name} 的子块")
|
||
loop_result = await executor.execute_children(block, "default")
|
||
|
||
# 处理子块执行结果
|
||
if loop_result.get("success", False):
|
||
# 子块执行成功,合并结果
|
||
logger.info(f"选择机器人块 {current_block_name} 的子块执行成功")
|
||
|
||
# 如果有需要合并的输出数据
|
||
child_results = loop_result.get("output", {}).get("results", [])
|
||
result["childBlockResults"] = child_results
|
||
|
||
# 保持成功状态
|
||
result["success"] = True
|
||
result["output"] = {
|
||
"selectedAgvId": agv_id,
|
||
"affectedBlocks": affected_blocks,
|
||
"childrenExecuted": True,
|
||
"childrenResult": loop_result
|
||
}
|
||
|
||
# 如果原始消息中没有包含子块执行信息,添加这部分信息
|
||
if "子块" not in result["message"]:
|
||
result["message"] = f"{result['message']},子块执行成功"
|
||
else:
|
||
# 子块执行失败,根据失败的子块更新消息
|
||
logger.error(f"选择机器人块 {current_block_name} 的子块执行失败: {loop_result.get('message')}")
|
||
|
||
# 创建包含子块失败信息的结果
|
||
error_msg = loop_result.get("message", "未知错误")
|
||
failed_block_id = loop_result.get("block_id", "unknown")
|
||
|
||
result = {
|
||
"success": False,
|
||
"message": f"选择执行机器人成功,但子块执行失败: {error_msg},失败块ID: {failed_block_id}",
|
||
"output": {
|
||
"selectedAgvId": agv_id,
|
||
"affectedBlocks": affected_blocks,
|
||
"childrenExecuted": False,
|
||
"childrenResult": loop_result
|
||
}
|
||
}
|
||
# 记录执行结果
|
||
from services.sync_service import closure_task
|
||
closure_result = await closure_task(
|
||
task_id=task_block_id,
|
||
token=context.token
|
||
)
|
||
else:
|
||
result["message"] = f"选择执行机器人失败: {result.get('message', '未知错误')}"
|
||
|
||
await self._record_task_log(block, result, context)
|
||
return result
|
||
except Exception as e:
|
||
result = {
|
||
"success": False,
|
||
"message": f"选择执行机器人异常: {str(e)}"
|
||
}
|
||
# 记录异常
|
||
await self._record_task_log(block, result, context)
|
||
return result |