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 <arksine.code@gmail.com>
This commit is contained in:
Arksine 2020-07-29 06:20:51 -04:00 committed by KevinOConnor
parent af39a209f3
commit 963d7c247b
1 changed files with 45 additions and 16 deletions

View File

@ -5,6 +5,8 @@
# This file may be distributed under the terms of the GNU GPLv3 license. # This file may be distributed under the terms of the GNU GPLv3 license.
import os, logging import os, logging
VALID_GCODE_EXTS = ['gcode', 'g', 'gco']
class VirtualSD: class VirtualSD:
def __init__(self, config): def __init__(self, config):
printer = config.get_printer() printer = config.get_printer()
@ -28,6 +30,9 @@ class VirtualSD:
self.gcode.register_command( self.gcode.register_command(
"SDCARD_RESET_FILE", self.cmd_SDCARD_RESET_FILE, "SDCARD_RESET_FILE", self.cmd_SDCARD_RESET_FILE,
desc=self.cmd_SDCARD_RESET_FILE_help) 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): def handle_shutdown(self):
if self.work_timer is not None: if self.work_timer is not None:
self.must_pause_work = True self.must_pause_work = True
@ -46,17 +51,31 @@ class VirtualSD:
if self.work_timer is None: if self.work_timer is None:
return False, "" return False, ""
return True, "sd_pos=%d" % (self.file_position,) return True, "sd_pos=%d" % (self.file_position,)
def get_file_list(self): def get_file_list(self, check_subdirs=False):
dname = self.sdcard_dirname if check_subdirs:
try: flist = []
filenames = os.listdir(self.sdcard_dirname) for root, dirs, files in os.walk(
return [(fname, os.path.getsize(os.path.join(dname, fname))) self.sdcard_dirname, followlinks=True):
for fname in sorted(filenames, key=str.lower) for name in files:
if not fname.startswith('.') ext = name[name.rfind('.')+1:]
and os.path.isfile((os.path.join(dname, fname)))] if ext not in VALID_GCODE_EXTS:
except: continue
logging.exception("virtual_sdcard get_file_list") full_path = os.path.join(root, name)
raise self.gcode.error("Unable to get file list") 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): def get_status(self, eventtime):
progress = 0. progress = 0.
if self.work_timer is not None and self.file_size: if self.work_timer is not None and self.file_size:
@ -85,6 +104,17 @@ class VirtualSD:
raise gcmd.error( raise gcmd.error(
"SDCARD_RESET_FILE cannot be run from the sdcard") "SDCARD_RESET_FILE cannot be run from the sdcard")
self._reset_file() 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): def cmd_M20(self, gcmd):
# List SD card # List SD card
files = self.get_file_list() files = self.get_file_list()
@ -99,10 +129,7 @@ class VirtualSD:
# Select SD file # Select SD file
if self.work_timer is not None: if self.work_timer is not None:
raise gcmd.error("SD busy") raise gcmd.error("SD busy")
if self.current_file is not None: self._reset_file()
self.current_file.close()
self.current_file = None
self.file_position = self.file_size = 0
try: try:
orig = gcmd.get_commandline() orig = gcmd.get_commandline()
filename = orig[orig.find("M23") + 4:].split()[0].strip() filename = orig[orig.find("M23") + 4:].split()[0].strip()
@ -112,7 +139,9 @@ class VirtualSD:
raise gcmd.error("Unable to extract filename") raise gcmd.error("Unable to extract filename")
if filename.startswith('/'): if filename.startswith('/'):
filename = filename[1:] 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 } files_by_lower = { fname.lower(): fname for fname, fsize in files }
try: try:
fname = files_by_lower[filename.lower()] fname = files_by_lower[filename.lower()]