New file: runner.py with three entry points for silorunner: - dag_extract(input_path, output_path): extract feature DAG as JSON - validate(input_path, output_path): rebuild features, report pass/fail - export(input_path, output_path, format): export to STEP/IGES/STL/OBJ Invoked via: create --console -e 'from runner import dag_extract; ...' Closes kindred/create#217
157 lines
4.2 KiB
Python
157 lines
4.2 KiB
Python
"""Headless runner entry points for silorunner compute jobs.
|
|
|
|
These functions are invoked via ``create --console -e`` by the
|
|
silorunner binary. They must work without a display server.
|
|
|
|
Entry Points
|
|
------------
|
|
dag_extract(input_path, output_path)
|
|
Extract feature DAG and write JSON.
|
|
validate(input_path, output_path)
|
|
Rebuild all features and report pass/fail per node.
|
|
export(input_path, output_path, format='step')
|
|
Export geometry to STEP, IGES, STL, or OBJ.
|
|
"""
|
|
|
|
import json
|
|
|
|
import FreeCAD
|
|
|
|
|
|
def dag_extract(input_path, output_path):
|
|
"""Extract the feature DAG from a Create file.
|
|
|
|
Parameters
|
|
----------
|
|
input_path : str
|
|
Path to the ``.kc`` or ``.FCStd`` file.
|
|
output_path : str
|
|
Path to write the JSON output.
|
|
|
|
Output JSON::
|
|
|
|
{"nodes": [...], "edges": [...]}
|
|
"""
|
|
from dag import extract_dag
|
|
|
|
doc = FreeCAD.openDocument(input_path)
|
|
try:
|
|
nodes, edges = extract_dag(doc)
|
|
with open(output_path, "w") as f:
|
|
json.dump({"nodes": nodes, "edges": edges}, f)
|
|
FreeCAD.Console.PrintMessage(
|
|
f"DAG extracted: {len(nodes)} nodes, {len(edges)} edges -> {output_path}\n"
|
|
)
|
|
finally:
|
|
FreeCAD.closeDocument(doc.Name)
|
|
|
|
|
|
def validate(input_path, output_path):
|
|
"""Validate a Create file by rebuilding all features.
|
|
|
|
Parameters
|
|
----------
|
|
input_path : str
|
|
Path to the ``.kc`` or ``.FCStd`` file.
|
|
output_path : str
|
|
Path to write the JSON output.
|
|
|
|
Output JSON::
|
|
|
|
{
|
|
"valid": true/false,
|
|
"nodes": [
|
|
{"node_key": "Pad001", "state": "clean", "message": null, "properties_hash": "..."},
|
|
...
|
|
]
|
|
}
|
|
"""
|
|
from dag import classify_type, compute_properties_hash
|
|
|
|
doc = FreeCAD.openDocument(input_path)
|
|
try:
|
|
doc.recompute()
|
|
|
|
results = []
|
|
all_valid = True
|
|
|
|
for obj in doc.Objects:
|
|
if not hasattr(obj, "TypeId"):
|
|
continue
|
|
node_type = classify_type(obj.TypeId)
|
|
if node_type is None:
|
|
continue
|
|
|
|
state = "clean"
|
|
message = None
|
|
if hasattr(obj, "isValid") and not obj.isValid():
|
|
state = "failed"
|
|
message = f"Feature {obj.Label} failed to recompute"
|
|
all_valid = False
|
|
|
|
results.append(
|
|
{
|
|
"node_key": obj.Name,
|
|
"state": state,
|
|
"message": message,
|
|
"properties_hash": compute_properties_hash(obj),
|
|
}
|
|
)
|
|
|
|
with open(output_path, "w") as f:
|
|
json.dump({"valid": all_valid, "nodes": results}, f)
|
|
|
|
status = "PASS" if all_valid else "FAIL"
|
|
FreeCAD.Console.PrintMessage(
|
|
f"Validation {status}: {len(results)} nodes -> {output_path}\n"
|
|
)
|
|
finally:
|
|
FreeCAD.closeDocument(doc.Name)
|
|
|
|
|
|
def export(input_path, output_path, format="step"):
|
|
"""Export a Create file to an external geometry format.
|
|
|
|
Parameters
|
|
----------
|
|
input_path : str
|
|
Path to the ``.kc`` or ``.FCStd`` file.
|
|
output_path : str
|
|
Path to write the exported file.
|
|
format : str
|
|
One of ``step``, ``iges``, ``stl``, ``obj``.
|
|
"""
|
|
import Part
|
|
|
|
doc = FreeCAD.openDocument(input_path)
|
|
try:
|
|
shapes = [
|
|
obj.Shape for obj in doc.Objects if hasattr(obj, "Shape") and obj.Shape
|
|
]
|
|
if not shapes:
|
|
raise ValueError("No geometry found in document")
|
|
|
|
compound = Part.makeCompound(shapes)
|
|
|
|
format_lower = format.lower()
|
|
if format_lower == "step":
|
|
compound.exportStep(output_path)
|
|
elif format_lower == "iges":
|
|
compound.exportIges(output_path)
|
|
elif format_lower == "stl":
|
|
import Mesh
|
|
|
|
Mesh.export([compound], output_path)
|
|
elif format_lower == "obj":
|
|
import Mesh
|
|
|
|
Mesh.export([compound], output_path)
|
|
else:
|
|
raise ValueError(f"Unsupported format: {format}")
|
|
|
|
FreeCAD.Console.PrintMessage(
|
|
f"Exported {format_lower.upper()} -> {output_path}\n"
|
|
)
|
|
finally:
|
|
FreeCAD.closeDocument(doc.Name)
|