Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- A diameter tool may set `cutter_sense` (`upcut`, `downcut`, or `compression`) and, with compression, `upcut_length` (millimetres at the tip). Omitted, compensation stays the flat diameter offset. Sense is stored on the job and the IR for later checks; it does not change the 2D offset. Posted G-code is unchanged when the field is omitted. This output has not been proven on a machine.
- A profile, pocket, face, engrave, drill, wrap or live mill may set `onion_skin`, a positive length strictly less than `depth`. Earlier depth passes stop that far short of the floor. The last pass cuts the skin with the same entry as the other passes. Omitting the field leaves the depth plan unchanged. `campanion run` checks that no earlier pass enters the skin and that the last pass reaches the floor. This output has not been proven on a machine.
- `linuxcnc_router` is a millimetre LinuxCNC gantry post: the same RS274 motion as `linuxcnc`, with no rotary axis. A Probe event with a contact pose and a feed is `G38.2` (LinuxCNC straight probe; <https://linuxcnc.org/docs/stable/html/gcode/g-code.html#gcode:g38>). `vacuum` and `dust` are immediate digital outputs `M64`/`M65` (<https://linuxcnc.org/docs/stable/html/gcode/m-code.html#mcode:m62-m65>); pin numbers default to 0 and 1. Any other Other name is refused. Existing posts still fall back to `process.other`. This output has not been proven on a machine.

## 0.9.3 — 2026-11-03

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ that post. The G-code is read back and compared to the toolpath at the job chord
| id | name | Rotary | description |
| --- | --- | --- | --- |
| `linuxcnc` | LinuxCNC | A, inverse time | Millimetre RS274 dialect for LinuxCNC. Absolute moves, cutter compensation cancelled, IJK arcs and helices. A axis with inverse-time feed (G93, cancelled by G94) when the angle changes. |
| `linuxcnc_router` | LinuxCNC router | - | Millimetre LinuxCNC gantry router. G38.2 tool-length probe, vacuum and dust as M64/M65. No rotary axis. |
| `grbl` | Grbl | - | Millimetre dialect for Grbl. Absolute moves, IJK arcs and helices, no line numbers, tool changes as comments. No rotary axis. |
| `mach3` | Mach3 | A, degrees per minute | Millimetre dialect for Mach3. Incremental IJK arcs and helices, dwell P in seconds, no line numbers. A axis with degrees-per-minute feed when the angle changes. |
| `fanuc` | Fanuc | A, inverse time | Millimetre Fanuc mill dialect. Program O0001, sequence numbers, R arcs, helices as lines, dwell P in milliseconds. A axis with inverse-time feed (G93, cancelled by G94) when the angle changes. |
Expand Down
1 change: 1 addition & 0 deletions crates/cli/tests/post_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fn posts_lists_every_builtin() -> Result<(), String> {
if ids
!= [
"linuxcnc",
"linuxcnc_router",
"linuxcnc_5ax",
"linuxcnc_5ax_tcp",
"grbl",
Expand Down
12 changes: 11 additions & 1 deletion crates/post/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ RS274/NGC posts. A post definition is JSON (`posts/linuxcnc.json` and the other

`per_cut` writes every process-on. When any process is still on and the next motion is a rapid (`G0`), the writer emits `thc.off` and that process's off lines before the rapid, then the on lines again before the next feed. Speed and power come from the process-on that is still in effect. A pierce dwell is written only when the IR already has a dwell at that cut. Plasma and `grbl_laser` set `per_cut`, so a torch or beam stays off through G0 travel even when the IR process is `spindle`.

## LinuxCNC router

`linuxcnc_router` is the millimetre LinuxCNC dialect for a gantry. Motion matches `linuxcnc`: absolute millimetres, cancelled cutter compensation, IJK arcs and helices, and path blend `G64 P0.01`. There is no `rotary` block. A typical gantry is XYZ, so a program with an A or B angle is rejected. Use `linuxcnc` when the machine has a fourth axis.

A Probe event with a contact pose and a feed is one `G38.2` block: the three axis words are the contact tip, and `F` is the probe feed. LinuxCNC `G38.2` is a straight probe that stops on contact and alarms if the probe does not trip ([G38](https://linuxcnc.org/docs/stable/html/gcode/g-code.html#gcode:g38)). `G38.2` is motion group 1, with `G0`/`G1`/`G2`/`G3`, so the next move states `G0` or `G1` again. The contact is not a part move: the writer's position and the audit position stay where they were, and `F` becomes the modal feed. A Probe with no contact pose is `probe needs a contact pose`. A Probe with no feed is `probe needs a feed`. A post with no `probe` block still writes `(probe)`. `probe.code` may be `G38.3` on a custom post (same move, no alarm if the probe does not trip).

`vacuum` and `dust` are Other processes. The post lists them in `auxiliaries` and refuses any other Other name. Posts that omit `auxiliaries` still fall back to `process.other`. On is `M64 P{pin}` and off is `M65 P{pin}`: immediate digital outputs ([M62–M65](https://linuxcnc.org/docs/stable/html/gcode/m-code.html#mcode:m62-m65)). `digital_out.vacuum_pin` defaults to 0 and `digital_out.dust_pin` defaults to 1. `{pin}` is allowed only on those two templates, and only when `digital_out` is set. A vacuum or dust that is still on at the end of the program is turned off with the same pin.

`audit` treats the probe block as a non-motion and accepts `M64`/`M65` only when `digital_out` is set and `P` is one of the declared pins. `G53` handling is unchanged. This output has not been proven on a machine.

## Modal audit

`audit` reads posted mill G-code and returns the modal state a control would keep at each motion block: motion mode (`G0`–`G3`), plane (`G17`–`G19`), distance (`G90`/`G91`), units (`G20`/`G21`), feed mode (`G93`/`G94`), the active `F`, work offset (`G54`–`G59`), the tool in the spindle (`T` and `M6`, or the post's tool-change comment), tool-length offset (`G43`/`G49`), spindle or beam (`M3`/`M4`/`M5`, including a laser `S` word), coolant, and a modal rotary word when the post has `rotary`.

A word these posts do not write is an error, so a typo in a template fails the replay. A `G2` or `G3` needs a known plane and a full centre: both in-plane letters (`I`/`J`, `I`/`K`, or `J`/`K`) or an `R`. A feed move needs a positive `F` already set. An inverse-time feed block must carry its own `F`. When line numbers are enabled, every numbered line follows the post's start and step, and each number stays at or below 1000000000. Each line must fit `max_line_length`. On a `decimal_point` post, dimensional words include a decimal point, and that includes rotary `A`, `B`, and `C`. A negative zero, scientific notation, and a word whose absolute value is above `1e9` are errors.
A word these posts do not write is an error, so a typo in a template fails the replay. On a post with `probe`, `G38.2` or `G38.3` is not a motion: position and motion mode stay put, and `F` on that block updates the modal feed. On a post with `digital_out`, `M64` and `M65` are accepted when `P` equals `vacuum_pin` or `dust_pin`. Other posts still reject those codes. A `G2` or `G3` needs a known plane and a full centre: both in-plane letters (`I`/`J`, `I`/`K`, or `J`/`K`) or an `R`. A feed move needs a positive `F` already set. An inverse-time feed block must carry its own `F`. When line numbers are enabled, every numbered line follows the post's start and step, and each number stays at or below 1000000000. Each line must fit `max_line_length`. On a `decimal_point` post, dimensional words include a decimal point, and that includes rotary `A`, `B`, and `C`. A negative zero, scientific notation, and a word whose absolute value is above `1e9` are errors.

Work offset stays unset when the text never selects `G54`–`G59`. Feed mode stays units per minute until `G93` or `G94`. `M2` and `M30` turn the spindle and coolant off. `M3` or `M4` with `S0` is a beam or torch that is off. A lathe post is rejected, because diameter mode is a different dialect. Calling `audit` on a five-axis post returns the `audit_five` replay as this mill report, with `A` in the rotary field.

Expand Down
45 changes: 45 additions & 0 deletions crates/post/posts/linuxcnc_router.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"id": "linuxcnc_router",
"name": "LinuxCNC router",
"description": "Millimetre RS274 dialect for a LinuxCNC gantry router. Absolute moves, cutter compensation cancelled, IJK arcs and helices, path blend G64 P0.01. No rotary axis: a gantry is XYZ, and a program with an A or B angle is rejected. A Probe event with a contact pose and a feed is G38.2 toward that pose (LinuxCNC straight probe; G38.2 alarms if the probe does not trip; https://linuxcnc.org/docs/stable/html/gcode/g-code.html#gcode:g38). A Probe with no contact pose or no feed is refused. Named Other processes vacuum and dust are immediate digital outputs M64 Pn and M65 Pn (https://linuxcnc.org/docs/stable/html/gcode/m-code.html#mcode:m62-m65). digital_out.vacuum_pin defaults to 0 and digital_out.dust_pin defaults to 1. Any other Other name is refused. Not proven on a machine.",
"units": "mm",
"line_numbers": { "enabled": true, "start": 10, "step": 10, "prefix": "N" },
"decimals": { "linear": 4, "feed": 4, "dwell": 3 },
"trailing_zeros": true,
"header": [
"({program_name})",
"G21",
"G90",
"G17",
"G40",
"G49",
"G64 P0.01"
],
"footer": [],
"modal_motion": true,
"arcs": { "mode": "ijk", "max_sweep_deg": 360, "helix": true },
"planes": true,
"dwell": { "word": "P", "unit": "s" },
"process": {
"spindle": { "on": ["M3 S{speed}"], "off": ["M5"] },
"torch": { "on": ["M3 S{speed}"], "off": ["M5"] },
"laser": { "on": ["M3 S{speed}"], "off": ["M5"] },
"extruder": { "on": ["M3 S{speed}"], "off": ["M5"] },
"knife": { "on": ["M3 S{speed}"], "off": ["M5"] },
"vacuum": { "on": ["M64 P{pin}"], "off": ["M65 P{pin}"] },
"dust": { "on": ["M64 P{pin}"], "off": ["M65 P{pin}"] }
},
"auxiliaries": ["vacuum", "dust"],
"digital_out": { "vacuum_pin": 0, "dust_pin": 1 },
"probe": { "code": "G38.2" },
"tool_change": ["T{tool} M6"],
"coolant": { "flood": ["M8"], "mist": ["M7"], "off": ["M9"] },
"comments": { "style": "paren" },
"program_end": ["M2"],
"max_line_length": 256,
"axis_letters": "XYZ",
"references": [
"https://linuxcnc.org/docs/stable/html/gcode/g-code.html#gcode:g38",
"https://linuxcnc.org/docs/stable/html/gcode/m-code.html#mcode:m62-m65"
]
}
Loading
Loading