zcbot/windows-node/adapters/ansys.geometry.inspect@v1/worker.py

141 lines
4.4 KiB
Python

"""ANSYS Mechanical 2024 R2 declarative geometry-inspection adapter."""
from __future__ import annotations
import json
import os
import sys
from importlib.metadata import version
from pathlib import Path
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from ansys_geometry import ( # noqa: E402
ANSYS_REVISION,
EXECUTION_GATE,
artifact_manifest,
atomic_json,
export_geometry_image,
file_sha256,
geometry_inventory,
import_geometry,
input_file,
probe,
terminal,
validate_artifact,
)
ADAPTER_VERSION = "0.3.0"
_ARTIFACTS = {
"geometry-manifest.json": ("geometry_manifest", "application/json"),
"geometry-preview.png": ("geometry_preview", "image/png"),
"provenance.json": ("provenance", "application/json"),
}
def _validate_semantics(request: dict[str, Any]) -> None:
inspect = request["operation"]["inspect"]
input_keys = {item["key"] for item in request["inputs"]}
if inspect["geometry"]["input"] not in input_keys:
raise ValueError("GEOMETRY_INPUT_NOT_BOUND")
if request["outputs"]:
raise ValueError("INSPECT_OUTPUTS_MUST_BE_EMPTY")
def _load_request(job_dir: Path) -> dict[str, Any]:
record = json.loads(
(job_dir / "request" / "request.json").read_text(encoding="utf-8")
)
request = record.get("request")
if not isinstance(request, dict):
raise TypeError("JOB_REQUEST_MISSING")
_validate_semantics(request)
return record
def run(job_dir: Path) -> list[dict[str, Any]]:
job_dir = job_dir.resolve(strict=True)
record = _load_request(job_dir)
request = record["request"]
inspect = request["operation"]["inspect"]
geometry = input_file(job_dir, inspect["geometry"]["input"])
output = job_dir / "output"
output.mkdir(exist_ok=True)
from ansys.mechanical.core import App
software_version: int | None = None
manifest_path = output / "geometry-manifest.json"
preview_path = output / "geometry-preview.png"
with App(version=ANSYS_REVISION, private_appdata=True) as app:
software_version = app.version
api: dict[str, Any] = {}
app.update_globals(api)
import_geometry(app, api, geometry)
inventory, _ = geometry_inventory(app)
inventory.update(
{
"title": inspect["title"],
"software": "ANSYS Mechanical",
"software_version": software_version,
}
)
atomic_json(manifest_path, inventory)
export_geometry_image(app, api, preview_path)
provenance_path = output / "provenance.json"
atomic_json(
provenance_path,
{
"adapter_version": ADAPTER_VERSION,
"pymechanical_version": version("ansys-mechanical-core"),
"ansys_revision": ANSYS_REVISION,
"request_digest": record["request_digest"],
"input": {
"key": inspect["geometry"]["input"],
"filename": geometry.name,
"sha256": file_sha256(geometry),
},
},
)
paths = [manifest_path, preview_path, provenance_path]
for path in paths:
validate_artifact(path)
return [artifact_manifest(path, _ARTIFACTS) for path in paths]
def main() -> int:
if sys.argv[1:] == ["--probe"]:
return probe(ADAPTER_VERSION)
if len(sys.argv) != 2:
print("[ERR] Usage: worker.py <job-directory>", file=sys.stderr)
return 2
job_dir = Path(sys.argv[1])
record: dict[str, Any] = {}
try:
record = _load_request(job_dir)
if os.environ.get(EXECUTION_GATE) != "1":
raise RuntimeError("ANSYS_242_REAL_LICENSE_ACCEPTANCE_REQUIRED")
artifacts = run(job_dir)
atomic_json(job_dir / "artifacts.json", artifacts)
atomic_json(job_dir / "terminal.json", terminal(record, "succeeded", artifacts, {}))
print("[OK] ANSYS Mechanical geometry inspection completed.")
return 0
except Exception as exc: # noqa: BLE001 - terminal.json must capture vendor failures
atomic_json(
job_dir / "terminal.json",
terminal(
record,
"failed",
[],
{"code": type(exc).__name__, "detail": str(exc)[:500]},
),
)
print(f"[ERR] {type(exc).__name__}: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())