1170 lines
44 KiB
Python
1170 lines
44 KiB
Python
|
#!/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
|