255 lines
8.7 KiB
Python
Raw Normal View History

2025-09-29 09:35:08 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED其他模块
提供各种杂项工具函数
"""
import uuid
import hashlib
import base64
import json
import smtplib
import math
from typing import List, Dict, Any, Optional
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import xml.etree.ElementTree as ET
from utils.json_parser import safe_parse_dict, safe_parse_list
from utils.logger import get_logger
logger = get_logger("services.online_script.other_module")
class VWEDOtherModule:
"""VWED其他模块 - 提供各种杂项工具函数"""
def __init__(self, script_id: str):
self.script_id = script_id
def create_uuid(self) -> str:
"""创建唯一的 ID 编号"""
try:
return str(uuid.uuid4())
except Exception as e:
logger.error(f"[{self.script_id}] 创建UUID失败: {e}")
raise
def send_mail(self, param: str) -> None:
"""向目标收件方发送简单邮件
Args:
param: 邮件详情JSON字符串包含to, subject, text等字段
"""
try:
mail_config = json.loads(param)
# 验证必填字段
required_fields = ['to', 'subject', 'text']
for field in required_fields:
if field not in mail_config:
raise ValueError(f"缺少必填字段: {field}")
# TODO: 实际的邮件发送逻辑需要根据系统配置实现
# 这里仅记录日志实际使用时需要配置SMTP服务器
logger.info(f"[{self.script_id}] 发送邮件: {mail_config['subject']} -> {mail_config['to']}")
# 示例实现需要配置SMTP服务器信息
"""
msg = MIMEMultipart()
msg['From'] = smtp_config['from']
msg['To'] = mail_config['to']
msg['Subject'] = mail_config['subject']
if 'cc' in mail_config:
msg['Cc'] = mail_config['cc']
msg.attach(MIMEText(mail_config['text'], 'plain', 'utf-8'))
server = smtplib.SMTP(smtp_config['host'], smtp_config['port'])
server.starttls()
server.login(smtp_config['username'], smtp_config['password'])
server.send_message(msg)
server.quit()
"""
except Exception as e:
logger.error(f"[{self.script_id}] 发送邮件失败: {e}")
raise
def send_user_message(self, level: str, message_title: str, message_body: str) -> None:
"""发送用户消息
Args:
level: 消息等级 ERROR, WARN, INFO
message_title: 消息标题
message_body: 消息内容
"""
try:
if level not in ['ERROR', 'WARN', 'INFO']:
level = 'INFO'
# TODO: 实际的用户消息发送逻辑需要与RDS消息管理系统集成
logger.info(f"[{self.script_id}] 用户消息 [{level}] {message_title}: {message_body}")
# 这里应该调用实际的消息推送API
# 可能需要WebSocket推送或者写入消息队列
except Exception as e:
logger.error(f"[{self.script_id}] 发送用户消息失败: {e}")
# 该方法不抛出异常
def json_to_xml(self, param: str) -> str:
"""将JSON字符串转化为XML字符串
Args:
param: JSON字符串
Returns:
XML字符串
"""
try:
# data = json.loads(param)
data = safe_parse_dict(param, self.script_id)
def dict_to_xml(tag, d):
"""递归将字典转换为XML元素"""
elem = ET.Element(tag)
for key, val in d.items():
if isinstance(val, dict):
elem.append(dict_to_xml(key, val))
elif isinstance(val, list):
for item in val:
if isinstance(item, dict):
elem.append(dict_to_xml(key, item))
else:
child = ET.SubElement(elem, key)
child.text = str(item)
else:
child = ET.SubElement(elem, key)
child.text = str(val)
return elem
root = dict_to_xml('root', data)
# 格式化XML输出 - 使用简单的格式化方式避免minidom兼容性问题
ET.indent(root, space=" ", level=0)
return ET.tostring(root, encoding='unicode')
except Exception as e:
logger.error(f"[{self.script_id}] JSON转XML失败: {e}")
return ""
def md5_encode(self, param: str) -> str:
"""将字符串进行MD5加密
Args:
param: 要加密的字符串
Returns:
32位小写MD5字符串
"""
try:
md5_hash = hashlib.md5()
md5_hash.update(param.encode('utf-8'))
return md5_hash.hexdigest()
except Exception as e:
logger.error(f"[{self.script_id}] MD5加密失败: {e}")
raise
def base64_encode(self, param: str) -> str:
"""将字符串进行Base64重编码
Args:
param: 要编码的字符串
Returns:
Base64编码后的字符串
"""
try:
encoded_bytes = base64.b64encode(param.encode('utf-8'))
return encoded_bytes.decode('utf-8')
except Exception as e:
logger.error(f"[{self.script_id}] Base64编码失败: {e}")
raise
def trans_to_page_obj(self, total_count: int, current_page: int, page_size: int,
total_page: int, page_list: str) -> str:
"""转换成分页对象
Args:
total_count: 分页数据的总条数
current_page: 当前是第几页
page_size: 每页显示的数量
total_page: 总页数
page_list: 当前页的数据
Returns:
转换后的分页对象字符串
"""
try:
page_obj = {
"total_count": total_count,
"current_page": current_page,
"page_size": page_size,
"total_page": total_page,
"page_list": json.loads(page_list) if isinstance(page_list, str) else page_list
}
return json.dumps(page_obj, ensure_ascii=False)
except Exception as e:
logger.error(f"[{self.script_id}] 转换分页对象失败: {e}")
return "{}"
async def get_script_value(self, key: str) -> str:
"""获取天风任务参数
Args:
key: 变量表达式 task.variables.status, blocks.b4.site_id
Returns:
参数值的字符串不存在时返回 "NULL"
"""
try:
# TODO: 这里需要与任务执行上下文集成,获取实际的任务变量
# 应该从task_context中获取对应的值
from services.execution.task_context import get_current_context
context = get_current_context()
if context:
value = context.get_variable(key)
return str(value) if value is not None else "NULL"
logger.warning(f"[{self.script_id}] 无法获取任务参数,没有活跃的任务上下文")
return "NULL"
except Exception as e:
logger.error(f"[{self.script_id}] 获取脚本值失败: {e}")
return "NULL"
def release_wait_pass(self, agv_ids: List[str]) -> bool:
"""批量放行机器人
Args:
agv_ids: 需放行的机器人ID列表
Returns:
True: 放行成功, False: 放行失败
"""
try:
if not agv_ids:
return True
# TODO: 这里需要与机器人调度系统集成,实现实际的放行逻辑
logger.info(f"[{self.script_id}] 批量放行机器人: {agv_ids}")
# 应该调用机器人调度服务的放行接口
# from services.robot_scheduling import release_robots
# return release_robots(agv_ids)
return True
except Exception as e:
logger.error(f"[{self.script_id}] 批量放行失败: {e}")
return False