################################################################################ # # AuxPreprocessor - rewrite ATC M-codes into hook calls # # History # ------- # v1: rewrote W tokens into (MSG,HOOK:aux:N) lines because the bbctrl # planner only understood XYZABC and the W axis was driven via a # side-channel. # v2: W is now exposed to gplan as a virtual A axis (see ExternalAxis), # so gplan handles W motion natively. The preprocessor no longer # touches W tokens. ATC pneumatics still go through the hook # channel because they're events, not motion. # # What this still does # -------------------- # Maps four user-defined M-codes onto pneumatic-tool-changer events: # # M100 DROPTOOL -> (MSG,HOOK:droptool:) # M101 GRABTOOL -> (MSG,HOOK:grabtool:) # M102 RELEASE -> (MSG,HOOK:release:) # M103 CLAMP -> (MSG,HOOK:clamp:) # # M100-M103 are in LinuxCNC/Buildbotics' user-defined range, so the # planner won't error if the codes leak through unrewritten - it just # won't *do* anything. We strip them out and emit the matching hook # line in their place. # # The preprocessor is intentionally conservative: anything it doesn't # understand is left alone. # ################################################################################ import os import re import shutil import tempfile # Strip line comments so we don't get fooled by "(M100 not really)". _PAREN_COMMENT_RE = re.compile(r'\([^)]*\)') # ATC pneumatics M-codes mapped onto hook events. _ATC_M_CODES = { 100: 'droptool', 101: 'grabtool', 102: 'release', 103: 'clamp', } _ATC_M_RE = re.compile( r'(?; old files must be # migrated by hand. if (not self._w_warned) and _W_TOKEN_RE.search(code): self._warn('Found W axis token in gcode; W is no ' 'longer recognized by bbctrl. Use A ' 'instead. (warning suppressed for ' 'subsequent W tokens in this file)') self._w_warned = True # ATC M-codes (M100-M103). Each ATC M-code on the line # is replaced with its (MSG,HOOK::) line and # stripped from the residual. 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 not code.strip(): # Nothing meaningful left; preserve any trailing # comment text but skip empty lines. rest = line.rstrip() if rest: fout.write(rest + '\n') continue # Other gcode remains on the line - emit it. fout.write(line + '\n') continue # No rewrite needed. fout.write(raw) return rewrote_any def preprocess_file(src_path, log=None, **_unused): """Convenience: rewrite src_path in place if it contains ATC M-codes. Returns True if the file was rewritten. Extra keyword args are accepted for backwards compat (the old w_first arg is no longer used).""" if not AuxPreprocessor.file_uses_aux(src_path): return False pre = AuxPreprocessor(log=log) fd, tmp = tempfile.mkstemp(prefix='auxpre_', suffix='.nc', dir=os.path.dirname(src_path) or None) os.close(fd) try: rewrote = pre.process(src_path, tmp) if rewrote: shutil.move(tmp, src_path) return True os.unlink(tmp) return False except Exception: try: os.unlink(tmp) except OSError: pass raise