#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ SUW Core - Machining Manager Module 拆分自: suw_impl.py (Line 2292-3500, 4790-4990) 用途: Blender加工管理、几何体创建、布尔运算 版本: 1.0.0 作者: SUWood Team """ from .material_manager import material_manager from .memory_manager import memory_manager from .data_manager import data_manager, get_data_manager import time import logging import threading from typing import Dict, Any, List, Optional import math # 设置日志 logger = logging.getLogger(__name__) # 检查Blender可用性 try: import bpy import bmesh BLENDER_AVAILABLE = True except ImportError: BLENDER_AVAILABLE = False # 导入依赖模块 # ==================== 加工管理器类 ==================== class MachiningManager: """加工管理器 - 负责所有加工相关操作""" def __init__(self): """ 初始化加工管理器 - 完全独立,不依赖suw_impl """ # 使用全局数据管理器 self.data_manager = get_data_manager() # 【新增】添加材质管理器引用 from .material_manager import get_material_manager self.material_manager = get_material_manager() # 加工数据存储 self.machinings = {} # 加工统计 self.machining_stats = { "machinings_created": 0, "trim_operations": 0, "creation_errors": 0 } logger.info("✅ 加工管理器初始化完成") # ==================== 原始命令方法 ==================== def c05(self, data: Dict[str, Any]): """c05 - 添加加工 - 参考SUW IMPL实现,按板件分组批量创建""" try: logger.info("🔧 执行c05命令: 创建加工") if not BLENDER_AVAILABLE: logger.warning("Blender 不可用,跳过加工创建") return 0 uid = data.get("uid") items = data.get("items", []) logger.info(f"开始创建加工: uid={uid}, 项目数={len(items)}") # 【参考SUW IMPL】获取部件和硬件集合 parts = self._get_parts(data) hardwares = self._get_hardwares(data) # 【参考SUW IMPL】分类处理:可视化加工 vs 布尔运算 visual_works = [] boolean_works = [] for i, work in enumerate(items): # 【修复】不再跳过cancel=1的项目,所有项目都要创建 cp = work.get("cp") if not cp: continue # 【参考SUW IMPL】获取组件 component = None if cp in parts: component = parts[cp] elif cp in hardwares: component = hardwares[cp] if not component or not self._is_object_valid(component): logger.info(f"🚨 组件查找失败: cp={cp}, component={component}") continue work['component'] = component work['index'] = i if work.get("trim3d", 0) == 1: boolean_works.append(work) else: visual_works.append(work) created_count = 0 # 【参考SUW IMPL】1. 批量处理可视化加工 if visual_works: created_count += self._create_visual_machining_batch_suw_style( visual_works, uid) # 【参考SUW IMPL】2. 批量处理布尔运算 if boolean_works: created_count += self._create_boolean_machining_batch_suw_style( boolean_works) logger.info(f"✅ c05创建加工完成: {created_count} 个对象") return created_count except Exception as e: logger.error(f"c05创建加工异常: {e}") return 0 def _create_visual_machining_batch_suw_style(self, visual_works, uid): """批量创建可视化加工对象 - 参考SUW IMPL实现,支持材质区分""" try: import bmesh created_count = 0 # 【参考SUW IMPL】按组件分组,同一组件的加工可以批量创建 component_groups = {} for work in visual_works: component = work['component'] if component not in component_groups: component_groups[component] = [] component_groups[component].append(work) for component, works in component_groups.items(): logger.info(f"🔨 为组件 {component.name} 批量创建 {len(works)} 个加工对象") # 【参考SUW IMPL】创建主加工组 main_machining = bpy.data.objects.new( f"Machining_{component.name}", None) bpy.context.scene.collection.objects.link(main_machining) main_machining.parent = component main_machining["sw_typ"] = "work" # 【参考SUW IMPL】创建记录标准化,为c0a对称删除做准备 import time creation_record = { "type": "visual_batch", "main_machining": main_machining.name, "geometry_objects": [], "material_applied": None, "created_timestamp": time.time() } # 添加到数据管理器 self.data_manager.add_machining(uid, main_machining) # 【修复】按cancel状态分组,分别创建不同材质的几何体 active_works = [] # cancel=0,蓝色材质 cancelled_works = [] # cancel=1,灰色材质 for work in works: if work.get("cancel", 0) == 1: cancelled_works.append(work) else: active_works.append(work) # 创建有效加工组(蓝色) if active_works: created_count += self._create_work_group_with_material( main_machining, active_works, "active") # 创建取消加工组(灰色) if cancelled_works: created_count += self._create_work_group_with_material( main_machining, cancelled_works, "cancelled") return created_count except Exception as e: logger.error(f"批量创建可视化加工失败: {e}") return 0 def _create_work_group_with_material(self, main_machining, works, work_type): """为指定材质类型创建工作组""" try: if not works: return 0 import bmesh created_count = 0 # 创建bmesh bm = bmesh.new() for work in works: try: # 解析坐标 p1 = self._parse_point3d(work.get("p1", "(0,0,0)")) p2 = self._parse_point3d(work.get("p2", "(0,0,0)")) # 根据类型创建几何体 if "tri" in work: self._add_triangle_to_bmesh_suw_style(bm, work, p1, p2) elif "surf" in work: self._add_surface_to_bmesh_suw_style(bm, work, p1, p2) else: self._add_circle_to_bmesh_suw_style(bm, work, p1, p2) created_count += 1 except Exception as e: logger.error(f"创建单个加工几何体失败: {e}") # 创建网格对象 if bm.verts: mesh = bpy.data.meshes.new( f"MachiningMesh_{main_machining.name}_{work_type}") bm.to_mesh(mesh) mesh.update() # 创建对象 mesh_obj = bpy.data.objects.new( f"MachiningGeometry_{main_machining.name}_{work_type}", mesh) bpy.context.scene.collection.objects.link(mesh_obj) mesh_obj.parent = main_machining # 【修复】应用对应材质 try: if hasattr(self, 'material_manager'): if work_type == "active": # 蓝色材质 - 有效加工 self.material_manager.apply_machining_material(mesh_obj) else: # 灰色材质 - 取消的加工 self.material_manager.apply_cancelled_machining_material(mesh_obj) except Exception as e: logger.warning(f"应用材质失败: {e}") bm.free() return created_count except Exception as e: logger.error(f"创建工作组失败: {e}") return 0 def _add_circle_to_bmesh_suw_style(self, bm, work, p1, p2): """向bmesh添加圆形几何体 - 参考SUW IMPL实现,修复孔位朝向""" try: import bmesh dia = work.get("dia", 5.0) radius = dia * 0.001 / 2.0 # 【参考SUW IMPL】计算方向和位置 if BLENDER_AVAILABLE: import mathutils # 转换为mathutils.Vector p1_vec = mathutils.Vector(p1) p2_vec = mathutils.Vector(p2) # 计算方向和长度 direction = p2_vec - p1_vec length = direction.length midpoint = (p1_vec + p2_vec) / 2 if length < 0.0001: logger.warning("圆柱体长度过短,跳过创建") return logger.debug(f"🔧 创建圆柱体: 半径={radius:.3f}, 长度={length:.3f}") # 【参考SUW IMPL】计算旋转矩阵 - 将Z轴对齐到加工方向 # 使用rotation_difference计算精确旋转,避免万向节锁 z_axis = mathutils.Vector((0, 0, 1)) rotation_quat = z_axis.rotation_difference( direction.normalized()) rotation_matrix = rotation_quat.to_matrix().to_4x4() # 组合变换矩阵: 先旋转,再平移 translation_matrix = mathutils.Matrix.Translation(midpoint) final_transform_matrix = translation_matrix @ rotation_matrix # 在临时bmesh中创建标准圆柱体 temp_bm = bmesh.new() bmesh.ops.create_cone( temp_bm, cap_ends=True, # 生成端盖 cap_tris=False, # 端盖用 n 边而非三角 segments=12, radius1=radius, radius2=radius, # 与 radius1 相同 → 圆柱 depth=length ) # 应用变换矩阵 bmesh.ops.transform( temp_bm, matrix=final_transform_matrix, verts=temp_bm.verts) # 将变换后的几何体合并到主bmesh vert_map = {} for v in temp_bm.verts: new_v = bm.verts.new(v.co) vert_map[v] = new_v for f in temp_bm.faces: bm.faces.new(tuple(vert_map[v] for v in f.verts)) temp_bm.free() logger.debug( f"✅ 圆柱体变换完成: 世界坐标中点({midpoint.x:.3f}, {midpoint.y:.3f}, {midpoint.z:.3f})") else: # 非Blender环境的简化版本 direction = (p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]) length = (direction[0]**2 + direction[1] ** 2 + direction[2]**2)**0.5 center = ((p1[0] + p2[0])/2, (p1[1] + p2[1]) / 2, (p1[2] + p2[2])/2) # 创建圆柱体(简化版本,不做旋转) bmesh.ops.create_cone( bm, cap_ends=True, cap_tris=False, segments=12, radius1=radius, radius2=radius, depth=max(length, 0.01) ) # 移动到正确位置 bmesh.ops.translate( bm, vec=center, verts=bm.verts[-24:] # 圆柱体的顶点 ) except Exception as e: logger.error(f"添加圆形到bmesh失败: {e}") import traceback logger.error(traceback.format_exc()) def _add_triangle_to_bmesh_suw_style(self, bm, work, p1, p2): """向bmesh添加三角形几何体 - 参考SUW IMPL实现""" try: # 获取第三个点 tri = self._parse_point3d(work.get("tri", "(0,0,0)")) p3 = self._parse_point3d(work.get("p3", "(0,0,0)")) # 计算三角形顶点 pts = [ tri, (tri[0] + p2[0] - p1[0], tri[1] + p2[1] - p1[1], tri[2] + p2[2] - p1[2]), (p1[0] + p1[0] - tri[0], p1[1] + p1[1] - tri[1], p1[2] + p1[2] - tri[2]) ] # 创建三角形顶点 v1 = bm.verts.new(pts[0]) v2 = bm.verts.new(pts[1]) v3 = bm.verts.new(pts[2]) # 创建面 bm.faces.new([v1, v2, v3]) except Exception as e: logger.error(f"添加三角形到bmesh失败: {e}") def _add_surface_to_bmesh_suw_style(self, bm, work, p1, p2): """向bmesh添加表面几何体 - 参考SUW IMPL实现""" try: # 解析表面数据 surf = work.get("surf", {}) segs = surf.get("segs", []) if not segs: return # 简化的表面创建 # 这里需要根据实际的表面数据格式进行解析 # 暂时创建一个简单的平面 v1 = bm.verts.new(p1) v2 = bm.verts.new(p2) v3 = bm.verts.new([(p1[0] + p2[0])/2, p1[1], (p1[2] + p2[2])/2]) v4 = bm.verts.new([(p1[0] + p2[0])/2, p2[1], (p1[2] + p2[2])/2]) # 创建面 bm.faces.new([v1, v2, v3, v4]) except Exception as e: logger.error(f"添加表面到bmesh失败: {e}") def _create_boolean_machining_batch_suw_style(self, boolean_works): """批量创建布尔运算加工对象 - 参考SUW IMPL实现""" try: created_count = 0 # 按组件分组 component_groups = {} for work in boolean_works: component = work['component'] if component not in component_groups: component_groups[component] = [] component_groups[component].append(work) for component, works in component_groups.items(): logger.info( f"🔨 为组件 {component.name} 批量创建 {len(works)} 个布尔运算加工") # 按类型分组 circle_works = [] triangle_works = [] surface_works = [] for work in works: p1 = self._parse_point3d(work.get("p1", "(0,0,0)")) p2 = self._parse_point3d(work.get("p2", "(0,0,0)")) if "tri" in work: triangle_works.append((work, p1, p2)) elif "surf" in work: surface_works.append((work, p1, p2)) else: circle_works.append((work, p1, p2)) # 批量创建统一修剪器 if circle_works: unified_circle = self._create_unified_circle_trimmer_suw_style( circle_works, component.name) if unified_circle: created_count += len(circle_works) if triangle_works: unified_triangle = self._create_unified_triangle_trimmer_suw_style( triangle_works, component.name) if unified_triangle: created_count += len(triangle_works) if surface_works: unified_surface = self._create_unified_surface_trimmer_suw_style( surface_works, component.name) if unified_surface: created_count += len(surface_works) return created_count except Exception as e: logger.error(f"批量创建布尔运算加工失败: {e}") return 0 def _create_unified_circle_trimmer_suw_style(self, circle_data_list, component_name): """创建统一圆形剪切器 - 参考SUW IMPL实现""" try: if not circle_data_list: return None # 创建合并的圆形剪切器 bm = bmesh.new() for circle_data in circle_data_list: work, p1, p2 = circle_data # 使用相同的圆形创建逻辑 self._add_circle_to_bmesh_suw_style(bm, work, p1, p2) # 创建网格 mesh = bpy.data.meshes.new( f"UnifiedCircleTrimmer_{component_name}") bm.to_mesh(mesh) mesh.update() obj = bpy.data.objects.new( f"UnifiedCircleTrimmer_{component_name}", mesh) bpy.context.scene.collection.objects.link(obj) bm.free() return obj except Exception as e: logger.error(f"创建统一圆形剪切器失败: {e}") return None def _create_unified_triangle_trimmer_suw_style(self, triangle_data_list, component_name): """创建统一三角形剪切器 - 参考SUW IMPL实现""" try: if not triangle_data_list: return None # 简化实现 return None except Exception as e: logger.error(f"创建统一三角形剪切器失败: {e}") return None def _create_unified_surface_trimmer_suw_style(self, surface_data_list, component_name): """创建统一表面剪切器 - 参考SUW IMPL实现""" try: if not surface_data_list: return None # 简化实现 return None except Exception as e: logger.error(f"创建统一表面剪切器失败: {e}") return None def _create_cylinder_ultra_safe(self, machining, p1, p2, diameter, index): """使用超安全的方法创建圆柱体 - 避免所有依赖图问题""" try: # 计算长度和中心点 length = math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2 + (p2[2] - p1[2])**2) if length < 0.001: length = 0.001 center = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2, (p1[2] + p2[2]) / 2] radius = (diameter * 0.001) / 2.0 # mm -> m # 【修复】使用时间戳确保唯一命名 import time timestamp = int(time.time() * 1000) % 100000 unique_id = f"{machining.name}_{index}_{timestamp}" # 【修复】使用更简单的命名避免组件ID冲突 mesh_name = f"Mesh_{unique_id}" obj_name = f"Cylinder_{unique_id}" # 【修复】检查名称是否已存在 if mesh_name in bpy.data.meshes: bpy.data.meshes.remove(bpy.data.meshes[mesh_name]) if obj_name in bpy.data.objects: bpy.data.objects.remove(bpy.data.objects[obj_name]) # 创建网格数据 mesh = bpy.data.meshes.new(mesh_name) # 使用简单的顶点和面创建圆柱体 vertices = [] faces = [] # 创建6个分段的圆柱体(减少复杂度) segments = 6 for i in range(segments): angle = 2 * math.pi * i / segments cos_val = math.cos(angle) sin_val = math.sin(angle) # 第一个端面 x1 = center[0] + radius * cos_val y1 = center[1] + radius * sin_val z1 = center[2] - length / 2 vertices.append((x1, y1, z1)) # 第二个端面 x2 = center[0] + radius * cos_val y2 = center[1] + radius * sin_val z2 = center[2] + length / 2 vertices.append((x2, y2, z2)) # 创建侧面 for i in range(segments): v1 = i * 2 v2 = (i + 1) % segments * 2 v3 = (i + 1) % segments * 2 + 1 v4 = i * 2 + 1 faces.append((v1, v2, v3, v4)) # 创建端面 end1_verts = list(range(0, segments * 2, 2)) if len(end1_verts) >= 3: faces.append(end1_verts) end2_verts = list(range(1, segments * 2, 2)) if len(end2_verts) >= 3: faces.append(end2_verts) # 【修复】安全的网格创建 try: mesh.from_pydata(vertices, [], faces) mesh.update() except Exception as e: logger.error(f"创建网格数据失败: {e}") bpy.data.meshes.remove(mesh) return None # 【修复】安全的对象创建 try: cylinder_obj = bpy.data.objects.new(obj_name, mesh) except Exception as e: logger.error(f"创建对象失败: {e}") bpy.data.meshes.remove(mesh) return None # 【修复】安全的场景链接 try: bpy.context.scene.collection.objects.link(cylinder_obj) except Exception as e: logger.error(f"链接到场景失败: {e}") bpy.data.objects.remove(cylinder_obj) bpy.data.meshes.remove(mesh) return None # 【修复】安全的父对象设置 try: if machining and machining.name in bpy.data.objects: cylinder_obj.parent = machining except Exception as e: logger.warning(f"设置父对象失败: {e}") # 【修复】安全的属性设置 try: cylinder_obj["sw_typ"] = "work" cylinder_obj["sw_special"] = 0 except Exception as e: logger.warning(f"设置属性失败: {e}") # 【修复】强制更新对象 try: cylinder_obj.update_tag() bpy.context.view_layer.update() except: pass return cylinder_obj except Exception as e: logger.error(f"创建超安全圆柱体失败: {e}") return None def _create_simple_surface(self, machining, item, index): """创建简单的表面几何体 - 修复版本""" try: surf = item.get("surf", {}) segs = surf.get("segs", []) if not segs: return None # 【修复】使用时间戳确保唯一命名 import time timestamp = int(time.time() * 1000) % 100000 unique_id = f"{machining.name}_{index}_{timestamp}" mesh_name = f"SurfaceMesh_{unique_id}" obj_name = f"Surface_{unique_id}" # 【修复】检查名称是否已存在 if mesh_name in bpy.data.meshes: bpy.data.meshes.remove(bpy.data.meshes[mesh_name]) if obj_name in bpy.data.objects: bpy.data.objects.remove(bpy.data.objects[obj_name]) # 创建网格数据 mesh = bpy.data.meshes.new(mesh_name) # 【修复】使用更安全的方法创建平面 try: # 使用bmesh创建平面 bm = bmesh.new() # 使用正确的操作符名称 bmesh.ops.create_grid( bm, x_segments=1, y_segments=1, size=0.1 ) # 转换为网格 bm.to_mesh(mesh) mesh.update() bm.free() except Exception as e: logger.error(f"创建表面网格失败: {e}") bpy.data.meshes.remove(mesh) return None # 【修复】安全的对象创建 try: surface_obj = bpy.data.objects.new(obj_name, mesh) bpy.context.scene.collection.objects.link(surface_obj) except Exception as e: logger.error(f"创建表面对象失败: {e}") bpy.data.meshes.remove(mesh) return None # 【修复】安全的父对象设置 try: if machining and machining.name in bpy.data.objects: surface_obj.parent = machining except Exception as e: logger.warning(f"设置父对象失败: {e}") # 【修复】安全的属性设置 try: surface_obj["sw_typ"] = "work" surface_obj["sw_special"] = 0 except Exception as e: logger.warning(f"设置属性失败: {e}") return surface_obj except Exception as e: logger.error(f"创建简单表面失败: {e}") return None def c0a(self, data: Dict[str, Any]): """del_machining - 删除加工 - 最终版本,处理已删除对象的引用问题""" try: logger.info("🗑️ 执行c0a命令: 删除加工") uid = data.get("uid") typ = data.get("typ") # type is unit or source oid = data.get("oid") special = data.get("special", 1) logger.info( f"🗑️ 删除加工参数: uid={uid}, typ={typ}, oid={oid}, special={special}") # 获取加工数据 machinings = self.data_manager.get_machinings(uid) if not machinings: logger.info(f"未找到单元 {uid} 的加工数据") return 0 logger.info(f"🔍 找到 {len(machinings)} 个加工对象") deleted_count = 0 # 【修复】使用更安全的删除策略:先收集要删除的对象名称,再批量删除 objects_to_delete = [] # 第一步:收集要删除的对象名称 for entity in machinings: try: # 检查对象是否有效 if not self._is_object_valid(entity): continue # 条件1: typ匹配 typ_match = False if typ == "uid": typ_match = True else: # 尝试获取属性,如果不存在则返回None try: entity_attr = entity.get("sw_" + typ, None) typ_match = (entity_attr == oid) except Exception as e: logger.debug(f"获取对象属性失败: {e}") continue # 条件2: special匹配 special_match = False if special == 1: special_match = True else: # special == 0 # 获取sw_special属性,如果不存在则默认为0 try: entity_special = entity.get("sw_special", 0) special_match = (entity_special == 0) except Exception as e: logger.debug(f"获取special属性失败: {e}") continue # 如果两个条件都满足,添加到删除列表 if typ_match and special_match: logger.info(f"🗑️ 标记删除加工对象: {entity.name}") objects_to_delete.append(entity.name) except Exception as e: logger.debug(f"处理加工对象时出错: {e}") continue # 第二步:批量删除收集到的对象 logger.info(f"🔍 开始删除 {len(objects_to_delete)} 个对象") for obj_name in objects_to_delete: try: # 使用名称查找对象 if obj_name in bpy.data.objects: obj = bpy.data.objects[obj_name] if self._is_object_valid(obj): if self._delete_object_safe(obj): deleted_count += 1 else: logger.debug(f"删除对象失败: {obj_name}") else: logger.debug(f"对象已无效: {obj_name}") # 如果对象无效,检查是否已经被删除 if obj_name not in bpy.data.objects: deleted_count += 1 else: logger.debug(f"对象不在Blender数据中: {obj_name}") # 如果对象不在数据中,认为已经被删除 deleted_count += 1 except Exception as e: logger.debug(f"删除对象时出错: {e}") # 检查对象是否已经被删除 try: if obj_name not in bpy.data.objects: deleted_count += 1 except: pass continue # 【修复】按照Ruby版本:最后清理已删除的对象 self.data_manager.cleanup_machinings(uid) logger.info(f"✅ 删除加工完成: {deleted_count} 个对象") return deleted_count except Exception as e: logger.error(f"c0a命令执行失败: {e}") return 0 # ==================== 核心实现方法 ==================== def _create_machining_object(self, uid): """为本次加工批次创建一个空的父对象,并注册到data_manager - 修复版本""" try: import bpy # 检查Blender可用性 if not BLENDER_AVAILABLE: logger.error("Blender不可用,无法创建加工对象") return None # 检查uid是否有效 if not uid: logger.error("无效的uid") return None name = f"Machining_{uid}" # 检查是否已存在同名对象 if name in bpy.data.objects: logger.info(f"加工对象已存在: {name}") return bpy.data.objects[name] # 创建空对象 try: obj = bpy.data.objects.new(name, None) except Exception as e: logger.error(f"创建对象失败: {e}") return None # 检查对象是否创建成功 if not obj: logger.error("对象创建失败") return None # 链接到场景 try: if hasattr(bpy.context, 'scene') and bpy.context.scene: bpy.context.scene.collection.objects.link(obj) else: logger.error("无法获取场景") bpy.data.objects.remove(obj) return None except Exception as e: logger.error(f"链接对象到场景失败: {e}") bpy.data.objects.remove(obj) return None # 设置属性 try: obj["sw_typ"] = "work" except Exception as e: logger.warning(f"设置对象属性失败: {e}") # 添加到数据管理器 try: self.data_manager.add_machining(uid, obj) except Exception as e: logger.warning(f"添加到数据管理器失败: {e}") logger.info(f"✅ 创建加工对象成功: {name}") return obj except Exception as e: logger.error(f"创建加工对象异常: {e}") return None def _parse_point3d(self, point_str): """解析3D点字符串 - 修复单位转换""" try: # 移除括号和空格 point_str = point_str.strip("()").replace(" ", "") coords = point_str.split(",") if len(coords) >= 3: # 【修复】单位转换:毫米转米 x = float(coords[0]) * 0.001 # mm -> m y = float(coords[1]) * 0.001 # mm -> m z = float(coords[2]) * 0.001 # mm -> m return [x, y, z] else: return [0.0, 0.0, 0.0] except Exception as e: logger.error(f"解析3D点失败: {e}") return [0.0, 0.0, 0.0] def _parse_surface_vertices(self, surface): """解析表面顶点""" try: # 简化的表面解析 vertices = [] # 这里应该根据实际的表面数据格式解析 return vertices except Exception as e: logger.error(f"解析表面顶点失败: {e}") return [] def _set_machining_color(self, machining, item): """设置加工颜色""" try: # 获取加工材质 material = material_manager.get_texture("mat_machining") if material and hasattr(machining, 'data') and machining.data: if not machining.data.materials: machining.data.materials.append(material) else: machining.data.materials[0] = material except Exception as e: logger.error(f"设置加工颜色失败: {e}") def _is_object_valid(self, obj) -> bool: """检查对象是否有效 - 增强版本""" try: if not obj: return False if not BLENDER_AVAILABLE: return True # 【修复】更强的有效性检查 try: # 检查对象是否有name属性 if not hasattr(obj, 'name'): return False # 检查name是否为空 if not obj.name: return False # 检查对象是否在Blender数据中 if obj.name not in bpy.data.objects: return False # 尝试访问对象属性来验证其有效性 test_name = obj.name return True except Exception as e: logger.debug(f"对象有效性检查失败: {e}") return False except Exception as e: logger.debug(f"对象有效性检查异常: {e}") return False def _delete_object_safe(self, obj) -> bool: """安全删除对象 - 最终版本,处理已删除对象的引用问题""" try: if not obj or not BLENDER_AVAILABLE: return False # 【修复】更强的对象有效性检查 try: # 检查对象是否仍然存在于Blender数据中 if obj.name not in bpy.data.objects: logger.debug(f"对象 {obj.name} 已不在Blender数据中") return True # 如果对象已经不在数据中,认为删除成功 # 检查对象是否仍然有效(没有被删除) if not hasattr(obj, 'name') or not obj.name: logger.debug("对象已无效(没有name属性)") return True # 如果对象已经无效,认为删除成功 except Exception as e: logger.debug(f"对象有效性检查失败: {e}") return True # 如果检查失败,认为对象已经被删除 # 【修复】使用更安全的删除策略 try: # 先收集所有子对象(使用名称而不是对象引用) children_names = [] try: if hasattr(obj, 'children'): for child in obj.children: try: if child and hasattr(child, 'name') and child.name in bpy.data.objects: children_names.append(child.name) except Exception as e: logger.debug(f"检查子对象时出错: {e}") continue except Exception as e: logger.debug(f"获取子对象列表失败: {e}") # 先删除子对象(使用名称查找) for child_name in children_names: try: if child_name in bpy.data.objects: child_obj = bpy.data.objects[child_name] # 再次检查对象是否有效 if hasattr(child_obj, 'name') and child_obj.name == child_name: bpy.data.objects.remove( child_obj, do_unlink=True) logger.debug(f"删除子对象: {child_name}") except Exception as e: logger.debug(f"删除子对象 {child_name} 失败: {e}") # 再删除父对象 try: # 最终检查对象是否仍然有效 if obj.name in bpy.data.objects: # 再次验证对象引用是否有效 try: if hasattr(obj, 'name') and obj.name in bpy.data.objects: bpy.data.objects.remove(obj, do_unlink=True) logger.debug(f"删除父对象: {obj.name}") return True else: logger.debug(f"父对象 {obj.name} 在删除前已无效") return True # 对象已经无效,认为删除成功 except Exception as e: logger.debug(f"删除父对象时出错: {e}") # 检查对象是否已经被删除 if obj.name not in bpy.data.objects: logger.debug(f"父对象 {obj.name} 已被删除") return True return False else: logger.debug(f"父对象 {obj.name} 已不在Blender数据中") return True # 对象已经不在数据中,认为删除成功 except Exception as e: logger.debug(f"删除父对象过程中出错: {e}") # 检查对象是否已经被删除 try: if obj.name not in bpy.data.objects: logger.debug(f"父对象 {obj.name} 已被删除") return True except: pass return False except Exception as e: logger.debug(f"删除对象过程中出错: {e}") # 检查对象是否已经被删除 try: if obj.name not in bpy.data.objects: logger.debug(f"对象 {obj.name} 已被删除") return True except: pass return False except Exception as e: logger.debug(f"删除对象失败: {e}") # 最后检查对象是否已经被删除 try: if obj.name not in bpy.data.objects: logger.debug(f"对象 {obj.name} 已被删除") return True except: pass return False def _get_parts(self, data: Dict[str, Any]) -> Dict[str, Any]: """获取部件数据 - 使用data_manager""" return self.data_manager.get_parts(data) def _get_hardwares(self, data: Dict[str, Any]) -> Dict[str, Any]: """获取硬件数据 - 使用data_manager""" return self.data_manager.get_hardwares(data) # ==================== 统计和管理方法 ==================== def get_machining_stats(self) -> Dict[str, Any]: """获取加工统计信息""" try: total_machinings = sum(len(self.data_manager.get_machinings(uid)) for uid in self.data_manager.machinings.keys()) stats = { "total_units": len(self.data_manager.machinings), "total_machinings": total_machinings, "creation_stats": self.machining_stats.copy(), "memory_usage": { "machinings_dict_size": len(self.data_manager.machinings), } } if BLENDER_AVAILABLE: stats["blender_objects"] = len([obj for obj in bpy.data.objects if obj.get("sw_typ") == "work"]) return stats except Exception as e: logger.error(f"获取加工统计失败: {e}") return {"error": str(e)} def get_creation_stats(self) -> Dict[str, Any]: """获取创建统计信息""" return self.machining_stats.copy() def reset_creation_stats(self): """重置创建统计""" self.machining_stats = { "machinings_created": 0, "trim_operations": 0, "creation_errors": 0 } logger.info("加工统计已重置") def cleanup(self): """清理加工管理器""" try: # 清理所有加工数据 total_deleted = 0 for uid in list(self.data_manager.machinings.keys()): machinings = self.data_manager.get_machinings(uid) for machining in machinings: if self._delete_object_safe(machining): total_deleted += 1 self.data_manager.clear_machinings(uid) self.reset_creation_stats() logger.info(f"✅ 加工管理器清理完成,删除了 {total_deleted} 个对象") except Exception as e: logger.error(f"清理加工管理器失败: {e}") # ==================== 全局加工管理器实例 ==================== # 全局实例 machining_manager = None def init_machining_manager(): """初始化全局加工管理器实例 - 不再需要suw_impl参数""" global machining_manager machining_manager = MachiningManager() return machining_manager def get_machining_manager(): """获取全局加工管理器实例""" global machining_manager if machining_manager is None: machining_manager = init_machining_manager() return machining_manager