From cea5324da53b49986bc30685b1e3a763b151410b Mon Sep 17 00:00:00 2001 From: SZCJW <792430652@qq.com> Date: Sun, 8 Mar 2026 15:13:42 +0800 Subject: [PATCH] x --- src/services/verification_service.py | 121 ++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/src/services/verification_service.py b/src/services/verification_service.py index 1391453..10771d7 100644 --- a/src/services/verification_service.py +++ b/src/services/verification_service.py @@ -68,18 +68,28 @@ class GeometryVerificationService: # 构建命令 - 使用 FreeCAD 命令行模式 cmd = None - # 尝试1: freecad.cmd (snap 安装的命令行模式) - for cmd_name in ['freecad.cmd', '/snap/bin/freecad.cmd', 'freecad-cmd']: + # 尝试多种 FreeCAD 命令名称 + # PPA 版本通常使用 freecad 或 freecadcmd + # snap 版本使用 freecad.cmd + for cmd_name in ['freecadcmd', 'freecad-cmd', 'freecad', '/usr/bin/freecad', '/usr/bin/freecadcmd', '/snap/bin/freecad.cmd']: try: result = subprocess.run(['which', cmd_name], capture_output=True, text=True) if result.returncode == 0 and result.stdout.strip(): # 使用绝对路径 cmd = [cmd_name, str(self.verification_script), str(Path(stp_path_str).absolute())] - logger.debug(f"找到 FreeCAD 命令: {cmd_name}") + logger.info(f"找到 FreeCAD 命令: {cmd_name}") break except: pass + # 如果 which 找不到,尝试直接检查常见路径 + if not cmd: + for cmd_path in ['/usr/bin/freecad', '/usr/bin/freecadcmd', '/usr/local/bin/freecad', '/snap/bin/freecad.cmd']: + if Path(cmd_path).exists(): + cmd = [cmd_path, str(self.verification_script), str(Path(stp_path_str).absolute())] + logger.info(f"找到 FreeCAD 命令路径: {cmd_path}") + break + # 尝试2: freecad with offscreen if not cmd: try: @@ -147,6 +157,11 @@ class GeometryVerificationService: if stderr_text: logger.warning(f"验证脚本 stderr: {stderr_text[:500]}") + # 检查是否有 FreeCAD 权限错误 + if 'Cannot read STEP file' in stderr_text or 'Cannot read STEP file' in stdout_text: + logger.warning("FreeCAD 无法读取文件(可能是 snap 沙盒限制),使用 PythonOCC 验证") + return self._verify_with_pythonocc_only(stp_path_str) + if process.returncode == 0: # 尝试读取生成的报告文件(在 STP 文件目录下) report_path = stp_dir / (stp_abs_path.stem + "_verification_report.json") @@ -241,6 +256,106 @@ class GeometryVerificationService: return result + def _verify_with_pythonocc_only(self, stp_path: str) -> Dict[str, Any]: + """ + 仅使用 PythonOCC 验证(当 FreeCAD 不可用时) + + Args: + stp_path: STP 文件路径 + + Returns: + 验证结果 + """ + try: + from OCC.Core.STEPControl import STEPControl_Reader + from OCC.Core.IFSelect import IFSelect_RetDone + from OCC.Core.GProp import GProp_GProps + from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties + from OCC.Core.Bnd import Bnd_Box + from OCC.Core.BRepBndLib import brepbndlib_Add + from OCC.Core.TopExp import TopExp_Explorer + from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX, TopAbs_SOLID + + logger.info(f"使用 PythonOCC 进行验证: {stp_path}") + + # 读取 STP 文件 + reader = STEPControl_Reader() + status = reader.ReadFile(stp_path) + + if status != IFSelect_RetDone: + return { + "status": "error", + "error": "无法读取 STP 文件", + "timestamp": datetime.now().isoformat() + } + + reader.TransferRoots() + shape = reader.OneShape() + + # 计算体积 + vol_props = GProp_GProps() + brepgprop_VolumeProperties(shape, vol_props) + volume_mm3 = vol_props.Mass() + com = vol_props.CentreOfMass() + + # 计算表面积 + surf_props = GProp_GProps() + brepgprop_SurfaceProperties(shape, surf_props) + surface_area_mm2 = surf_props.Mass() + + # 计算边界框 + bbox = Bnd_Box() + brepbndlib_Add(shape, bbox) + xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() + + # 拓扑统计 + def count_topology(shape, top_type): + explorer = TopExp_Explorer(shape, top_type) + count = 0 + while explorer.More(): + count += 1 + explorer.Next() + return count + + return { + "status": "skipped", + "reason": "FreeCAD 无法访问文件(snap 沙盒限制),仅使用 PythonOCC 验证", + "timestamp": datetime.now().isoformat(), + "pythonocc": { + "volume_mm3": float(volume_mm3), + "volume_cm3": float(volume_mm3 / 1000), + "surface_area_mm2": float(surface_area_mm2), + "surface_area_cm2": float(surface_area_mm2 / 100), + "bounding_box": { + "x_min": float(xmin), + "x_max": float(xmax), + "y_min": float(ymin), + "y_max": float(ymax), + "z_min": float(zmin), + "z_max": float(zmax), + "x_length": float(xmax - xmin), + "y_length": float(ymax - ymin), + "z_length": float(zmax - zmin), + "center": [float((xmin + xmax) / 2), float((ymin + ymax) / 2), float((zmin + zmax) / 2)] + }, + "center_of_mass": [float(com.X()), float(com.Y()), float(com.Z())], + "topology": { + "faces": count_topology(shape, TopAbs_FACE), + "edges": count_topology(shape, TopAbs_EDGE), + "vertices": count_topology(shape, TopAbs_VERTEX), + "solids": count_topology(shape, TopAbs_SOLID) + } + } + } + + except Exception as e: + logger.error(f"PythonOCC 验证失败: {e}") + return { + "status": "error", + "error": str(e), + "timestamp": datetime.now().isoformat() + } + def verify_with_pythonocc(self, shape) -> Dict[str, Any]: """ 使用 PythonOCC 验证几何数据(同步方法,用于内部验证)