From 5071fe82ab221627ef10cf4125896e3d2585993a Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 25 Feb 2020 22:18:14 -0600 Subject: [PATCH] Ensure proper SD print completion (#16967) --- Marlin/src/MarlinCore.cpp | 80 ++++++++++++++++++++++++++++++---- Marlin/src/gcode/queue.cpp | 43 ++++++------------ Marlin/src/gcode/queue.h | 6 +++ Marlin/src/lcd/menu/menu.cpp | 2 +- Marlin/src/module/endstops.cpp | 2 +- Marlin/src/sd/cardreader.cpp | 36 +++++---------- Marlin/src/sd/cardreader.h | 5 ++- 7 files changed, 106 insertions(+), 68 deletions(-) diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 19fe1c0624..839b64e091 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -390,8 +390,8 @@ void startOrResumeJob() { #if ENABLED(SDSUPPORT) - void abortSDPrinting() { - card.stopSDPrint( + inline void abortSDPrinting() { + card.endFilePrint( #if SD_RESORT true #endif @@ -412,10 +412,68 @@ void startOrResumeJob() { #endif } -#endif + #if ENABLED(PRINTER_EVENT_LEDS) + #include "feature/leds/printer_event_leds.h" + #endif + + inline void finishSDPrinting() { + bool did_state = true; + switch (card.sdprinting_done_state) { + + #if ENABLED(PRINTER_EVENT_LEDS) + case 1: + printerEventLEDs.onPrintCompleted(); // Change LED color for Print Completed + break; + #endif + + #if HAS_RESUME_CONTINUE // Display "Click to Continue..." + case 2: + did_state = queue.enqueue_P(PSTR("M0 S" + #if HAS_LCD_MENU + "1800" // ...for 30 minutes with LCD + #else + "60" // ...for 1 minute with no LCD + #endif + )); + break; + #endif + + case 3: print_job_timer.stop(); break; + + case 4: + did_state = print_job_timer.duration() < 60 || queue.enqueue_P(PSTR("M31")); + break; + + case 5: + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.purge(); + #endif + + #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) + planner.finish_and_disable(); + #endif + + #if ENABLED(LCD_SET_PROGRESS_MANUALLY) + ui.set_progress_done(); + #endif + + #if ENABLED(SD_REPRINT_LAST_SELECTED_FILE) + ui.reselect_last_file(); + #endif + + SERIAL_ECHOLNPGM(MSG_FILE_PRINTED); + + default: + did_state = false; + card.sdprinting_done_state = 0; + } + if (did_state) ++card.sdprinting_done_state; + } + +#endif // SDSUPPORT /** - * Manage several activities: + * Minimal management of Marlin's core activities: * - Check for Filament Runout * - Keep the command buffer full * - Check for maximum inactive time between commands @@ -1122,10 +1180,15 @@ void setup() { /** * The main Marlin program loop * - * - Save or log commands to SD - * - Process available commands (if not saving) - * - Call endstop manager - * - Call inactivity manager + * - Call idle() to handle all tasks between G-code commands + * Note that no G-codes from the queue can be executed during idle() + * but many G-codes can be called directly anytime like macros. + * - Check whether SD card auto-start is needed now. + * - Check whether SD print finishing is needed now. + * - Run one G-code command from the immediate or main command queue + * and open up one space. Commands in the main queue may come from sd + * card, host, or by direct injection. The queue will continue to fill + * as long as idle() or manage_inactivity() are being called. */ void loop() { do { @@ -1135,6 +1198,7 @@ void loop() { #if ENABLED(SDSUPPORT) card.checkautostart(); if (card.flag.abort_sd_printing) abortSDPrinting(); + if (card.sdprinting_done_state) finishSDPrinting(); #endif queue.advance(); diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index edcb025bb3..ec1d3ebd35 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -512,9 +512,10 @@ void GCodeQueue::get_serial_commands() { #if ENABLED(SDSUPPORT) /** - * Get commands from the SD Card until the command buffer is full - * or until the end of the file is reached. The special character '#' - * can also interrupt buffering. + * Get lines from the SD Card until the command buffer is full + * or until the end of the file is reached. Because this method + * always receives complete command-lines, they can go directly + * into the main command queue. */ inline void GCodeQueue::get_sdcard_commands() { static uint8_t sd_input_state = PS_NORMAL; @@ -527,37 +528,21 @@ void GCodeQueue::get_serial_commands() { const int16_t n = card.get(); card_eof = card.eof(); if (n < 0 && !card_eof) { SERIAL_ERROR_MSG(MSG_SD_ERR_READ); continue; } + const char sd_char = (char)n; - if (sd_char == '\n' || sd_char == '\r' || card_eof) { + const bool is_eol = sd_char == '\n' || sd_char == '\r'; + if (is_eol || card_eof) { // Reset stream state, terminate the buffer, and commit a non-empty command + if (!is_eol && sd_count) ++sd_count; // End of file with no newline if (!process_line_done(sd_input_state, command_buffer[index_w], sd_count)) { - _commit_command(false); // Can handle last line missing a newline terminator + _commit_command(false); #if ENABLED(POWER_LOSS_RECOVERY) - recovery.cmd_sdpos = card.getIndex(); // Prime for the next _commit_command + recovery.cmd_sdpos = card.getIndex(); // Prime for the NEXT _commit_command #endif } - if (card_eof) { - - card.fileHasFinished(); // Handle end of file reached - - if (!IS_SD_PRINTING()) { // Was it the main job file? - SERIAL_ECHOLNPGM(MSG_FILE_PRINTED); // Tell the host the file is printed. - #if ENABLED(PRINTER_EVENT_LEDS) - printerEventLEDs.onPrintCompleted(); // Change LED color for Print Completed - #if HAS_RESUME_CONTINUE - enqueue_now_P(PSTR("M0 S" // Display "Click to Continue..." - #if HAS_LCD_MENU - "1800" // ...for 30 minutes with LCD - #else - "60" // ...for 1 minute with no LCD - #endif - )); - #endif - #endif - } - } + if (card_eof) card.fileHasFinished(); // Handle end of file reached } else process_stream_char(sd_char, sd_input_state, command_buffer[index_w], sd_count); @@ -633,9 +618,7 @@ void GCodeQueue::advance() { #endif // SDSUPPORT // The queue may be reset by a command handler or by code invoked by idle() within a handler - if (length) { - --length; - if (++index_r >= BUFSIZE) index_r = 0; - } + --length; + if (++index_r >= BUFSIZE) index_r = 0; } diff --git a/Marlin/src/gcode/queue.h b/Marlin/src/gcode/queue.h index 10b0ec7643..1ce64590c3 100644 --- a/Marlin/src/gcode/queue.h +++ b/Marlin/src/gcode/queue.h @@ -117,6 +117,12 @@ public: */ static void flush_and_request_resend(); + /** + * Attempt to enqueue a single G-code command + * and return 'true' if successful. + */ + FORCE_INLINE static bool enqueue_P(const char* cmd) { return _enqueue(cmd); } + private: static uint8_t index_w; // Ring buffer write position diff --git a/Marlin/src/lcd/menu/menu.cpp b/Marlin/src/lcd/menu/menu.cpp index f787e492e8..f1bcb970d4 100644 --- a/Marlin/src/lcd/menu/menu.cpp +++ b/Marlin/src/lcd/menu/menu.cpp @@ -324,9 +324,9 @@ void MarlinUI::_synchronize() { if (should_draw()) MenuItem_static::draw(LCD_HEIGHT >= 4, sync_message); if (no_reentry) return; // Make this the current handler till all moves are done - no_reentry = true; const screenFunc_t old_screen = currentScreen; goto_screen(_synchronize); + no_reentry = true; planner.synchronize(); // idle() is called until moves complete no_reentry = false; goto_screen(old_screen); diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 1491d881d5..d291f972d3 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -403,7 +403,7 @@ void Endstops::event_handler() { #if BOTH(SD_ABORT_ON_ENDSTOP_HIT, SDSUPPORT) if (planner.abort_on_endstop_hit) { - card.stopSDPrint(); + card.endFilePrint(); quickstop_stepper(); thermalManager.disable_all_heaters(); print_job_timer.stop(); diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index 3cc888fd96..426d540c7e 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -28,7 +28,7 @@ #include "../MarlinCore.h" #include "../lcd/ultralcd.h" -#include "../module/planner.h" +#include "../module/planner.h" // for synchronize #include "../module/printcounter.h" #include "../core/language.h" #include "../gcode/queue.h" @@ -49,6 +49,7 @@ // public: card_flags_t CardReader::flag; +uint8_t CardReader::sdprinting_done_state; char CardReader::filename[FILENAME_LENGTH], CardReader::longFilename[LONG_FILENAME_LENGTH]; int8_t CardReader::autostart_index; @@ -379,7 +380,7 @@ void CardReader::mount() { } void CardReader::release() { - stopSDPrint(); + endFilePrint(); flag.mounted = false; } @@ -401,7 +402,7 @@ void CardReader::startFileprint() { } } -void CardReader::stopSDPrint( +void CardReader::endFilePrint( #if SD_RESORT const bool re_sort/*=false*/ #endif @@ -501,7 +502,7 @@ void CardReader::openFileRead(char * const path, const uint8_t subcall_type/*=0* break; } - stopSDPrint(); + endFilePrint(); SdFile *curDir; const char * const fname = diveToFile(true, curDir, path); @@ -529,7 +530,7 @@ void CardReader::openFileWrite(char * const path) { announceOpen(2, path); file_subcall_ctr = 0; - stopSDPrint(); + endFilePrint(); SdFile *curDir; const char * const fname = diveToFile(false, curDir, path); @@ -554,7 +555,7 @@ void CardReader::openFileWrite(char * const path) { void CardReader::removeFile(const char * const name) { if (!isMounted()) return; - //stopSDPrint(); + //endFilePrint(); SdFile *curDir; const char * const fname = diveToFile(false, curDir, name); @@ -937,7 +938,7 @@ void CardReader::cdroot() { bool didSwap = false; uint8_t o1 = sort_order[0]; #if DISABLED(SDSORT_USES_RAM) - selectFileByIndex(o1); // Pre-fetch the first entry and save it + selectFileByIndex(o1); // Pre-fetch the first entry and save it strcpy(name1, longest_filename()); // so the loop only needs one fetch #if HAS_FOLDER_SORTING bool dir1 = flag.filenameIsDir; @@ -1073,30 +1074,13 @@ void CardReader::fileHasFinished() { startFileprint(); } else { - stopSDPrint(); - - #if ENABLED(POWER_LOSS_RECOVERY) - recovery.purge(); - #endif - - #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) - planner.finish_and_disable(); - #endif - - print_job_timer.stop(); - queue.enqueue_now_P(print_job_timer.duration() > 60 ? PSTR("M31") : PSTR("M117")); + endFilePrint(); #if ENABLED(SDCARD_SORT_ALPHA) presort(); #endif - #if ENABLED(LCD_SET_PROGRESS_MANUALLY) - ui.set_progress_done(); - #endif - - #if ENABLED(SD_REPRINT_LAST_SELECTED_FILE) - ui.reselect_last_file(); - #endif + sdprinting_done_state = 1; } } diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 8e09f44479..be700401ee 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -49,6 +49,7 @@ typedef struct { class CardReader { public: + static uint8_t sdprinting_done_state; static card_flags_t flag; // Flags (above) static char filename[FILENAME_LENGTH], // DOS 8.3 filename of the selected item longFilename[LONG_FILENAME_LENGTH]; // Long name of the selected item @@ -108,9 +109,9 @@ public: static void openAndPrintFile(const char *name); // (working directory) static void fileHasFinished(); static void getAbsFilename(char *dst); - static void startFileprint(); static void printFilename(); - static void stopSDPrint( + static void startFileprint(); + static void endFilePrint( #if SD_RESORT const bool re_sort=false #endif