bdwidth_status: keep a bdwidth USB dropout from shutting down Klipper
bdwidth reads its CH340 from a reactor timer without error handling, so "write failed: [Errno 5] Input/output error" shut down Klipper mid print. bdwidth_status now wraps the sensor's Read_bdwidth (bdwidth.py itself unchanged): on a USB error it closes the port, reports once and returns False, so bdwidth retries every 10 s; the port is reopened when the device is back. New status fields usb_ok and usb_errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
eb7e0d4482
commit
e4d2230a57
3 files changed
+57
-1
No files matched your search
Binary file not shown.
@@ -26,6 +26,19 @@
|
||||
# only starts reading on the first SET_BDWIDTH ... ENABLE
|
||||
# linear_motion - counts per mm (motion_linear_coefficient)
|
||||
# sample_time - seconds between sensor reads
|
||||
# usb_ok - False while the USB connection is down (port: usb)
|
||||
# usb_errors - USB read errors since Klipper start
|
||||
#
|
||||
# USB guard (port: usb): bdwidth reads the sensor from a reactor timer without
|
||||
# any error handling, so one USB dropout of its CH340 (write failed: [Errno 5]
|
||||
# Input/output error) shut down Klipper mid print. This module wraps the
|
||||
# sensor's Read_bdwidth at connect: on a USB error it closes the port, reports
|
||||
# it and returns False, after which bdwidth tries again 10 s later; the next
|
||||
# reads reopen the port (the /dev/serial/by-id path returns when the device
|
||||
# reconnects). bdwidth.py itself is not changed.
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class BDWidthStatus:
|
||||
@@ -34,11 +47,52 @@ class BDWidthStatus:
|
||||
self.name = config.get('sensor', 'fila_width_0')
|
||||
self.sensor_name = 'bdwidth ' + self.name
|
||||
self.sensor = None
|
||||
self.usb_ok = True
|
||||
self.usb_errors = 0
|
||||
self._read = None
|
||||
self.printer.register_event_handler('klippy:connect',
|
||||
self._handle_connect)
|
||||
|
||||
def _handle_connect(self):
|
||||
self.sensor = self.printer.lookup_object(self.sensor_name)
|
||||
if (getattr(self.sensor, 'port', None) == 'usb'
|
||||
and callable(getattr(self.sensor, 'Read_bdwidth', None))
|
||||
and getattr(self.sensor, 'usb', None) is not None):
|
||||
self._read = self.sensor.Read_bdwidth
|
||||
self.sensor.Read_bdwidth = self._guarded_read
|
||||
logging.info("bdwidth_status: USB guard active for %s", self.name)
|
||||
|
||||
def _respond(self, msg):
|
||||
logging.warning(msg)
|
||||
try:
|
||||
self.printer.lookup_object('gcode').respond_info(msg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _guarded_read(self):
|
||||
# Called from bdwidth's reactor timer: must never raise
|
||||
usb = self.sensor.usb
|
||||
try:
|
||||
if not usb.is_open:
|
||||
usb.open()
|
||||
result = self._read()
|
||||
except Exception as e:
|
||||
self.usb_errors += 1
|
||||
try:
|
||||
usb.close()
|
||||
except Exception:
|
||||
pass
|
||||
if self.usb_ok:
|
||||
self.usb_ok = False
|
||||
self._respond("bdwidth %s: USB error (%s), print continues "
|
||||
"without width readings, retrying"
|
||||
% (self.name, e))
|
||||
return False
|
||||
if not self.usb_ok:
|
||||
self.usb_ok = True
|
||||
self._respond("bdwidth %s: USB connection restored after %d "
|
||||
"errors" % (self.name, self.usb_errors))
|
||||
return result
|
||||
|
||||
def get_status(self, eventtime):
|
||||
s = self.sensor
|
||||
@@ -64,6 +118,8 @@ class BDWidthStatus:
|
||||
'active': str(getattr(s, 'is_active', 'disable')),
|
||||
'linear_motion': getattr(s, 'linear_motion', 42.8),
|
||||
'sample_time': getattr(s, 'sample_time', 1.),
|
||||
'usb_ok': self.usb_ok,
|
||||
'usb_errors': self.usb_errors,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user