x
This commit is contained in:
@@ -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]] = []
|
||||
|
||||
Reference in New Issue
Block a user