#!/usr/bin/env python # -*- coding: utf-8 -*- """ VWED.robot 模块 - 机器人相关功能 """ import json import asyncio from typing import Dict, Any, Optional, List from utils.logger import get_logger from utils.json_parser import safe_parse_dict class VWEDRobotModule: """VWED.robot 模块 - 机器人相关功能""" def __init__(self, script_id: str): self.script_id = script_id self.logger = get_logger(f"script.{script_id}.robot") 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 is_robot_exist(self, robot_name: str) -> bool: """校验机器人是否存在 Args: robot_name: 机器人名称 Returns: bool: True表示存在,False表示不存在 """ try: # 获取认证token token = await self._get_auth_token() # 调用获取AMR列表接口 from services.sync_service import get_amr_list response = await get_amr_list(token) if response and response.get("success") and response.get("result"): # 检查机器人名称是否在列表中 amr_list = response.get("result", {}).get("records", []) for amr in amr_list: if amr.get("name") == robot_name: self.logger.info(f"脚本 {self.script_id} 机器人存在: {robot_name}") return True self.logger.info(f"脚本 {self.script_id} 机器人不存在: {robot_name}") return False else: self.logger.warning(f"脚本 {self.script_id} 获取机器人信息失败") return False except Exception as e: self.logger.error(f"脚本 {self.script_id} 校验机器人是否存在异常: {str(e)}") return False async def get_lite_robots_status(self) -> str: """获取机器人基本信息 Returns: str: JSON格式的机器人信息列表 """ try: # 获取认证token token = await self._get_auth_token() # 调用获取AMR列表接口 from services.sync_service import get_amr_list response = await get_amr_list(token) if response and response.get("success") and response.get("result"): amr_list = response.get("result", {}).get("records", []) # 转换所有字段为蛇形命名 robots_info = [] for amr in amr_list: robot_info = { "id": amr.get("id", ""), "tenant_id": amr.get("tenantId", ""), "brand_id": amr.get("brandId", ""), "type_id": amr.get("typeId", ""), "ip_address": amr.get("ipAddress", ""), "name": amr.get("name", ""), "scene_id": amr.get("sceneId", ""), "group_id": amr.get("groupId", ""), "online_station_id": amr.get("onlineStationId", ""), "is_online": amr.get("isOnline", 0), "is_simulation": amr.get("isSimulation", 0), "is_available": amr.get("isAvailable", 0), "is_accept_task": amr.get("isAcceptTask", 0), "length": amr.get("length", 0.0), "width": amr.get("width", 0.0), "min_power": amr.get("minPower", 0), "max_power": amr.get("maxPower", 0), "charge_power": amr.get("chargePower", 0), "task_power": amr.get("taskPower", 0), "exchange_power": amr.get("exchangePower", 0), "amr_status": amr.get("amrStatus", 0), "create_by": amr.get("createBy", ""), "create_time": amr.get("createTime", "") } robots_info.append(robot_info) result = json.dumps(robots_info, ensure_ascii=False) self.logger.info(f"脚本 {self.script_id} 成功获取机器人信息,共 {len(robots_info)} 个机器人") return result else: self.logger.warning(f"脚本 {self.script_id} 获取机器人信息失败") return json.dumps([], ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 获取机器人信息异常: {str(e)}") return json.dumps([], ensure_ascii=False) async def get_lite_robots_status_by_id(self, agv_id: str) -> str: """根据ID获取机器人基本信息 Args: agv_id: 机器人ID Returns: str: JSON格式的机器人信息 """ try: # 获取认证token token = await self._get_auth_token() # 调用获取AMR列表接口 from services.sync_service import get_amr_list response = await get_amr_list(token) if response and response.get("success") and response.get("result"): amr_list = response.get("result", {}).get("records", []) # 查找指定ID的机器人 for amr in amr_list: if amr.get("id") == agv_id or amr.get("name") == agv_id: robot_info = { "id": amr.get("id", ""), "tenant_id": amr.get("tenantId", ""), "brand_id": amr.get("brandId", ""), "type_id": amr.get("typeId", ""), "ip_address": amr.get("ipAddress", ""), "name": amr.get("name", ""), "scene_id": amr.get("sceneId", ""), "group_id": amr.get("groupId", ""), "online_station_id": amr.get("onlineStationId", ""), "is_online": amr.get("isOnline", 0), "is_simulation": amr.get("isSimulation", 0), "is_available": amr.get("isAvailable", 0), "is_accept_task": amr.get("isAcceptTask", 0), "length": amr.get("length", 0.0), "width": amr.get("width", 0.0), "min_power": amr.get("minPower", 0), "max_power": amr.get("maxPower", 0), "charge_power": amr.get("chargePower", 0), "task_power": amr.get("taskPower", 0), "exchange_power": amr.get("exchangePower", 0), "amr_status": amr.get("amrStatus", 0), "create_by": amr.get("createBy", ""), "create_time": amr.get("createTime", "") } result = json.dumps(robot_info, ensure_ascii=False) self.logger.info(f"脚本 {self.script_id} 成功获取机器人信息: {agv_id}") return result # 未找到机器人,返回空对象 self.logger.warning(f"脚本 {self.script_id} 未找到机器人: {agv_id}") return "{}" else: self.logger.warning(f"脚本 {self.script_id} 获取机器人信息失败") return "{}" except Exception as e: self.logger.error(f"脚本 {self.script_id} 获取机器人信息异常: {str(e)}") return "{}" async def get_vehicle_station(self, agv_id: str) -> str: """根据机器人ID获取机器人位置 Args: agv_id: 机器人ID Returns: str: JSON格式的位置信息,如果与调度断连返回空对象 """ try: # 获取认证token token = await self._get_auth_token() # 调用获取AMR信息接口 from services.sync_service import get_amr_info response = await get_amr_info(token) if response and response.get("success") and response.get("result"): amr_list = response.get("result", []) # 查找指定ID的机器人 for amr in amr_list: if amr.get("id") == agv_id or amr.get("name") == agv_id: station_info = { "last_station": amr.get("lastNodeName", ""), "station": amr.get("lastNodeName", "") } result = json.dumps(station_info, ensure_ascii=False) self.logger.info(f"脚本 {self.script_id} 成功获取机器人位置: {agv_id}") return result # 未找到机器人,返回空对象 return "{}" else: # 与调度断连,返回空对象 return "{}" except Exception as e: self.logger.error(f"脚本 {self.script_id} 获取机器人位置异常: {str(e)}") return "{}" async def get_core_alarms(self) -> Optional[str]: """获取Core报错信息 Returns: Optional[str]: JSON格式的报错信息,断连时返回None """ ## TODO async def get_core_alarms_by_id(self, code: int) -> Optional[str]: """根据ID获取Core报错信息 Args: code: 错误码 Returns: Optional[str]: JSON格式的报错信息,断连时返回None """ ## TODO async def get_rbk_alarms(self) -> Optional[str]: """获取Rbk报错信息 Returns: Optional[str]: JSON格式的报错信息,断连时返回None """ ## TODO async def get_rbk_alarms_by_id(self, code: int) -> Optional[str]: """根据ID获取Rbk报错信息 Args: code: 错误码 Returns: Optional[str]: JSON格式的报错信息,断连时返回None """ ## TODO async def set_soft_stop(self, vehicle_id: str, status: bool) -> bool: """设置机器人软急停 Args: vehicle_id: 机器人名称 status: True表示设置软急停,False表示取消软急停 Returns: bool: True表示成功,False表示失败 """ ## TODO async def get_robots_status(self) -> Optional[str]: """获取机器人所有信息 Returns: Optional[str]: JSON格式的详细机器人信息,断连时返回None """ ## TODO async def query_charge_param(self) -> Optional[str]: """查询所有机器人充电阈值 Returns: Optional[str]: JSON格式的充电阈值信息,断连时返回None """ try: # 获取认证token token = await self._get_auth_token() # 调用充电参数查询接口 from services.sync_service import get_amr_charge_params response = await get_amr_charge_params(token) if response and response.get("success") and response.get("result"): charge_params = response.get("result", []) result = json.dumps(charge_params, ensure_ascii=False) self.logger.info(f"脚本 {self.script_id} 成功查询所有机器人充电阈值,共 {len(charge_params)} 个机器人") return result else: self.logger.warning(f"脚本 {self.script_id} 查询所有机器人充电阈值失败") return json.dumps([], ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 查询所有机器人充电阈值异常: {str(e)}") return None async def query_charge_param_by_vehicles(self, vehicles: str) -> Optional[str]: """查询指定机器人充电阈值 Args: vehicles: JSON格式的机器人名称列表字符串 Returns: Optional[str]: JSON格式的充电阈值信息 """ try: # 解析机器人列表 vehicle_list = json.loads(vehicles) if isinstance(vehicles, str) else vehicles # 获取认证token token = await self._get_auth_token() # 调用指定机器人充电参数查询接口 from services.sync_service import get_amr_charge_params_by_vehicles response = await get_amr_charge_params_by_vehicles(vehicle_list, token) if response and response.get("success") and response.get("result"): charge_params = response.get("result", []) result = json.dumps(charge_params, ensure_ascii=False) self.logger.info(f"脚本 {self.script_id} 成功查询指定机器人充电阈值,共 {len(charge_params)} 个机器人") return result else: self.logger.warning(f"脚本 {self.script_id} 查询指定机器人充电阈值失败") return json.dumps([], ensure_ascii=False) except Exception as e: self.logger.error(f"脚本 {self.script_id} 查询指定机器人充电阈值异常: {str(e)}") return None async def modify_charge_param(self, param: str) -> bool: """修改机器人充电阈值 Args: param: JSON格式的修改参数字符串,包含id(机器人ID或名称)和要修改的充电参数 Returns: bool: True表示修改成功,False表示修改失败 """ try: # 解析修改参数 param_data = safe_parse_dict(param, self.script_id) if param_data is None: return False # 获取机器人ID或名称 amr_identifier = param_data.get("id") if not amr_identifier: self.logger.error(f"脚本 {self.script_id} 修改机器人充电阈值失败: 缺少机器人ID") return False # 获取认证token token = await self._get_auth_token() # 首先获取所有AMR信息,找到对应的机器人并获取当前充电参数 from services.sync_service import get_amr_list response = await get_amr_list(token) if not response or not response.get("success") or not response.get("result"): self.logger.warning(f"脚本 {self.script_id} 获取机器人信息失败") return False # 查找对应的机器人,获取真实的ID和当前充电参数 amr_list = response.get("result", {}).get("records", []) actual_amr_id = None current_charge_params = None for amr in amr_list: if amr.get("id") == amr_identifier or amr.get("name") == amr_identifier: actual_amr_id = amr.get("id") # 获取当前的充电参数作为默认值 current_charge_params = { "exchange_power": amr.get("exchangePower", 0), "task_power": amr.get("taskPower", 0), "charge_power": amr.get("chargePower", 0), "min_power": amr.get("minPower", 0), "max_power": amr.get("maxPower", 0) } break if not actual_amr_id or current_charge_params is None: self.logger.error(f"脚本 {self.script_id} 修改机器人充电阈值失败: 未找到机器人 {amr_identifier}") return False # 准备充电参数 - 从当前参数开始,用用户传入的参数覆盖对应字段 charge_params = current_charge_params.copy() # 蛇形命名字段列表 snake_fields = ["exchange_power", "task_power", "charge_power", "min_power", "max_power"] has_changes = False # 用用户传入的参数覆盖对应字段 for snake_field in snake_fields: if snake_field in param_data: charge_params[snake_field] = param_data[snake_field] has_changes = True if not has_changes: self.logger.error(f"脚本 {self.script_id} 修改机器人充电阈值失败: 没有要修改的充电参数") return False self.logger.info(f"脚本 {self.script_id} 准备修改机器人 {amr_identifier} 的充电参数: {charge_params}") # 调用修改充电参数接口,使用真实的机器人ID,传入完整的5个参数 from services.sync_service import modify_amr_charge_param response = await modify_amr_charge_param(actual_amr_id, charge_params, token) if response and response.get("success"): self.logger.info(f"脚本 {self.script_id} 成功修改机器人充电阈值: {amr_identifier} (ID: {actual_amr_id})") return True else: error_msg = response.get("message", "未知错误") if response else "接口调用失败" self.logger.warning(f"脚本 {self.script_id} 修改机器人充电阈值失败: {error_msg}") return False except Exception as e: self.logger.error(f"脚本 {self.script_id} 修改机器人充电阈值异常: {str(e)}") return False