Add per-cut torch on/off support (opt-in) + a SteelTailor plasma profile

CAM mode's exporter treats the tool as continuously on from the first
cut to the last (correct for a spinning router bit, just lifted via Z
between cuts) - it has no way to turn a tool off between separate
disconnected cut regions. A real plasma torch may need to be genuinely
off during rapid travel between cuts, not just raised, matching
CMMS/ATG's own SteelTailor table's proven-working G-code (M07/M08
bracketing every individual feature, reverse-engineered from a real
working file, not a manual's spec).

Added a device.gcodeSpindleOff hook: when a profile sets it to a
non-empty array, every disconnected toolpath group re-triggers the
spindle-on command (forcing newOp regardless of operation-type changes)
and fires gcodeSpindleOff right after each group finishes - mirrors the
existing laser on/off mechanism, generalized. Every device profile that
doesn't set gcodeSpindleOff (i.e. every profile that existed before
this) behaves exactly as before - this is a pure opt-in addition, not a
behavior change to anything else.

New profile: SteelTailor.Plasma.json - G90/absolute (controller's own
touchscreen sets the origin, so gcodePre doesn't redefine it), M07/M08
via the new per-cut mechanism, no G41/G40 cutter comp (Kiri already
offsets toolpaths in software via the operation's own tool-diameter
setting - stacking controller-side comp on top would double-compensate).
Bed size/max height are placeholders pending real numbers.
This commit is contained in:
AI Assistant 2026-08-27 21:15:05 +10:00
commit 62c031b831
2 changed files with 47 additions and 1 deletions

View file

@ -0,0 +1,34 @@
{
"mode": "CAM",
"internal": 0,
"bedHeight": 3,
"bedWidth": 3000,
"bedDepth": 1500,
"maxHeight": 200,
"originCenter": false,
"spindleMax": 100,
"gcodePre": [
"G21 ; units mm",
"G90 ; absolute positioning - controller's own touchscreen sets the origin/start point, this file does not redefine it"
],
"gcodePost": [
"M08 ; torch off",
"M02 ; program end"
],
"gcodeDwell": [],
"gcodeSpindle": [
"M07 ; torch on"
],
"gcodeSpindleOff": [
"M08 ; torch off"
],
"gcodeChange": [],
"gcodeFExt": "nc",
"gcodeSpace": true,
"gcodeStrip": true,
"new": false,
"deviceName": "SteelTailor Plasma",
"imageURL": "",
"useLaser": false,
"profiles": []
}

View file

@ -493,6 +493,16 @@ export function cam_export(print, online) {
}
}
newSpindle = layerout.spindle;
// CMMS (not upstream): some CAM tools (e.g. a plasma torch) need
// fully switching off between every disconnected cut region, not
// just once at the end of the whole job - a spinning router bit
// stays "on" continuously between cuts (just lifted via Z), but a
// torch commanded to stay lit through rapid travel between
// separate features is a real difference, not a cosmetic one.
// Opt-in via device.gcodeSpindleOff being a real, non-empty array -
// every device profile that doesn't set it (i.e. every profile that
// existed before this) behaves exactly as before, byte-for-byte.
const perCutTorch = Array.isArray(device.gcodeSpindleOff) && device.gcodeSpindleOff.length > 0;
// iterate over layer output records
layerout.forEach((out, ind) => {
if (out.type === 'lerp') {
@ -502,13 +512,15 @@ export function cam_export(print, online) {
if (out.gcode && Array.isArray(out.gcode)) {
filterEmit(out.gcode, consts);
} else {
moveTo(out, { newOp: ind === 0 ? newOp : false });
moveTo(out, { newOp: ind === 0 ? (newOp || perCutTorch) : false });
}
});
if (lasering && laserOp) {
// disable laser
filterEmit(laserOp.off, consts);
lasering = false;
} else if (perCutTorch && spindle) {
filterEmit(device.gcodeSpindleOff, consts);
}
}