450 lines
19 KiB
Python
450 lines
19 KiB
Python
#!/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 |