suwoodblender/test/blender_suw_client.py

410 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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