AuxPreprocessor: canonical M100-M103 for ATC pneumatics
Map four user-defined M-codes to the existing ATC hooks:
M100 DROPTOOL -> (MSG,HOOK:droptool:)
M101 GRABTOOL -> (MSG,HOOK:grabtool:)
M102 RELEASE -> (MSG,HOOK:release:)
M103 CLAMP -> (MSG,HOOK🗜️)
M100-M103 are in LinuxCNC/Buildbotics user-defined range so the
planner won't error on the raw codes if the preprocessor is bypassed.
Stripped from the residual line and replaced with the hook line.
Order is left-to-right; multiple ATC codes per line and ATC+W on
the same line both work (M100 W10 -> drop then move to W=10).
The file scanner (file_uses_aux, formerly file_uses_w) now wakes
up for either W tokens or ATC M-codes; backwards-compat alias kept.
MDI rewrite (Mach._rewrite_w_mdi) updated likewise.
Tested locally with mixed ATC/W gcode in tmp/20260501_atc_mcodes.
This commit is contained in:
@@ -42,6 +42,30 @@ _PAREN_COMMENT_RE = re.compile(r'\([^)]*\)')
|
||||
# Modal G-code groups we care about.
|
||||
_MODAL_RE = re.compile(r'(?<![A-Za-z_0-9])[Gg]\s*0*(\d+(?:\.\d+)?)')
|
||||
|
||||
# ATC pneumatics. We map a small range of user-defined M-codes onto
|
||||
# our existing HOOK: events. M100-M103 are in LinuxCNC/Buildbotics'
|
||||
# user-defined range so the planner won't error on them - but it also
|
||||
# won't *do* anything with them, so we strip them out and emit the
|
||||
# matching hook line in their place. M6 is intentionally NOT mapped:
|
||||
# users keep their own probe-and-prompt M6 override.
|
||||
#
|
||||
# M100 DROPTOOL (eject current tool, automatic sequence)
|
||||
# M101 GRABTOOL (auto-clamp on inserted holder)
|
||||
# M102 RELEASE (manually open collet, no clamp)
|
||||
# M103 CLAMP (manually close collet with bleed)
|
||||
_ATC_M_CODES = {
|
||||
100: 'droptool',
|
||||
101: 'grabtool',
|
||||
102: 'release',
|
||||
103: 'clamp',
|
||||
}
|
||||
# A token like 'M100' or 'm103', not preceded/followed by alnum.
|
||||
_ATC_M_RE = re.compile(
|
||||
r'(?<![A-Za-z_0-9])[Mm]\s*0*(' +
|
||||
'|'.join(str(n) for n in _ATC_M_CODES) +
|
||||
r')(?![\w.])'
|
||||
)
|
||||
|
||||
|
||||
class AuxPreprocessorError(Exception):
|
||||
pass
|
||||
@@ -65,9 +89,10 @@ class AuxPreprocessor(object):
|
||||
# ------------------------------------------------------------------ scan
|
||||
|
||||
@staticmethod
|
||||
def file_uses_w(path):
|
||||
"""Quick check: does this file contain any W-axis word? Used to skip
|
||||
preprocessing entirely for files that don't care about W."""
|
||||
def file_uses_aux(path):
|
||||
"""Quick check: does this file contain anything the preprocessor
|
||||
would rewrite (W axis tokens or ATC M-codes)? Used to skip
|
||||
preprocessing entirely for files that don't need it."""
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8', errors='replace') as f:
|
||||
for line in f:
|
||||
@@ -75,10 +100,16 @@ class AuxPreprocessor(object):
|
||||
code = code.split(';', 1)[0]
|
||||
if _W_TOKEN_RE.search(code):
|
||||
return True
|
||||
if _ATC_M_RE.search(code):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
# Backwards-compat alias: external callers used file_uses_w before
|
||||
# the ATC M-codes were added.
|
||||
file_uses_w = file_uses_aux
|
||||
|
||||
# ------------------------------------------------------------------ core
|
||||
|
||||
def _strip_w(self, line):
|
||||
@@ -139,8 +170,38 @@ class AuxPreprocessor(object):
|
||||
# vs incremental matches what the planner sees for XYZ).
|
||||
self._detect_modals(code, modal)
|
||||
|
||||
# ATC M-codes (M100-M103). Each ATC M-code on the line
|
||||
# is replaced with its (MSG,HOOK:<event>:) line and
|
||||
# stripped from the residual. Multiple ATC codes per
|
||||
# line are honoured but order is left-to-right. We do
|
||||
# this BEFORE the W-axis path so a line like
|
||||
# "M100 W10" cleanly emits drop+move.
|
||||
atc_matches = list(_ATC_M_RE.finditer(line))
|
||||
if atc_matches:
|
||||
rewrote_any = True
|
||||
for m in atc_matches:
|
||||
try: num = int(m.group(1))
|
||||
except ValueError: continue
|
||||
event = _ATC_M_CODES.get(num)
|
||||
if event:
|
||||
fout.write('(MSG,HOOK:%s:)\n' % event)
|
||||
line = _ATC_M_RE.sub('', line)
|
||||
code = _PAREN_COMMENT_RE.sub('', line)
|
||||
code = code.split(';', 1)[0]
|
||||
# If nothing else is left on the line, we're done.
|
||||
if not code.strip():
|
||||
# Preserve any trailing comment, but skip if the
|
||||
# whole line is empty after the M-code strip.
|
||||
rest = line.rstrip()
|
||||
if rest:
|
||||
fout.write(rest + '\n')
|
||||
continue
|
||||
|
||||
if not _W_TOKEN_RE.search(code):
|
||||
fout.write(raw)
|
||||
# No W work to do; emit whatever's left after ATC
|
||||
# M-code stripping (or the original line if there
|
||||
# were no ATC codes).
|
||||
fout.write(line + '\n' if atc_matches else raw)
|
||||
continue
|
||||
|
||||
rewrote_any = True
|
||||
@@ -214,9 +275,10 @@ class AuxPreprocessor(object):
|
||||
|
||||
|
||||
def preprocess_file(src_path, log=None, w_first=True):
|
||||
"""Convenience: rewrite src_path in place if it uses W.
|
||||
"""Convenience: rewrite src_path in place if it uses anything the
|
||||
preprocessor handles (W axis tokens or ATC M-codes).
|
||||
Returns True if the file was rewritten."""
|
||||
if not AuxPreprocessor.file_uses_w(src_path):
|
||||
if not AuxPreprocessor.file_uses_aux(src_path):
|
||||
return False
|
||||
pre = AuxPreprocessor(log=log, w_first=w_first)
|
||||
fd, tmp = tempfile.mkstemp(prefix='auxpre_', suffix='.nc',
|
||||
|
||||
@@ -270,8 +270,9 @@ class Mach(Comm):
|
||||
"""Apply the W-axis preprocessor to a single MDI line. Returns
|
||||
possibly-multi-line G-code with HOOK: comments inserted."""
|
||||
try:
|
||||
from bbctrl.AuxPreprocessor import AuxPreprocessor, _W_TOKEN_RE
|
||||
if not _W_TOKEN_RE.search(cmd):
|
||||
from bbctrl.AuxPreprocessor import (
|
||||
AuxPreprocessor, _W_TOKEN_RE, _ATC_M_RE)
|
||||
if not _W_TOKEN_RE.search(cmd) and not _ATC_M_RE.search(cmd):
|
||||
return cmd
|
||||
import io, tempfile, os
|
||||
# AuxPreprocessor.process is file-based; route through
|
||||
|
||||
Reference in New Issue
Block a user