523 lines
19 KiB
Python
523 lines
19 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
SUW 自动客户端模块
|
||
用于在 Blender 插件启动时自动启动 SUW 客户端
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import time
|
||
import threading
|
||
import datetime
|
||
import traceback
|
||
import socket
|
||
from typing import Dict, Any, Optional, List
|
||
import logging
|
||
|
||
# 配置日志
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 尝试导入 Blender 模块
|
||
try:
|
||
import bpy
|
||
BLENDER_AVAILABLE = True
|
||
except ImportError:
|
||
BLENDER_AVAILABLE = False
|
||
logger.warning("⚠️ Blender模块不可用")
|
||
|
||
# 尝试导入 SUWood 模块
|
||
try:
|
||
from . import suw_core
|
||
from . import suw_client
|
||
SUWOOD_AVAILABLE = True
|
||
logger.info("✅ SUWood模块导入成功")
|
||
except ImportError as e:
|
||
SUWOOD_AVAILABLE = False
|
||
logger.error(f"❌ SUWood模块导入失败: {e}")
|
||
|
||
|
||
class SUWAutoClient:
|
||
"""SUW 自动客户端 - 集成到 Blender 插件中"""
|
||
|
||
def __init__(self):
|
||
"""初始化 SUW 自动客户端"""
|
||
self.client = None
|
||
self.is_running = False
|
||
self.command_count = 0
|
||
self.success_count = 0
|
||
self.fail_count = 0
|
||
self.last_check_time = None
|
||
self.start_time = None
|
||
self.command_dispatcher = None
|
||
self.client_thread = None
|
||
self.auto_start_enabled = True
|
||
|
||
def initialize_system(self):
|
||
"""初始化 SUW 系统"""
|
||
try:
|
||
logger.info("🔧 初始化 SUW 自动客户端系统...")
|
||
|
||
if not SUWOOD_AVAILABLE:
|
||
logger.error("❌ SUWood模块不可用,无法初始化客户端")
|
||
return False
|
||
|
||
# 导入客户端模块
|
||
logger.info("📡 导入客户端模块...")
|
||
from .suw_client import SUWClient
|
||
|
||
# 创建客户端实例
|
||
logger.info("🔗 创建客户端连接...")
|
||
self.client = SUWClient()
|
||
|
||
# 检查连接状态
|
||
if self.client.sock is None:
|
||
logger.error("❌ 客户端连接失败")
|
||
return False
|
||
|
||
logger.info("✅ 客户端连接成功")
|
||
|
||
# 测试连接
|
||
logger.info("🔗 测试服务器连接...")
|
||
test_result = self._test_connection()
|
||
if test_result:
|
||
logger.info("✅ 服务器连接正常")
|
||
|
||
# 初始化命令分发器
|
||
logger.info("🔧 初始化命令分发器...")
|
||
if self._init_command_dispatcher():
|
||
logger.info("✅ 命令分发器初始化完成")
|
||
return True
|
||
else:
|
||
logger.error("❌ 命令分发器初始化失败")
|
||
return False
|
||
else:
|
||
logger.error("❌ 服务器连接测试失败")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ SUW 自动客户端初始化失败: {e}")
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def _init_command_dispatcher(self):
|
||
"""初始化命令分发器"""
|
||
try:
|
||
logger.info("📦 导入管理器模块...")
|
||
|
||
# 导入各个管理器
|
||
from .suw_core.data_manager import get_data_manager
|
||
from .suw_core.material_manager import MaterialManager
|
||
from .suw_core.part_creator import PartCreator
|
||
from .suw_core.machining_manager import MachiningManager
|
||
from .suw_core.selection_manager import SelectionManager
|
||
from .suw_core.deletion_manager import DeletionManager
|
||
from .suw_core.hardware_manager import HardwareManager
|
||
from .suw_core.door_drawer_manager import get_door_drawer_manager
|
||
from .suw_core.dimension_manager import get_dimension_manager
|
||
from .suw_core.command_dispatcher import get_command_dispatcher
|
||
|
||
logger.info("✅ 所有管理器模块导入完成")
|
||
|
||
# 获取管理器实例
|
||
logger.info("🔧 获取管理器实例...")
|
||
data_manager = get_data_manager()
|
||
material_manager = MaterialManager()
|
||
part_creator = PartCreator()
|
||
machining_manager = MachiningManager()
|
||
selection_manager = SelectionManager()
|
||
deletion_manager = DeletionManager()
|
||
hardware_manager = HardwareManager()
|
||
door_drawer_manager = get_door_drawer_manager()
|
||
dimension_manager = get_dimension_manager()
|
||
|
||
logger.info("✅ 管理器实例获取完成")
|
||
|
||
# 获取命令分发器
|
||
self.command_dispatcher = get_command_dispatcher()
|
||
logger.info(f"✅ 命令分发器获取完成: {type(self.command_dispatcher)}")
|
||
|
||
# 测试命令分发器
|
||
if self.command_dispatcher:
|
||
logger.info("✅ 命令分发器测试: 已初始化")
|
||
# 测试一个简单的命令
|
||
try:
|
||
test_result = self.command_dispatcher.dispatch_command(
|
||
"test", {})
|
||
logger.info(f"🔧 命令分发器测试结果: {test_result}")
|
||
except Exception as e:
|
||
logger.info(f"🔧 命令分发器测试异常(正常): {e}")
|
||
else:
|
||
logger.error("❌ 命令分发器获取失败")
|
||
return False
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 命令分发器初始化失败: {e}")
|
||
logger.error(f"❌ 异常详情: {traceback.format_exc()}")
|
||
return False
|
||
|
||
def _test_connection(self):
|
||
"""测试连接"""
|
||
try:
|
||
if not self.client or not self.client.sock:
|
||
return False
|
||
|
||
# 发送一个简单的测试消息
|
||
test_msg = '{"cmd": "test", "params": {"from": "blender_plugin"}}'
|
||
if self.client.send_msg(0x01, test_msg):
|
||
logger.info("✅ 测试消息发送成功")
|
||
return True
|
||
else:
|
||
logger.error("❌ 测试消息发送失败")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 连接测试失败: {e}")
|
||
return False
|
||
|
||
def start_client(self):
|
||
"""启动客户端"""
|
||
try:
|
||
logger.info("🌐 启动 SUW 自动客户端...")
|
||
|
||
if not self.client:
|
||
logger.error("❌ 客户端未初始化")
|
||
return False
|
||
|
||
self.is_running = True
|
||
self.start_time = datetime.datetime.now()
|
||
self.last_check_time = self.start_time
|
||
|
||
# 启动后台线程
|
||
logger.info("🧵 启动客户端后台线程...")
|
||
self.client_thread = threading.Thread(
|
||
target=self._client_loop, daemon=True)
|
||
self.client_thread.start()
|
||
|
||
logger.info("✅ SUW 自动客户端启动成功!")
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 客户端启动失败: {e}")
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def _client_loop(self):
|
||
"""客户端主循环"""
|
||
logger.info("🔄 进入客户端监听循环...")
|
||
|
||
consecutive_errors = 0
|
||
max_consecutive_errors = 10
|
||
|
||
try:
|
||
if not self.client or not self.client.sock:
|
||
logger.error("❌ 无法连接到SUWood服务器")
|
||
return
|
||
|
||
logger.info("✅ 已连接到SUWood服务器")
|
||
logger.info("🎯 开始监听命令...")
|
||
|
||
while self.is_running:
|
||
try:
|
||
# 获取命令
|
||
from .suw_client import get_cmds
|
||
commands = get_cmds()
|
||
|
||
if commands and len(commands) > 0:
|
||
logger.info(f"\n📨 收到 {len(commands)} 个命令")
|
||
consecutive_errors = 0 # 重置错误计数
|
||
|
||
# 处理每个命令
|
||
for cmd in commands:
|
||
if not self.is_running:
|
||
break
|
||
self._process_command(cmd)
|
||
|
||
# 短暂休眠避免过度占用CPU
|
||
time.sleep(0.1)
|
||
|
||
except KeyboardInterrupt:
|
||
logger.info("🛑 收到中断信号,退出客户端循环")
|
||
break
|
||
|
||
except Exception as e:
|
||
consecutive_errors += 1
|
||
logger.error(
|
||
f"❌ 客户端循环异常 ({consecutive_errors}/{max_consecutive_errors}): {e}")
|
||
|
||
if consecutive_errors >= max_consecutive_errors:
|
||
logger.error("💀 连续错误过多,退出客户端循环")
|
||
break
|
||
|
||
# 错误后稍长休眠
|
||
time.sleep(1)
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 客户端线程异常: {e}")
|
||
|
||
logger.info("🔄 客户端循环结束")
|
||
|
||
def check_commands(self):
|
||
"""手动检查命令"""
|
||
try:
|
||
if not self.is_running or not self.client:
|
||
return # 静默返回,不输出日志
|
||
|
||
# 使用get_cmds函数检查命令,添加超时保护
|
||
from .suw_client import get_cmds
|
||
try:
|
||
# 设置socket超时,避免阻塞
|
||
if self.client and self.client.sock:
|
||
self.client.sock.settimeout(0.3) # 300ms超时
|
||
|
||
cmds = get_cmds()
|
||
|
||
# 检查返回值是否为None或空列表
|
||
if cmds is None:
|
||
cmds = []
|
||
elif not isinstance(cmds, list):
|
||
cmds = []
|
||
|
||
# 恢复socket超时设置
|
||
if self.client and self.client.sock:
|
||
self.client.sock.settimeout(None)
|
||
|
||
if cmds and len(cmds) > 0:
|
||
# 只有在有命令时才输出日志
|
||
logger.info(
|
||
f"\n 手动检查命令... (上次检查: {self.last_check_time.strftime('%H:%M:%S') if self.last_check_time else '从未'})")
|
||
logger.info(f" 收到 {len(cmds)} 个命令")
|
||
logger.info(
|
||
f" 命令分发器状态: {'✅ 已初始化' if self.command_dispatcher else '❌ 未初始化'}")
|
||
|
||
# 参考blender_suw_core_independent.py的处理方式
|
||
for i, cmd in enumerate(cmds):
|
||
logger.info(f"🔍 处理第 {i+1}/{len(cmds)} 个命令")
|
||
self._process_command(cmd)
|
||
|
||
except socket.timeout:
|
||
# 超时是正常的,静默处理
|
||
pass
|
||
except Exception as e:
|
||
logger.error(f"❌ 获取命令失败: {e}")
|
||
|
||
self.last_check_time = datetime.datetime.now()
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 检查命令失败: {e}")
|
||
|
||
def _process_command(self, cmd_data):
|
||
"""处理命令"""
|
||
from datetime import datetime
|
||
try:
|
||
self.command_count += 1
|
||
logger.info(
|
||
f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}")
|
||
logger.info(f"🎯 处理命令 #{self.command_count}: {cmd_data}")
|
||
|
||
# 解析命令数据
|
||
command_type = None
|
||
command_data = {}
|
||
|
||
# 处理不同的命令格式
|
||
if isinstance(cmd_data, dict):
|
||
if 'cmd' in cmd_data and 'data' in cmd_data:
|
||
# 格式: {'cmd': 'set_cmd', 'data': {'cmd': 'c04', ...}}
|
||
command_type = cmd_data['data'].get('cmd')
|
||
command_data = cmd_data['data']
|
||
elif 'cmd' in cmd_data:
|
||
# 格式: {'cmd': 'c04', ...}
|
||
command_type = cmd_data['cmd']
|
||
command_data = cmd_data
|
||
else:
|
||
logger.warning(f"⚠️ 无法解析命令格式: {cmd_data}")
|
||
return
|
||
|
||
if command_type:
|
||
logger.info(f"🔧 执行命令: {command_type}")
|
||
|
||
# 使用命令分发器执行命令 - 简化处理
|
||
if self.command_dispatcher:
|
||
try:
|
||
result = self.command_dispatcher.dispatch_command(
|
||
command_type, command_data)
|
||
if result:
|
||
logger.info(f"✅ 命令 {command_type} 执行成功")
|
||
self.success_count += 1
|
||
else:
|
||
logger.error(f"❌ 命令 {command_type} 执行失败")
|
||
self.fail_count += 1
|
||
except Exception as e:
|
||
logger.error(f"❌ 命令 {command_type} 执行异常: {e}")
|
||
self.fail_count += 1
|
||
else:
|
||
logger.warning("⚠️ 命令分发器未初始化,只记录命令")
|
||
self.success_count += 1
|
||
|
||
logger.info("") # 空行分隔
|
||
else:
|
||
logger.warning(f"⚠️ 无法识别命令类型: {cmd_data}")
|
||
self.fail_count += 1
|
||
logger.info("") # 空行分隔
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 命令处理失败: {e}")
|
||
self.fail_count += 1
|
||
logger.info("") # 空行分隔
|
||
traceback.print_exc()
|
||
|
||
def print_status(self):
|
||
"""打印状态"""
|
||
if not self.is_running:
|
||
logger.info("❌ 客户端未运行")
|
||
return
|
||
|
||
runtime = datetime.datetime.now(
|
||
) - self.start_time if self.start_time else datetime.timedelta(0)
|
||
success_rate = (self.success_count / self.command_count *
|
||
100) if self.command_count > 0 else 0
|
||
thread_alive = self.client_thread.is_alive() if self.client_thread else False
|
||
|
||
logger.info("📊 SUW 自动客户端状态:")
|
||
logger.info(f"🔄 运行状态: {'✅ 运行中' if self.is_running else '❌ 已停止'}")
|
||
logger.info(f"🧵 线程状态: {'✅ 活跃' if thread_alive else '❌ 停止'}")
|
||
logger.info(f"⏱️ 运行时间: {runtime}")
|
||
logger.info(
|
||
f"📈 命令统计: 总计: {self.command_count}, 成功: {self.success_count}, 失败: {self.fail_count}, 成功率: {success_rate:.1f}%")
|
||
logger.info(
|
||
f"🔍 最后检查: {self.last_check_time.strftime('%H:%M:%S') if self.last_check_time else '从未'}")
|
||
logger.info(
|
||
f"🎯 命令分发器: {'✅ 已初始化' if self.command_dispatcher else '❌ 未初始化'}")
|
||
|
||
def stop_client(self):
|
||
"""停止客户端"""
|
||
try:
|
||
logger.info("🛑 停止 SUW 自动客户端...")
|
||
|
||
self.is_running = False
|
||
|
||
if self.client_thread and self.client_thread.is_alive():
|
||
self.client_thread.join(timeout=2)
|
||
|
||
if self.client and self.client.sock:
|
||
try:
|
||
self.client.sock.close()
|
||
except:
|
||
pass
|
||
|
||
logger.info("✅ 客户端已停止")
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 停止客户端失败: {e}")
|
||
traceback.print_exc()
|
||
|
||
|
||
# ==================== 全局客户端实例 ====================
|
||
|
||
suw_auto_client = SUWAutoClient()
|
||
|
||
|
||
# ==================== 便捷函数 ====================
|
||
|
||
def start_suw_auto_client():
|
||
"""启动 SUW 自动客户端"""
|
||
logger.info("🚀 启动 SUW 自动客户端...")
|
||
|
||
if suw_auto_client.initialize_system():
|
||
if suw_auto_client.start_client():
|
||
logger.info("🎉 SUW 自动客户端启动成功!")
|
||
return True
|
||
else:
|
||
logger.error("❌ 客户端启动失败")
|
||
return False
|
||
else:
|
||
logger.error("❌ 系统初始化失败")
|
||
return False
|
||
|
||
|
||
def stop_suw_auto_client():
|
||
"""停止 SUW 自动客户端"""
|
||
suw_auto_client.stop_client()
|
||
|
||
|
||
def check_suw_commands():
|
||
"""检查 SUW 命令"""
|
||
suw_auto_client.check_commands()
|
||
|
||
|
||
def print_suw_status():
|
||
"""打印 SUW 状态"""
|
||
suw_auto_client.print_status()
|
||
|
||
|
||
# ==================== Blender 集成函数 ====================
|
||
|
||
def register_suw_auto_client():
|
||
"""注册 SUW 自动客户端到 Blender"""
|
||
try:
|
||
if not BLENDER_AVAILABLE:
|
||
logger.error("❌ Blender环境不可用,无法注册SUW自动客户端")
|
||
return False
|
||
|
||
if not SUWOOD_AVAILABLE:
|
||
logger.error("❌ SUWood模块不可用,无法注册SUW自动客户端")
|
||
return False
|
||
|
||
# 启动 SUW 自动客户端
|
||
if start_suw_auto_client():
|
||
logger.info("✅ SUW 自动客户端注册成功")
|
||
return True
|
||
else:
|
||
logger.error("❌ SUW 自动客户端注册失败")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ SUW 自动客户端注册失败: {e}")
|
||
return False
|
||
|
||
|
||
def unregister_suw_auto_client():
|
||
"""注销 SUW 自动客户端"""
|
||
try:
|
||
stop_suw_auto_client()
|
||
logger.info("✅ SUW 自动客户端注销成功")
|
||
except Exception as e:
|
||
logger.error(f"❌ SUW 自动客户端注销失败: {e}")
|
||
|
||
|
||
# ==================== 定时器回调函数 ====================
|
||
|
||
def suw_client_timer():
|
||
"""SUW 客户端定时器回调函数"""
|
||
# 暂时禁用定时器,避免阻塞Blender主线程
|
||
return None # 返回None停止定时器
|
||
|
||
|
||
def start_suw_client_timer():
|
||
"""启动 SUW 客户端定时器"""
|
||
try:
|
||
if BLENDER_AVAILABLE:
|
||
# 注册定时器
|
||
bpy.app.timers.register(suw_client_timer)
|
||
logger.info("✅ SUW 客户端定时器启动成功")
|
||
else:
|
||
logger.warning("⚠️ Blender环境不可用,无法启动定时器")
|
||
except Exception as e:
|
||
logger.error(f"❌ 启动SUW客户端定时器失败: {e}")
|
||
|
||
|
||
def stop_suw_client_timer():
|
||
"""停止 SUW 客户端定时器"""
|
||
try:
|
||
if BLENDER_AVAILABLE:
|
||
# 注销定时器
|
||
bpy.app.timers.unregister(suw_client_timer)
|
||
logger.info("✅ SUW 客户端定时器停止成功")
|
||
else:
|
||
logger.warning("⚠️ Blender环境不可用,无法停止定时器")
|
||
except Exception as e:
|
||
logger.error(f"❌ 停止SUW客户端定时器失败: {e}")
|