CAM Planning¶
From STEP file to G-code in five POSTs: create a project, upload the workpiece, set stock, fire the planner, and postprocess each setup.
Concepts¶
| Term | What it is |
|---|---|
| Project | The container for one machining job, owning a workpiece, stock, machine, material, equipment library, and the resulting CAM tree. Auto-creates a sibling quote job (see Quotation). |
| Workpiece | The target geometry. STEP, STL, or native BREP. Tessellation runs synchronously on upload; the response includes presigned URLs for the resulting mesh. |
| Stock | The blank you machine the workpiece out of. Defined by per-axis bounding-box offsets in mm. |
| Automated CAM planning | Async Celery task that auto-detects machinable features, picks tools from the project's equipment library, and computes toolpaths. Returns a task_id; observe via webhook (task.succeeded) or poll GET /tasks/{id}. |
| Setup | One physical fixturing of the part. Each setup yields its own G-code program. The automated planner emits at least one setup per project. |
| Postprocessing | Translate the per-setup CAM tree into vendor-specific G-code (.nc) using the machine's postprocessor. |
Endpoints¶
Project lifecycle¶
| Method | Path | Scope |
|---|---|---|
POST |
/projects |
projects:write |
GET |
/projects |
projects:read |
GET |
/projects/{project_id} |
projects:read |
PATCH |
/projects/{project_id} |
projects:write |
DELETE |
/projects/{project_id} |
projects:write |
Inputs¶
| Method | Path | Scope |
|---|---|---|
POST |
/projects/{project_id}/workpiece |
cam:write |
POST |
/projects/{project_id}/stock/bounding-box |
cam:write |
Plan and postprocess¶
| Method | Path | Scope |
|---|---|---|
POST |
/projects/{project_id}/automation/cam-planning |
automation:run, cam:write |
POST |
/projects/{project_id}/post/run?setup_index=N |
cam:write |
Task status¶
| Method | Path | Scope |
|---|---|---|
GET |
/tasks/{task_id} |
projects:read |
GET |
/tasks/by-project/{project_id} |
projects:read |
The full request/response schemas live in the Scalar reference. Webhook delivery and signing are documented under Webhooks.
Flow: STEP file to G-code¶
import requests
import time
API = "https://app.autonomiq.de/backend-api/api/public/v1"
H = {"X-API-Key": "smk_live_..."}
# 1. Create a project
# - machine: auto-picked from user's first assigned machine
# - equipment: auto-picked from user's default library (is_default=true)
# Pass product_id / library_id to override.
project = requests.post(
f"{API}/projects",
json={"name": "Bracket v3", "customer": "Acme"},
headers=H,
).json()
PID = project["id"]
# 2. Upload the workpiece (STEP / STL / BREP)
with open("bracket.step", "rb") as f:
requests.post(
f"{API}/projects/{PID}/workpiece",
files={"file": f},
headers=H,
).raise_for_status()
# 3. Define stock from per-axis offsets (mm)
requests.post(
f"{API}/projects/{PID}/stock/bounding-box",
json={
"minus_x": 0, "plus_x": 0,
"minus_y": 0, "plus_y": 0,
"minus_z": 0, "plus_z": 5,
},
headers=H,
).raise_for_status()
# 4. Kick off automated CAM planning (async, returns 202 + task_id)
task = requests.post(
f"{API}/projects/{PID}/automation/cam-planning",
json={"include_face_milling": True, "sort_by_height": True},
headers=H,
).json()
task_id = task["task_id"]
# 5. Poll until the task finishes (or register a webhook; see /webhooks)
while True:
snap = requests.get(f"{API}/tasks/{task_id}", headers=H).json()
if snap["status"] in ("success", "failed", "cancelled"):
break
time.sleep(2)
# 6. Generate G-code for setup 0
nc = requests.post(
f"{API}/projects/{PID}/post/run",
params={"setup_index": 0},
headers={**H, "Accept-Encoding": "gzip"},
).content
open("program_setup0.nc", "wb").write(nc)
Flow: pick a specific machine and material¶
By default the planner auto-selects the user's first machine and default equipment library. Override at creation time via product_id / library_id, or reassign afterward with PATCH /projects/{id}:
# Discover what the user has access to
machines = requests.get(f"{API}/libraries/machines", headers=H).json()
materials = requests.get(f"{API}/libraries/materials", headers=H).json()
# Find a specific machine by name
datron_neo = next(m for m in machines if "Neo" in m.get("name", ""))
# Option A: pin at creation time
project = requests.post(
f"{API}/projects",
json={
"name": "Bracket v3 / Datron Neo",
"product_id": datron_neo["product_id"],
"material_id": materials[0]["id"],
},
headers=H,
).json()
PID = project["id"]
# Option B: reassign machine/material on an existing project
requests.patch(
f"{API}/projects/{PID}",
json={
"product_id": datron_neo["product_id"],
"material_id": materials[0]["id"],
},
headers=H,
).raise_for_status()
PATCH also accepts name, customer, library_id, and the other contact fields. Only provided fields are updated.
Flow: webhook-driven instead of polling¶
Register an endpoint once, then drive everything off task.succeeded / task.failed:
requests.post(
f"{API}/webhooks",
json={
"url": "https://your-app.example/sm",
"event_types": ["task.succeeded", "task.failed", "quote.ready"],
},
headers=H,
).raise_for_status()
Inside your receiver, dedupe on X-SimplyMill-Delivery-Id and verify the HMAC signature; see Webhooks.
Concurrency rules¶
- One automation task per user runs at a time. Concurrent requests from the same user queue (the response carries
queued: trueand aqueue_position). - Same project, second request → rejected immediately with
409 Conflict, code=automation_slot_busy. - For parallel automation across projects, mint API keys for separate users.
Idempotency¶
POST /projects, POST /projects/{id}/workpiece, and POST /projects/{id}/automation/cam-planning honour the Idempotency-Key header. Subsequent requests with the same key replay the original response with Idempotency-Replayed: true for 24h.
Cleanup¶
Soft-delete returns 204 immediately; a Celery task drops the BOSS-stored meshes/toolpaths, the MongoDB job, and the Postgres rows in the background.