Jog: detailed event/state logging + dry-run env var
Adds visibility into the gamepad event path so future regressions can be diagnosed without the gantry attached. AJOG EV logs every incoming KEY event and any ABS event matching the trigger codes; AJOG STATE logs every transition; the would-be JOG / JOGSTOP is also logged. BBCTRL_AJOG_DRYRUN=1 in the bbctrl env disables actuation while keeping the logging, so the host-side state machine can be tested without driving the ESP. Default is live actuation (dry-run off). Used this to prove the host side was correct on hardware where the firmware bug was hiding -- pendant taps produced perfect press/release pairs at ~200 ms while the ESP was the one ignoring JOGSTOP.
This commit is contained in:
@@ -25,12 +25,21 @@
|
|||||||
# #
|
# #
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
import inevent
|
import inevent
|
||||||
from inevent.Constants import *
|
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
|
# Listen for input events
|
||||||
class Jog(inevent.JogHandler):
|
class Jog(inevent.JogHandler):
|
||||||
def __init__(self, ctrl):
|
def __init__(self, ctrl):
|
||||||
@@ -94,6 +103,12 @@ class Jog(inevent.JogHandler):
|
|||||||
|
|
||||||
def _a_stop(self):
|
def _a_stop(self):
|
||||||
ext = getattr(self.ctrl, 'ext_axis', None)
|
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:
|
if ext is None or not ext.enabled:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@@ -103,6 +118,22 @@ class Jog(inevent.JogHandler):
|
|||||||
|
|
||||||
def _a_start(self, direction):
|
def _a_start(self, direction):
|
||||||
ext = getattr(self.ctrl, 'ext_axis', None)
|
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:
|
if ext is None or not ext.enabled or direction == 0:
|
||||||
return
|
return
|
||||||
scale = self._a_speed_scale()
|
scale = self._a_speed_scale()
|
||||||
@@ -154,6 +185,28 @@ class Jog(inevent.JogHandler):
|
|||||||
cfg = self.get_config(dev_name)
|
cfg = self.get_config(dev_name)
|
||||||
old = self.a_button
|
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.type == EV_KEY:
|
||||||
if event.code == cfg.get('a_pos_btn'):
|
if event.code == cfg.get('a_pos_btn'):
|
||||||
if event.value: self.a_button = 1
|
if event.value: self.a_button = 1
|
||||||
@@ -169,13 +222,15 @@ class Jog(inevent.JogHandler):
|
|||||||
elif self.a_button == -1: self.a_button = 0
|
elif self.a_button == -1: self.a_button = 0
|
||||||
|
|
||||||
if self.a_button != old:
|
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)
|
self._a_apply(self.a_button, old)
|
||||||
# On every release pull a fresh position mirror in case
|
# On every release pull a fresh position mirror in case
|
||||||
# the user does a gplan-driven A move next. The terminal
|
# the user does a gplan-driven A move next. The terminal
|
||||||
# [jog] done line itself already updates aux._pos_steps;
|
# [jog] done line itself already updates aux._pos_steps;
|
||||||
# this propagates that into ExternalAxis._pos_mm.
|
# 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
|
# Wait briefly so the [jog] done line has time to
|
||||||
# arrive before we read aux.position_mm.
|
# arrive before we read aux.position_mm.
|
||||||
self.ctrl.ioloop.call_later(0.2, self._a_resync_pos)
|
self.ctrl.ioloop.call_later(0.2, self._a_resync_pos)
|
||||||
|
|||||||
Reference in New Issue
Block a user