diff --git a/src/py/bbctrl/AuxPreprocessor.py b/src/py/bbctrl/AuxPreprocessor.py index f31abba..e0a8f02 100644 --- a/src/py/bbctrl/AuxPreprocessor.py +++ b/src/py/bbctrl/AuxPreprocessor.py @@ -394,22 +394,42 @@ class AuxPreprocessor(object): except ValueError: continue event = _ATC_M_CODES.get(num) if event: - # gplan only delivers `(MSG,...)` to the - # message stream when it's attached to an - # executable block (so the host can release - # it on the matching cmd-id ack). A bare - # comment-only line gets collapsed and the - # message is silently dropped, which means - # back-to-back hook lines (like 4 ejects in - # a row) only deliver the last one. + # We need two things here that aren't + # naturally provided by the (MSG,...) + # transport: # - # Pair each HOOK line with an essentially- - # zero dwell so it gets a planner block id - # of its own. G4 P0.001 = 1us dwell which - # is below any timer resolution and has no - # observable effect on the machine. - fout.write('G4 P0.001 (MSG,HOOK:%s:)\n' - % event) + # (1) Synchronization. (MSG,HOOK:...) is + # fire-and-forget from gplan's view - + # gplan emits the message and keeps + # streaming subsequent blocks (Z + # moves, the next eject, etc.) to the + # AVR. Meanwhile the hook handler + # runs the actual ESP RPC in a + # thread, and Z lifts while V2 is + # still wiggling. To make M-codes + # behave like proper blocking gcode, + # we precede each HOOK with M0 + # (program pause). The Hooks layer + # registers the atom as block_unpause + # + auto_resume, so: + # M0 -> machine pauses + # (MSG,HOOK:event:) fires hook + # hook thread runs ESP RPC + # hook completes, auto-unpauses + # next block streams + # End result: M100/M102/M103 block + # until the ESP says done, just like + # a G-code dwell. + # + # (2) Block separation. gplan collapses + # consecutive comment-only lines + # into a single block, so back-to- + # back HOOK lines used to drop all + # but the last. M0 is its own block + # so this falls out automatically - + # the (MSG,...) attaches cleanly to + # each M0. + fout.write('M0 (MSG,HOOK:%s:)\n' % event) code_stripped = _ATC_M_RE.sub('', code).strip() if code_stripped: # Mixed line: keep the residual executable