This commit is contained in:
2026-08-31 18:01:34 +08:00
parent 3ea59551db
commit bee439cf34
46 changed files with 1884 additions and 1898 deletions
+140 -8
View File
@@ -38,6 +38,26 @@ logger = get_logger(__name__)
class CADExporter:
"""CAD 文件导出器"""
# 组件 -> (cavity_data 取值键, 显示名)
_SHAPE_MAP = {
"cavity": ("cavity", "型腔"),
"core": ("core", "型芯"),
"parting_surface": ("parting_surface", "分型面"),
"product": ("product", "产品本体"),
"a_plate": ("a_plate", "A板(上模)"),
"b_plate": ("b_plate", "B板(下模)"),
}
_COMPONENT_LABELS = {
"cavity": "型腔",
"core": "型芯",
"parting_surface": "分型面",
"product": "产品本体",
"a_plate": "A板(上模)",
"b_plate": "B板(下模)",
"assembly": "模具装配体",
}
def __init__(self, output_dir: str = "./exports"):
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
@@ -71,6 +91,125 @@ class CADExporter:
relative = Path(os.path.basename(filepath))
return relative.as_posix()
def build_file_entry(self, component: str, fmt: str, filepath: str,
label: Optional[str] = None) -> Dict[str, Any]:
"""构造统一格式的导出文件条目(供导出/重建流程复用)。"""
file_size = os.path.getsize(filepath) if os.path.exists(filepath) else 0
return {
"component": component,
"component_label": label or self._COMPONENT_LABELS.get(component, component),
"format": fmt,
"filepath": filepath,
"relative_path": self.get_relative_path(filepath),
"filename": os.path.basename(filepath),
"size_bytes": file_size,
"size_readable": self._format_file_size(file_size),
}
def export_persisted_steps(
self,
cavity_data: Dict,
base_filename: str,
components: Optional[List[str]] = None,
task_id: Optional[str] = None,
scheme_id: Optional[str] = None,
) -> Dict[str, Any]:
"""持久化方案几何为 STEP:装配体单文件 + 逐组件单文件。
逐组件 STEP 是服务重启后按需重导出其他格式(STL/IGES/BRep)的
几何来源:读回单组件 STEP 即可现场转换,用户无需重新分析。
"""
if components is None:
components = list(self._SHAPE_MAP.keys())
export_dir = self.build_export_dir(
base_filename=base_filename,
task_id=task_id,
scheme_id=scheme_id,
)
os.makedirs(export_dir, exist_ok=True)
results = {"files": [], "errors": []}
shapes_with_names: List[Tuple[TopoDS_Shape, str]] = []
for comp in components:
data_key, label = self._SHAPE_MAP.get(comp, (comp, comp))
shape = cavity_data.get(data_key)
if shape is None:
results["errors"].append(f"{label}形状不可用")
continue
shapes_with_names.append((shape, label))
# 逐组件单文件 STEP(重启后转换其他格式的几何来源)
filepath = os.path.join(export_dir, f"{base_filename}_{comp}.step")
if self.export_step(shape, filepath):
results["files"].append(
self.build_file_entry(comp, "step", filepath, label)
)
# 装配体 STEP(所有组件写入同一文件)
if shapes_with_names:
filepath = os.path.join(export_dir, f"{base_filename}_mold.step")
if self.export_assembly_step(shapes_with_names, filepath):
results["files"].append(
self.build_file_entry("assembly", "step", filepath)
)
results["total_files"] = len(results["files"])
results["total_errors"] = len(results["errors"])
logger.info(
f"方案 STEP 持久化完成: {results['total_files']} 个文件, "
f"{results['total_errors']} 个错误"
)
return results
def convert_component_step(self, step_path: str, output_path: str, fmt: str) -> bool:
"""读回单组件 STEP,转换导出为其他格式(stl/iges/brep/step)。
用于内存 shape 缓存失效后的按需重导出。
"""
shape = self._read_step_shape(step_path)
if shape is None:
return False
if fmt == "stl":
return self.export_stl(shape, output_path)
if fmt == "iges":
return self.export_iges(shape, output_path)
if fmt == "brep":
return self.export_brep(shape, output_path)
if fmt == "step":
import shutil
try:
shutil.copyfile(step_path, output_path)
return os.path.exists(output_path) and os.path.getsize(output_path) > 0
except Exception as e:
logger.error(f"STEP 复制失败: {e}")
return False
logger.error(f"不支持的转换格式: {fmt}")
return False
def _read_step_shape(self, filepath: str) -> Optional[TopoDS_Shape]:
"""读回 STEP 文件为 OCC 形状"""
try:
from OCC.Core.STEPControl import STEPControl_Reader
from OCC.Core.IFSelect import IFSelect_RetDone
if not os.path.exists(filepath):
logger.error(f"STEP 文件不存在: {filepath}")
return None
reader = STEPControl_Reader()
if reader.ReadFile(filepath) != IFSelect_RetDone:
logger.error(f"STEP 读回失败(状态非 Done): {filepath}")
return None
reader.TransferRoots()
return reader.OneShape()
except Exception as e:
logger.error(f"STEP 读回失败: {filepath}: {e}")
return None
def export_step(self, shape: TopoDS_Shape, filepath: str,
schema: str = "AP214") -> bool:
"""
@@ -262,14 +401,7 @@ class CADExporter:
"errors": [],
}
shape_map = {
"cavity": ("cavity", "型腔"),
"core": ("core", "型芯"),
"parting_surface": ("parting_surface", "分型面"),
"product": ("product", "产品本体"),
"a_plate": ("a_plate", "A板(上模)"),
"b_plate": ("b_plate", "B板(下模)"),
}
shape_map = self._SHAPE_MAP
shapes_to_export: List[Tuple[str, str, TopoDS_Shape]] = []
assembly_shapes: List[Tuple[TopoDS_Shape, str]] = []