diff options
author | Jason S. McMullan <jason.mcmullan@gmail.com> | 2021-04-17 06:54:12 -0400 |
---|---|---|
committer | KevinOConnor <kevin@koconnor.net> | 2021-05-26 11:57:57 -0400 |
commit | 913649de2e8057e849be2a54e08d34302ee37dad (patch) | |
tree | b7461d6351d46c73a5ad10e3aa49e7b8ad8facae /klippy/extras/sdcard_loop.py | |
parent | 4ea434796bb1f5bcc9d44bfcd0d9d66b8476faa9 (diff) | |
download | kutter-913649de2e8057e849be2a54e08d34302ee37dad.tar.gz kutter-913649de2e8057e849be2a54e08d34302ee37dad.tar.xz kutter-913649de2e8057e849be2a54e08d34302ee37dad.zip |
loop_sdcard: Add loopable SD card file sections
To support continuous belt printing, add nestable repeat
loop support via an `[sdcard_loop]` module.
Supported G-Code:
- SDCARD_LOOP_BEGIN COUNT=n ; Loop for N times, or infinitely if N is 0
- SDCARD_LOOP_END ; End of loop
- SDCARD_LOOP_DESIST ; Complete all loops without iterating
Marlin M808 compatibility example in `config/sample-macros.cfg`:
- M808 Ln ; Loop for N times, or infinitely if N is 0
- M808 ; End of loop
- M808 K ; Complete all loops without iterating
Added unit tests in test/klippy/sdcard_loop.test
See https://reprap.org/wiki/G-code#M808:_Set_or_Goto_Repeat_Marker
Signed-off-by: Jason S. McMullan <jason.mcmullan@gmail.com>
Diffstat (limited to 'klippy/extras/sdcard_loop.py')
-rw-r--r-- | klippy/extras/sdcard_loop.py | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/klippy/extras/sdcard_loop.py b/klippy/extras/sdcard_loop.py new file mode 100644 index 00000000..3cba979f --- /dev/null +++ b/klippy/extras/sdcard_loop.py @@ -0,0 +1,73 @@ +# Sdcard file looping support +# +# Copyright (C) 2021 Jason S. McMullan <jason.mcmullan@gmail.com> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import logging + +class SDCardLoop: + def __init__(self, config): + printer = config.get_printer() + self.sdcard = printer.load_object(config, "virtual_sdcard") + self.gcode = printer.lookup_object('gcode') + self.gcode.register_command( + "SDCARD_LOOP_BEGIN", self.cmd_SDCARD_LOOP_BEGIN, + desc=self.cmd_SDCARD_LOOP_BEGIN_help) + self.gcode.register_command( + "SDCARD_LOOP_END", self.cmd_SDCARD_LOOP_END, + desc=self.cmd_SDCARD_LOOP_END_help) + self.gcode.register_command( + "SDCARD_LOOP_DESIST", self.cmd_SDCARD_LOOP_DESIST, + desc=self.cmd_SDCARD_LOOP_DESIST_help) + self.loop_stack = [] + cmd_SDCARD_LOOP_BEGIN_help = "Begins a looped section in the SD file." + def cmd_SDCARD_LOOP_BEGIN(self, gcmd): + count = gcmd.get_int("COUNT", minval=0) + if not self.loop_begin(count): + raise gcmd.error("Only permitted in SD file.") + cmd_SDCARD_LOOP_END_help = "Ends a looped section in the SD file." + def cmd_SDCARD_LOOP_END(self, gcmd): + if not self.loop_end(): + raise gcmd.error("Only permitted in SD file.") + cmd_SDCARD_LOOP_DESIST_help = "Stops iterating the current loop stack." + def cmd_SDCARD_LOOP_DESIST(self, gcmd): + if not self.loop_desist(): + raise gcmd.error("Only permitted outside of a SD file.") + def loop_begin(self, count): + if not self.sdcard.is_cmd_from_sd(): + # Can only run inside of an SD file + return False + self.loop_stack.append((count, self.sdcard.get_file_position())) + return True + def loop_end(self): + if not self.sdcard.is_cmd_from_sd(): + # Can only run inside of an SD file + return False + # If the stack is empty, no need to skip back + if len(self.loop_stack) == 0: + return True + # Get iteration count and return position + count, position = self.loop_stack.pop() + if count == 0: # Infinite loop + self.sdcard.set_file_position(position) + self.loop_stack.append((0, position)) + elif count == 1: # Last repeat + # Nothing to do + pass + else: + # At the next opportunity, seek back to the start of the loop + self.sdcard.set_file_position(position) + # Decrement the count by 1, and add the position back to the stack + self.loop_stack.append((count - 1, position)) + return True + def loop_desist(self): + if self.sdcard.is_cmd_from_sd(): + # Can only run outside of an SD file + return False + logging.info("Desisting existing SD loops") + self.loop_stack = [] + return True + +def load_config(config): + return SDCardLoop(config) |