From 963d7c247b7cb9ffc023b3b6700d830f67af9bae Mon Sep 17 00:00:00 2001 From: Arksine Date: Wed, 29 Jul 2020 06:20:51 -0400 Subject: [PATCH] virtual_sdcard: add SDCARD_PRINT_FILE gcode SDCARD_PRINT_FILE allows Klipper to load and start the print for any gcode file within the virtual_sdcard path, including subdirectories. Signed-off-by: Eric Callahan --- klippy/extras/virtual_sdcard.py | 61 ++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/klippy/extras/virtual_sdcard.py b/klippy/extras/virtual_sdcard.py index b08cdd18..45467906 100644 --- a/klippy/extras/virtual_sdcard.py +++ b/klippy/extras/virtual_sdcard.py @@ -5,6 +5,8 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import os, logging +VALID_GCODE_EXTS = ['gcode', 'g', 'gco'] + class VirtualSD: def __init__(self, config): printer = config.get_printer() @@ -28,6 +30,9 @@ class VirtualSD: self.gcode.register_command( "SDCARD_RESET_FILE", self.cmd_SDCARD_RESET_FILE, desc=self.cmd_SDCARD_RESET_FILE_help) + self.gcode.register_command( + "SDCARD_PRINT_FILE", self.cmd_SDCARD_PRINT_FILE, + desc=self.cmd_SDCARD_PRINT_FILE_help) def handle_shutdown(self): if self.work_timer is not None: self.must_pause_work = True @@ -46,17 +51,31 @@ class VirtualSD: if self.work_timer is None: return False, "" return True, "sd_pos=%d" % (self.file_position,) - def get_file_list(self): - dname = self.sdcard_dirname - try: - filenames = os.listdir(self.sdcard_dirname) - return [(fname, os.path.getsize(os.path.join(dname, fname))) - for fname in sorted(filenames, key=str.lower) - if not fname.startswith('.') - and os.path.isfile((os.path.join(dname, fname)))] - except: - logging.exception("virtual_sdcard get_file_list") - raise self.gcode.error("Unable to get file list") + def get_file_list(self, check_subdirs=False): + if check_subdirs: + flist = [] + for root, dirs, files in os.walk( + self.sdcard_dirname, followlinks=True): + for name in files: + ext = name[name.rfind('.')+1:] + if ext not in VALID_GCODE_EXTS: + continue + full_path = os.path.join(root, name) + r_path = full_path[len(self.sdcard_dirname) + 1:] + size = os.path.getsize(full_path) + flist.append((r_path, size)) + return sorted(flist, key=lambda f: f[0].lower()) + else: + dname = self.sdcard_dirname + try: + filenames = os.listdir(self.sdcard_dirname) + return [(fname, os.path.getsize(os.path.join(dname, fname))) + for fname in sorted(filenames, key=str.lower) + if not fname.startswith('.') + and os.path.isfile((os.path.join(dname, fname)))] + except: + logging.exception("virtual_sdcard get_file_list") + raise self.gcode.error("Unable to get file list") def get_status(self, eventtime): progress = 0. if self.work_timer is not None and self.file_size: @@ -85,6 +104,17 @@ class VirtualSD: raise gcmd.error( "SDCARD_RESET_FILE cannot be run from the sdcard") self._reset_file() + cmd_SDCARD_PRINT_FILE_help = "Loads a SD file and starts the print. May "\ + "include files in subdirectories." + def cmd_SDCARD_PRINT_FILE(self, gcmd): + if self.work_timer is not None: + raise gcmd.error("SD busy") + self._reset_file() + filename = gcmd.get("FILENAME") + if filename[0] == '/': + filename = filename[1:] + self._load_file(gcmd, filename, check_subdirs=True) + self.cmd_M24(gcmd) def cmd_M20(self, gcmd): # List SD card files = self.get_file_list() @@ -99,10 +129,7 @@ class VirtualSD: # Select SD file if self.work_timer is not None: raise gcmd.error("SD busy") - if self.current_file is not None: - self.current_file.close() - self.current_file = None - self.file_position = self.file_size = 0 + self._reset_file() try: orig = gcmd.get_commandline() filename = orig[orig.find("M23") + 4:].split()[0].strip() @@ -112,7 +139,9 @@ class VirtualSD: raise gcmd.error("Unable to extract filename") if filename.startswith('/'): filename = filename[1:] - files = self.get_file_list() + self._load_file(gcmd, filename) + def _load_file(self, gcmd, filename, check_subdirs=False): + files = self.get_file_list(check_subdirs) files_by_lower = { fname.lower(): fname for fname, fsize in files } try: fname = files_by_lower[filename.lower()]