Skip to content

feat: CNC CAM — GRBL G-code artifact via FreeCAD headless CAM - #4

Open
bepitulaz wants to merge 1 commit into
mainfrom
feat/cnc-cam-gcode
Open

bepitulaz wants to merge 1 commit into
mainfrom
feat/cnc-cam-gcode

Conversation

@bepitulaz

Copy link
Copy Markdown
Member

What

Every successful build123d build can now also produce a GRBL model.gcode for a desktop CNC. The assistant emits a machining plan (```camJSON block: material + face/drill/pocket/profile ops with toolbit ids), it is validated Elixir-side, and a FreeCAD 1.1--console` CAM job machines the exported STEP with the real toolbit geometry. The G-code appears as a "G-code (GRBL)" download next to STEP/STL/GLB; CAM notes/warnings render as an amber card in chat.

How it works

  • Tool truth: mnft_cad/priv/cam/toolbits.json (feeds/plunge/RPM/stepdown per material, work envelope, post) + 24 vendored .fctb files from potsed/freecad_sainsmart_toolbits (Genmitsu MC40A flat ends 0.8–3.175, BN10A/MC40A ball noses, 20°/0.1 mm V-bit). Ball noses 1.2–3.0 mm are synthesized from same-size flat-end geometry (provenance noted in-file). Tune feeds by editing the JSON — no code change.
  • Plan validation: Mnft.Cad.CamPlan — strict schema, toolbit ids must exist; rejection is non-fatal ("CAM plan rejected: …" note, model still lands).
  • FreeCAD bridge: cad_worker/cam.py → cam_freecad.py. Geometry selection is deterministic (largest +Z face = top, +Z planar faces below it = pocket floors, full-circle edges = holes — the LLM never picks faces). Origin = stock min corner, Z0 = stock top. Ops clear their SetupSheet expression bindings so scripted depth/stepdown values stick.
  • Honest output: G-code header lists tools/feeds/ops/warnings (oversized part, hole smaller than tool, undersized drilled holes, aluminum lube reminder). Missing FreeCAD / timeout / any CAM failure = chat note only; the model always lands.
  • Timeouts: worker SIGALRM and the Elixir port deadline extend by the CAM budget so a 180 s CAM job isn't killed by the 60 s exec alarm.
  • OpenSCAD: unchanged, mesh-only; the prompt says CNC export requires build123d.

Verification

  • Elixir core 198/198, web 16/16, worker pytest 155/155 (16 new CamPlan cases, 11 new cam.py cases with FreeCAD mocked).
  • E2E on the live pipeline (real LLM → build123d → FreeCAD):
    • aluminum 40×80×3 plate: plan {aluminum, face+drill+profile, flat-3.175}; G-code header rpm=10000 feed=340 plunge=140; four drills at exactly (6,6)/(34,6)/(34,74)/(6,74) through Z-3; perimeter profile with radius offset; feeds only 140/340.
    • "birch wood" phrasing → material=wood, feeds 900/300 @ 12000.
    • MNFT_ENGINE=openscad + CNC ask → no G-code, chat explains build123d requirement.

Deviations worth review

  • V-bit: uses 20deg_V-Bit_0.100mm_Tip_MC50A.fctb (3.175 mm shank, 20°, 0.1 mm tip — exact match for the nano-blue bits) instead of the planned WC04A entry, which has a 6.35 mm shank that doesn't fit the 3.175 mm collet.
  • FreeCAD ToolBit.from_file crashes on ° under the macOS C locale → the bridge uses ToolBit.from_dict after reading the JSON itself.

Config

MNFT_FREECAD_CMD (default the macOS bundle) and MNFT_CAM_TIMEOUT_S (default 180) — documented in .env.example and the README.

On build123d turns the LLM now also emits a machining plan (a \`cam\`
JSON block: material + face/drill/pocket/profile ops with toolbit ids).
Mnft.Cad.CamPlan validates it against priv/cam/toolbits.json (23 ids:
Genmitsu MC40A/BN10A geometry vendored from potsed's SainSmart library,
plus per-material feeds/RPM/stepdown tables). A validated plan flows
through the worker payload into a FreeCAD 1.1 --console CAM job
(cad_worker/cam.py + cam_freecad.py) that machines the exported STEP and
posts it with the grbl post-processor; model.gcode lands as a
downloadable artifact next to STEP/STL/GLB.

- Deterministic geometry selection: ops name the toolbit, the FreeCAD
  script picks faces/edges (largest +Z face = top, +Z planar faces below
  it = pocket floors, full-circle edges = holes); origin = stock min
  corner, Z0 = stock top.
- Honest output: header comments list tools/feeds/ops; warnings for
  oversized parts, holes narrower than the tool, undersized drilled
  holes, missing pockets, aluminum lube.
- CAM failures are non-fatal: missing FreeCAD, timeouts and rejected
  plans surface as chat notes/amber warnings; the model still lands.
- OpenSCAD stays mesh-only (prompt explains CNC needs build123d).
- Worker SIGALRM and Elixir port deadline extend by the CAM budget.
- Tests: CamPlan validator (16 cases), cam.py bridge (11 cases,
  FreeCAD mocked); E2E-verified on the live pipeline: aluminum plate
  G-code carries 340/140 @ 10000 rpm for flat-3.175 with four drills at
  the exact corner positions; birch wood gives 900/300 @ 12000.
  Note: vbit-20 uses 20deg/0.1mm/3.175mm-shank MC50A entry (exact match
  for the nano-blue bits) instead of the 6.35mm-shank WC04A.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant