450 lines
19 KiB
Python
Raw Normal View History

2025-09-29 09:35:08 +08:00
#!/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