diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py index 7d7c757..8095255 100644 --- a/src/py/bbctrl/Jog.py +++ b/src/py/bbctrl/Jog.py @@ -25,12 +25,21 @@ # # ################################################################################ +import os import threading +import time import inevent from inevent.Constants import * +# Set to True (or BBCTRL_AJOG_DRYRUN=1 in env) to log press/release +# events and would-be ESP commands without actually sending JOG / +# JOGSTOP. Useful for debugging the gamepad event path without +# touching the gantry. Defaults to live actuation. +A_DRY_RUN = os.environ.get('BBCTRL_AJOG_DRYRUN', '') == '1' + + # Listen for input events class Jog(inevent.JogHandler): def __init__(self, ctrl): @@ -94,6 +103,12 @@ class Jog(inevent.JogHandler): def _a_stop(self): ext = getattr(self.ctrl, 'ext_axis', None) + ext_state = ('present' if (ext is not None and ext.enabled) + else 'unavailable') + if A_DRY_RUN: + self.log.info('AJOG DRYRUN _a_stop ext=%s (would send JOGSTOP)', + ext_state) + return if ext is None or not ext.enabled: return try: @@ -103,6 +118,22 @@ class Jog(inevent.JogHandler): def _a_start(self, direction): ext = getattr(self.ctrl, 'ext_axis', None) + ext_state = ('present' if (ext is not None and ext.enabled) + else 'unavailable') + if A_DRY_RUN: + scale = self._a_speed_scale() + try: + step_max = (int(ext.aux._cfg['step_max_sps']) + if ext is not None and ext.enabled else -1) + accel = (int(ext.aux._cfg['step_accel_sps2']) + if ext is not None and ext.enabled else -1) + except Exception: + step_max, accel = -1, -1 + self.log.info( + 'AJOG DRYRUN _a_start dir=%+d ext=%s speed=%d scale=%.4f ' + 'step_max=%d accel=%d (would send JOG)', + direction, ext_state, self.speed, scale, step_max, accel) + return if ext is None or not ext.enabled or direction == 0: return scale = self._a_speed_scale() @@ -154,6 +185,28 @@ class Jog(inevent.JogHandler): cfg = self.get_config(dev_name) old = self.a_button + # DEBUG: log EVERY incoming gamepad event so we can see + # exactly what the pendant is producing on press/release. + # Skip noisy stick / report-syn events to keep the journal + # readable but log all KEY events and any ABS event whose + # code matches one we care about. + try: + tname = ev_type_name.get(event.type, '?') + except Exception: + tname = '?' + if event.type == EV_KEY: + self.log.info( + 'AJOG EV dev=%r type=%s(%d) code=0x%x val=%d ' + 'cfg.a_pos_btn=0x%x cfg.a_neg_btn=0x%x', + dev_name, tname, event.type, event.code, event.value, + cfg.get('a_pos_btn', 0), cfg.get('a_neg_btn', 0)) + elif event.type == EV_ABS and event.code in ( + cfg.get('a_neg_abs', -1), + cfg.get('a_pos_abs', -1)): + self.log.info( + 'AJOG EV dev=%r type=%s(%d) code=0x%x val=%d (trigger ABS)', + dev_name, tname, event.type, event.code, event.value) + if event.type == EV_KEY: if event.code == cfg.get('a_pos_btn'): if event.value: self.a_button = 1 @@ -169,13 +222,15 @@ class Jog(inevent.JogHandler): elif self.a_button == -1: self.a_button = 0 if self.a_button != old: - self.log.info('A-axis trigger -> %s', self.a_button) + self.log.info( + 'AJOG STATE %+d -> %+d (t=%.3f dry_run=%s)', + old, self.a_button, time.monotonic(), A_DRY_RUN) self._a_apply(self.a_button, old) # On every release pull a fresh position mirror in case # the user does a gplan-driven A move next. The terminal # [jog] done line itself already updates aux._pos_steps; # this propagates that into ExternalAxis._pos_mm. - if self.a_button == 0: + if self.a_button == 0 and not A_DRY_RUN: # Wait briefly so the [jog] done line has time to # arrive before we read aux.position_mm. self.ctrl.ioloop.call_later(0.2, self._a_resync_pos)