suwoodblender/test/blender_suw_client.py

410 lines
16 KiB
Python
Raw Normal View History

# 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)