410 lines
16 KiB
Python
410 lines
16 KiB
Python
# Blender中运行SUWood客户端 - 接收和处理SUWood命令
|
||
import bpy
|
||
import sys
|
||
import threading
|
||
import time
|
||
from pathlib import Path
|
||
|
||
print("🚀 Blender SUWood 客户端启动")
|
||
print("=" * 50)
|
||
|
||
# 添加SUWood模块路径
|
||
sys.path.insert(0, r"D:\XL\code\blender\blenderpython")
|
||
|
||
# 全局变量
|
||
client_thread = None
|
||
running = False
|
||
suw_impl = None
|
||
|
||
try:
|
||
# 导入SUWood模块
|
||
from suw_impl import SUWImpl
|
||
from suw_client import SUWClient, get_client
|
||
print("✅ SUWood模块导入成功")
|
||
|
||
# 初始化SUWImpl
|
||
suw_impl = SUWImpl.get_instance()
|
||
suw_impl.startup()
|
||
print("✅ SUWImpl初始化完成")
|
||
|
||
def process_suwood_command(cmd_type, data):
|
||
"""处理SUWood命令"""
|
||
import datetime
|
||
|
||
# 显示命令接收信息
|
||
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
|
||
print("=" * 60)
|
||
print(f"📥 [{timestamp}] 接收到SUWood命令")
|
||
print(f"🏷️ 原始命令类型: {cmd_type}")
|
||
print(f"📦 原始命令数据: {data}")
|
||
print("-" * 60)
|
||
|
||
# 处理嵌套命令格式
|
||
actual_cmd_type = cmd_type
|
||
actual_data = data
|
||
|
||
# 如果是set_cmd格式,提取内部真实命令
|
||
if cmd_type == 'set_cmd' and isinstance(data, dict) and 'cmd' in data:
|
||
actual_cmd_type = data.get('cmd')
|
||
# 将除了cmd之外的所有数据作为参数传递
|
||
actual_data = {k: v for k, v in data.items() if k != 'cmd'}
|
||
print(f"🔄 检测到嵌套命令格式,提取真实命令:")
|
||
print(f" 实际命令类型: {actual_cmd_type}")
|
||
print(f" 实际命令数据: {actual_data}")
|
||
print("-" * 60)
|
||
|
||
try:
|
||
# 确保在主线程中执行Blender操作
|
||
def execute_in_main_thread():
|
||
start_time = time.time()
|
||
try:
|
||
if hasattr(suw_impl, actual_cmd_type):
|
||
print(
|
||
f"🔍 [{time.strftime('%H:%M:%S')}] 查找到命令方法: {actual_cmd_type}")
|
||
method = getattr(suw_impl, actual_cmd_type)
|
||
print(f"⚡ [{time.strftime('%H:%M:%S')}] 开始执行命令...")
|
||
|
||
result = method(actual_data)
|
||
|
||
elapsed_time = time.time() - start_time
|
||
print(
|
||
f"✅ [{time.strftime('%H:%M:%S')}] 命令 '{actual_cmd_type}' 执行成功!")
|
||
print(f"⏱️ 执行耗时: {elapsed_time:.3f}秒")
|
||
if result is not None:
|
||
print(
|
||
f"📋 [{time.strftime('%H:%M:%S')}] 执行结果: {result}")
|
||
else:
|
||
print(f"📋 执行结果: 无返回值")
|
||
|
||
return result
|
||
else:
|
||
print(f"❌ 未找到命令方法: {actual_cmd_type}")
|
||
print(f"🔍 可用方法列表:")
|
||
available_methods = [attr for attr in dir(suw_impl) if not attr.startswith(
|
||
'_') and callable(getattr(suw_impl, attr))]
|
||
# 按字母顺序排序,方便查找
|
||
available_methods.sort()
|
||
# 显示前15个避免输出过长
|
||
for i, method in enumerate(available_methods[:15]):
|
||
print(f" - {method}")
|
||
if len(available_methods) > 15:
|
||
print(
|
||
f" ... 还有 {len(available_methods) - 15} 个方法")
|
||
|
||
# 查找相似命令
|
||
similar_commands = [m for m in available_methods if actual_cmd_type.lower(
|
||
) in m.lower() or m.lower() in actual_cmd_type.lower()]
|
||
if similar_commands:
|
||
print(f"🔍 相似命令建议:")
|
||
for cmd in similar_commands[:5]:
|
||
print(f" - {cmd}")
|
||
|
||
return None
|
||
except Exception as e:
|
||
elapsed_time = time.time() - start_time
|
||
print(
|
||
f"❌ [{time.strftime('%H:%M:%S')}] 命令 '{actual_cmd_type}' 执行失败!")
|
||
print(f"⏱️ 执行耗时: {elapsed_time:.3f}秒")
|
||
print(f"🚨 错误信息: {str(e)}")
|
||
print(f"📍 错误详情:")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
# 在主线程中执行
|
||
result = execute_in_main_thread()
|
||
|
||
print("=" * 60)
|
||
print() # 空行分隔
|
||
return result
|
||
|
||
except Exception as e:
|
||
print(f"❌ 命令处理框架异常: {e}")
|
||
print("=" * 60)
|
||
print()
|
||
return None
|
||
|
||
def client_worker():
|
||
"""客户端工作线程"""
|
||
global running
|
||
print("🔄 启动SUWood客户端工作线程...")
|
||
|
||
# 统计信息
|
||
total_commands = 0
|
||
successful_commands = 0
|
||
failed_commands = 0
|
||
start_time = time.time()
|
||
|
||
try:
|
||
# 连接SUWood服务器
|
||
client = get_client()
|
||
if not client or not client.sock:
|
||
print("❌ 无法连接到SUWood服务器")
|
||
return
|
||
|
||
print("✅ 已连接到SUWood服务器 (127.0.0.1:7999)")
|
||
print("🎯 开始监听SUWood命令...")
|
||
print("💡 提示: 在System Console (Window > Toggle System Console) 中查看详细输出")
|
||
print()
|
||
|
||
while running:
|
||
try:
|
||
# 获取命令
|
||
from suw_client import get_cmds
|
||
commands = get_cmds()
|
||
|
||
if commands and len(commands) > 0:
|
||
print(
|
||
f"🚀 [{time.strftime('%H:%M:%S')}] 接收到 {len(commands)} 个新命令")
|
||
|
||
for i, cmd in enumerate(commands, 1):
|
||
if not running:
|
||
break
|
||
|
||
# 支持多种命令格式
|
||
cmd_type = None
|
||
cmd_data = {}
|
||
|
||
# 格式1: {'type': '...', 'data': {...}}
|
||
if 'type' in cmd:
|
||
cmd_type = cmd.get('type')
|
||
cmd_data = cmd.get('data', {})
|
||
print(
|
||
f"\n📝 处理第 {i}/{len(commands)} 个命令 (格式1: type/data)...")
|
||
# 格式2: {'cmd': '...', 'data': {...}}
|
||
elif 'cmd' in cmd and 'data' in cmd:
|
||
cmd_type = cmd.get('cmd')
|
||
cmd_data = cmd.get('data', {})
|
||
print(
|
||
f"\n📝 处理第 {i}/{len(commands)} 个命令 (格式2: cmd/data)...")
|
||
# 格式3: 直接的命令数据 {'cmd': '...', ...}
|
||
elif isinstance(cmd, dict) and len(cmd) > 0:
|
||
# 查找可能的命令字段
|
||
for key in ['cmd', 'command', 'action', 'method']:
|
||
if key in cmd:
|
||
cmd_type = cmd.get(key)
|
||
cmd_data = {
|
||
k: v for k, v in cmd.items() if k != key}
|
||
print(
|
||
f"\n📝 处理第 {i}/{len(commands)} 个命令 (格式3: 直接命令)...")
|
||
break
|
||
|
||
if cmd_type:
|
||
print(f"🔧 识别的命令格式: {cmd}")
|
||
total_commands += 1
|
||
|
||
# 处理命令
|
||
result = process_suwood_command(
|
||
cmd_type, cmd_data)
|
||
|
||
# 判断执行是否成功
|
||
# c02是纹理命令,c09是删除命令等,这些命令通常正常返回None
|
||
success_commands = ['c02', 'c03', 'c04', 'c05', 'c06', 'c07', 'c08', 'c09', 'c0a', 'c0c', 'c0d', 'c0e', 'c0f',
|
||
'c10', 'c11', 'c12', 'c13', 'c14', 'c15', 'c16', 'c17', 'c18', 'c1a', 'c1b', 'c23', 'c24', 'c25', 'c28', 'c30',
|
||
'set_cmd', 'startup', 'init', 'set_config', 'show_message', 'add_surf']
|
||
|
||
if result is not None or cmd_type in success_commands:
|
||
successful_commands += 1
|
||
print(f"✅ 命令 {i} 处理成功")
|
||
else:
|
||
failed_commands += 1
|
||
print(f"❌ 命令 {i} 处理失败")
|
||
else:
|
||
failed_commands += 1
|
||
print(f"⚠️ 无法识别的命令格式: {cmd}")
|
||
print(f" 💡 支持的格式:")
|
||
print(
|
||
f" - {{'type': 'command_name', 'data': {{...}}}}")
|
||
print(
|
||
f" - {{'cmd': 'command_name', 'data': {{...}}}}")
|
||
print(
|
||
f" - {{'cmd': 'command_name', ...其他参数...}}")
|
||
|
||
# 显示统计信息
|
||
elapsed = time.time() - start_time
|
||
print(f"\n📊 会话统计 (运行时长: {elapsed:.1f}秒):")
|
||
print(f" 总命令数: {total_commands}")
|
||
print(f" 成功执行: {successful_commands}")
|
||
print(f" 执行失败: {failed_commands}")
|
||
if total_commands > 0:
|
||
success_rate = (
|
||
successful_commands / total_commands) * 100
|
||
print(f" 成功率: {success_rate:.1f}%")
|
||
print()
|
||
|
||
# 短暂休眠避免过度占用CPU
|
||
time.sleep(0.1)
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n🛑 接收到停止信号")
|
||
break
|
||
except Exception as e:
|
||
print(f"⚠️ 客户端循环异常: {e}")
|
||
print("🔄 1秒后重试...")
|
||
time.sleep(1) # 出错时休眠长一点
|
||
|
||
except Exception as e:
|
||
print(f"❌ 客户端工作线程异常: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
finally:
|
||
elapsed = time.time() - start_time
|
||
print("\n" + "=" * 50)
|
||
print("🛑 SUWood客户端工作线程已停止")
|
||
print(f"📊 最终统计 (总运行时长: {elapsed:.1f}秒):")
|
||
print(f" 总命令数: {total_commands}")
|
||
print(f" 成功执行: {successful_commands}")
|
||
print(f" 执行失败: {failed_commands}")
|
||
if total_commands > 0:
|
||
success_rate = (successful_commands / total_commands) * 100
|
||
print(f" 成功率: {success_rate:.1f}%")
|
||
print("=" * 50)
|
||
|
||
def start_client():
|
||
"""启动客户端"""
|
||
global client_thread, running
|
||
|
||
if running:
|
||
print("⚠️ 客户端已在运行中")
|
||
return
|
||
|
||
running = True
|
||
client_thread = threading.Thread(target=client_worker, daemon=True)
|
||
client_thread.start()
|
||
print("✅ SUWood客户端已启动")
|
||
|
||
def stop_client():
|
||
"""停止客户端"""
|
||
global running
|
||
running = False
|
||
print("🛑 正在停止SUWood客户端...")
|
||
|
||
# 创建UI操作面板
|
||
class SUWoodClientPanel(bpy.types.Panel):
|
||
"""SUWood客户端控制面板"""
|
||
bl_label = "SUWood客户端"
|
||
bl_idname = "VIEW3D_PT_suwood_client"
|
||
bl_space_type = 'VIEW_3D'
|
||
bl_region_type = 'UI'
|
||
bl_category = "SUWood"
|
||
|
||
def draw(self, context):
|
||
layout = self.layout
|
||
|
||
layout.label(text="SUWood服务器连接:")
|
||
|
||
row = layout.row()
|
||
row.operator("suwood.start_client", text="启动客户端")
|
||
row.operator("suwood.stop_client", text="停止客户端")
|
||
|
||
layout.separator()
|
||
layout.label(text="手动测试:")
|
||
layout.operator("suwood.test_connection", text="测试连接")
|
||
layout.operator("suwood.get_commands", text="获取命令")
|
||
|
||
class SUWoodStartClient(bpy.types.Operator):
|
||
"""启动SUWood客户端操作"""
|
||
bl_idname = "suwood.start_client"
|
||
bl_label = "启动SUWood客户端"
|
||
|
||
def execute(self, context):
|
||
start_client()
|
||
self.report({'INFO'}, "SUWood客户端已启动")
|
||
return {'FINISHED'}
|
||
|
||
class SUWoodStopClient(bpy.types.Operator):
|
||
"""停止SUWood客户端操作"""
|
||
bl_idname = "suwood.stop_client"
|
||
bl_label = "停止SUWood客户端"
|
||
|
||
def execute(self, context):
|
||
stop_client()
|
||
self.report({'INFO'}, "SUWood客户端已停止")
|
||
return {'FINISHED'}
|
||
|
||
class SUWoodTestConnection(bpy.types.Operator):
|
||
"""测试SUWood连接操作"""
|
||
bl_idname = "suwood.test_connection"
|
||
bl_label = "测试SUWood连接"
|
||
|
||
def execute(self, context):
|
||
try:
|
||
client = get_client()
|
||
if client and client.sock:
|
||
self.report({'INFO'}, "SUWood服务器连接正常")
|
||
else:
|
||
self.report({'ERROR'}, "无法连接到SUWood服务器")
|
||
except Exception as e:
|
||
self.report({'ERROR'}, f"连接测试失败: {e}")
|
||
return {'FINISHED'}
|
||
|
||
class SUWoodGetCommands(bpy.types.Operator):
|
||
"""手动获取SUWood命令操作"""
|
||
bl_idname = "suwood.get_commands"
|
||
bl_label = "获取SUWood命令"
|
||
|
||
def execute(self, context):
|
||
try:
|
||
from suw_client import get_cmds
|
||
commands = get_cmds()
|
||
|
||
if commands and len(commands) > 0:
|
||
self.report({'INFO'}, f"获取到 {len(commands)} 个命令")
|
||
|
||
# 处理命令
|
||
for cmd in commands:
|
||
cmd_type = cmd.get('type')
|
||
cmd_data = cmd.get('data', {})
|
||
if cmd_type:
|
||
process_suwood_command(cmd_type, cmd_data)
|
||
else:
|
||
self.report({'INFO'}, "当前没有待处理的命令")
|
||
|
||
except Exception as e:
|
||
self.report({'ERROR'}, f"获取命令失败: {e}")
|
||
return {'FINISHED'}
|
||
|
||
# 注册类
|
||
classes = [
|
||
SUWoodClientPanel,
|
||
SUWoodStartClient,
|
||
SUWoodStopClient,
|
||
SUWoodTestConnection,
|
||
SUWoodGetCommands
|
||
]
|
||
|
||
for cls in classes:
|
||
try:
|
||
bpy.utils.register_class(cls)
|
||
except:
|
||
pass # 可能已经注册过了
|
||
|
||
print("\n🎉 SUWood客户端已初始化完成!")
|
||
print("=" * 50)
|
||
print("📋 使用方法:")
|
||
print("1. 在3D视图右侧面板找到'SUWood'标签")
|
||
print("2. 点击'启动客户端'开始接收命令")
|
||
print("3. 或者手动点击'获取命令'测试")
|
||
print("4. 在System Console中查看详细输出")
|
||
|
||
print("\n🔧 或者直接运行:")
|
||
print("start_client() # 启动自动客户端")
|
||
print("stop_client() # 停止客户端")
|
||
|
||
# 提供直接调用函数
|
||
globals()['start_client'] = start_client
|
||
globals()['stop_client'] = stop_client
|
||
globals()['process_suwood_command'] = process_suwood_command
|
||
|
||
except Exception as e:
|
||
print(f"❌ SUWood客户端初始化失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
print("\n🔧 请检查:")
|
||
print("1. SUWood服务器是否在端口7999运行")
|
||
print("2. 模块路径是否正确")
|
||
print("3. 网络连接是否正常")
|
||
|
||
print("\n" + "=" * 50)
|