ModelHandle/tools/step_service/freecad_worker.py
sladro 16a2e43649
Some checks failed
Build / build (18.x, macos-latest) (push) Has been cancelled
Build / build (18.x, ubuntu-latest) (push) Has been cancelled
Build / build (18.x, windows-latest) (push) Has been cancelled
Build / build (20.x, macos-latest) (push) Has been cancelled
Build / build (20.x, ubuntu-latest) (push) Has been cancelled
Build / build (20.x, windows-latest) (push) Has been cancelled
fix step save flow and add portable backend runtime support
2026-04-13 15:16:30 +08:00

90 lines
3.2 KiB
Python

import json
import os
import pathlib
import subprocess
import tempfile
from .step_tree import normalize_deleted_paths
class FreeCADWorker:
def __init__(self, freecad_cmd="FreeCADCmd", project_root=None):
self.freecad_cmd = freecad_cmd
if project_root is None:
self.project_root = pathlib.Path(__file__).resolve().parents[2]
else:
self.project_root = pathlib.Path(project_root)
def get_freecad_cmd(self):
if isinstance(self.freecad_cmd, str) and self.freecad_cmd == "FreeCADCmd":
project_freecad_cmd = self._get_project_freecad_cmd()
if project_freecad_cmd is not None:
return project_freecad_cmd
return self.freecad_cmd
def _get_project_freecad_cmd(self):
runtime_dir = self.project_root / "runtime"
candidate_commands = [
runtime_dir / "freecad" / "bin" / "FreeCADCmd.exe",
runtime_dir / "FreeCAD" / "bin" / "FreeCADCmd.exe",
]
for child in runtime_dir.iterdir() if runtime_dir.exists() else []:
child_name = child.name.lower()
if "freecad" not in child_name:
continue
candidate_commands.extend(
[
child / "bin" / "FreeCADCmd.exe",
child / "bin" / "freecadcmd.exe",
]
)
for project_freecad_cmd in candidate_commands:
if project_freecad_cmd.exists():
return project_freecad_cmd
return None
def trim_step_file(self, file_name, file_bytes, deleted_paths):
normalized_paths = normalize_deleted_paths(deleted_paths)
with tempfile.TemporaryDirectory(prefix="o3dv-step-") as temp_dir:
temp_path = pathlib.Path(temp_dir)
input_path = temp_path / file_name
output_name = self._build_output_name(file_name)
output_path = temp_path / output_name
delete_manifest_path = temp_path / "deleted_paths.json"
input_path.write_bytes(file_bytes)
delete_manifest_path.write_text(
json.dumps(normalized_paths),
encoding="utf-8",
)
subprocess.run(
[
os.fspath(self.get_freecad_cmd()),
os.fspath(pathlib.Path(__file__).with_name("freecad_trim_step.py")),
"--pass",
os.fspath(input_path),
os.fspath(delete_manifest_path),
os.fspath(output_path),
],
check=True,
capture_output=True,
text=True,
)
if not output_path.exists():
raise RuntimeError(
"FreeCAD did not produce the expected STEP output file."
)
return output_path.read_bytes(), output_name
def _build_output_name(self, file_name):
lower_name = file_name.lower()
if lower_name.endswith(".step"):
return file_name[:-5] + "-edited.step"
if lower_name.endswith(".stp"):
return file_name[:-4] + "-edited.stp"
return file_name + "-edited.step"