diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index e5dd8f155b..1f56d184fc 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1799,6 +1799,14 @@ #define PE_LEDS_COMPLETED_TIME (30*60) // (seconds) Time to keep the LED "done" color before restoring normal illumination #endif + /** + * Priming for the Remaining Time estimate + * Long processes at the start of a G-code file can skew the Remaining Time estimate. + * Enable these options to start this estimation at a later point in the G-code file. + */ + //#define REMAINING_TIME_PRIME // Provide G-code 'M75 R' to prime the Remaining Time estimate + //#define REMAINING_TIME_AUTOPRIME // Prime the Remaining Time estimate later (e.g., at the end of 'M109') + /** * Continue after Power-Loss (Creality3D) * diff --git a/Marlin/src/gcode/stats/M75-M78.cpp b/Marlin/src/gcode/stats/M75-M78.cpp index b5daa8809c..8c7378c977 100644 --- a/Marlin/src/gcode/stats/M75-M78.cpp +++ b/Marlin/src/gcode/stats/M75-M78.cpp @@ -38,9 +38,22 @@ * * ProUI: If the print fails to start and any text is * included in the command, print it in the header. + * + * With REMAINING_TIME_PRIME: + * + * 'M75 R' : Prime Remaining Time Estimate with the current job time and SD file position and return. + * */ void GcodeSuite::M75() { + #if ENABLED(REMAINING_TIME_PRIME) + if (parser.seen_test('R')) { + print_job_timer.primeRemainingTimeEstimate(card.getIndex(), card.getFileSize()); + return; + } + #endif + marlin.startOrResumeJob(); // ... ExtUI::onPrintTimerStarted() + #if ENABLED(DWIN_LCD_PROUI) // TODO: Remove if M75 is never used if (!card.isStillPrinting()) dwinPrintHeader(parser.has_string() ? parser.string_arg : GET_TEXT(MSG_HOST_START_PRINT)); diff --git a/Marlin/src/gcode/temp/M104_M109.cpp b/Marlin/src/gcode/temp/M104_M109.cpp index badc8b7fe7..d27ff75d39 100644 --- a/Marlin/src/gcode/temp/M104_M109.cpp +++ b/Marlin/src/gcode/temp/M104_M109.cpp @@ -130,8 +130,16 @@ void GcodeSuite::M104_M109(const bool isM109) { TERN_(AUTOTEMP, thermalManager.autotemp_M104_M109()); - if (isM109 && got_temp) + if (isM109 && got_temp) { (void)thermalManager.wait_for_hotend(target_extruder, no_wait_for_cooling); + #if ENABLED(REMAINING_TIME_AUTOPRIME) + if (card.isStillPrinting()) { + print_job_timer.primeRemainingTimeEstimate(card.getIndex(), card.getFileSize()); + //SERIAL_ECHOLN(F("M109 - Prime Remaining Time Estimate: "), print_job_timer.duration(), C(' '), card.getIndex(), C(' '), card.getFileSize() - card.getIndex()); + } + #endif + } + } #if ENABLED(AUTOTEMP) diff --git a/Marlin/src/gcode/temp/M140_M190.cpp b/Marlin/src/gcode/temp/M140_M190.cpp index 7d2de2084a..f153be4ab5 100644 --- a/Marlin/src/gcode/temp/M140_M190.cpp +++ b/Marlin/src/gcode/temp/M140_M190.cpp @@ -121,6 +121,13 @@ void GcodeSuite::M140_M190(const bool isM190) { #endif thermalManager.wait_for_bed(no_wait_for_cooling); + + #if ENABLED(REMAINING_TIME_AUTOPRIME) + if (card.isStillPrinting()) { + print_job_timer.primeRemainingTimeEstimate(card.getIndex(), card.getFileSize()); + //SERIAL_ECHOLN(F("M190 - Prime Remaining Time Estimate: "), print_job_timer.duration(), C(' '), card.getIndex(), C(' '), card.getFileSize() - card.getIndex()); + } + #endif } else { ui.set_status_reset_fn([]{ diff --git a/Marlin/src/lcd/marlinui.h b/Marlin/src/lcd/marlinui.h index 85d05887e3..94a333629a 100644 --- a/Marlin/src/lcd/marlinui.h +++ b/Marlin/src/lcd/marlinui.h @@ -328,9 +328,13 @@ public: #endif #if ANY(SHOW_REMAINING_TIME, SET_PROGRESS_MANUALLY) static uint32_t _calculated_remaining_time() { - const duration_t elapsed = print_job_timer.duration(); - const progress_t progress = _get_progress(); - return progress ? elapsed.value * (100 * (PROGRESS_SCALE) - progress) / progress : 0; + #if ANY(REMAINING_TIME_PRIME, REMAINING_TIME_AUTOPRIME) + return print_job_timer.remainingTimeEstimate(card.getIndex()); + #else + const uint32_t elapsed = print_job_timer.duration(); + const progress_t progress = _get_progress(); + return progress ? elapsed * (100U * (PROGRESS_SCALE) - progress) / progress : 0; + #endif } #if ENABLED(SET_REMAINING_TIME) static uint32_t remaining_time; diff --git a/Marlin/src/lcd/tft/ui_color_ui.cpp b/Marlin/src/lcd/tft/ui_color_ui.cpp index a81ff3ccc6..53b3b0d068 100644 --- a/Marlin/src/lcd/tft/ui_color_ui.cpp +++ b/Marlin/src/lcd/tft/ui_color_ui.cpp @@ -346,8 +346,6 @@ void MarlinUI::draw_status_screen() { duration_t elapsed = print_job_timer.duration(); #endif - const progress_t progress = TERN(HAS_PRINT_PROGRESS_PERMYRIAD, get_progress_permyriad, get_progress_percent)(); - #if ENABLED(SHOW_ELAPSED_TIME) elapsed.toDigital(buffer); tft.canvas(ELAPSED_TIME_X, ELAPSED_TIME_Y, ELAPSED_TIME_W, ELAPSED_TIME_H); @@ -360,15 +358,8 @@ void MarlinUI::draw_status_screen() { #endif #if ENABLED(SHOW_REMAINING_TIME) - // Get the estimate, first from M73 - uint32_t estimate_remaining = (0 - #if ALL(SET_PROGRESS_MANUALLY, SET_REMAINING_TIME) - + get_remaining_time() - #endif - ); - // If no M73 estimate is available but we have progress data, calculate time remaining assuming time elapsed is linear with progress - if (!estimate_remaining && progress > 0) - estimate_remaining = elapsed.value * (100 * (PROGRESS_SCALE) - progress) / progress; + // Get a Remaining Time estimate from M73 R, a primed calculation, or percent/time calculation + const uint32_t estimate_remaining = get_remaining_time(); // Generate estimate string if (!estimate_remaining) @@ -387,13 +378,14 @@ void MarlinUI::draw_status_screen() { tft.add_image(REMAINING_TIME_IMAGE_X, REMAINING_TIME_IMAGE_Y, imgTimeRemaining, color); #endif tft.add_text(REMAINING_TIME_TEXT_X, REMAINING_TIME_TEXT_Y, color, tft_string); - #endif + #endif // SHOW_REMAINING_TIME // Progress bar // TODO: print percentage text for SHOW_PROGRESS_PERCENT tft.canvas(PROGRESS_BAR_X, PROGRESS_BAR_Y, PROGRESS_BAR_W, PROGRESS_BAR_H); tft.set_background(COLOR_PROGRESS_BG); tft.add_rectangle(0, 0, PROGRESS_BAR_W, PROGRESS_BAR_H, COLOR_PROGRESS_FRAME); + const progress_t progress = TERN(HAS_PRINT_PROGRESS_PERMYRIAD, get_progress_permyriad, get_progress_percent)(); if (progress) tft.add_bar(1, 1, ((PROGRESS_BAR_W - 2) * progress / (PROGRESS_SCALE)) / 100, 7, COLOR_PROGRESS_BAR); diff --git a/Marlin/src/libs/duration_t.h b/Marlin/src/libs/duration_t.h index acfbf0d7a8..d648924dc9 100644 --- a/Marlin/src/libs/duration_t.h +++ b/Marlin/src/libs/duration_t.h @@ -211,28 +211,5 @@ struct duration_t { } } - /** - * @brief Get the estimated remaining time - * @details Use the given start time and sdpos values to estimate the - * remaining time (as reckoned from this duration_t value). - * Should be superseded by 'M73 R' (SET_REMAINING_TIME). - * This estimate is rendered meaningless by M808, but reset start_time for per-object estimates. - * The UI should consider a "start_sdpos" of 0 to be unset and show "---" - * - * @param start_time The time to consider the "real" start of the print. Saved time of the first E move with X and/or Y. - * @param start_sdpos The sdpos of the first printing move (E move with X and/or Y). - * @param end_sdpos The sdpos of the end of the print (before cooldown). - * @param sdpos The current sdpos of the print job. - */ - uint32_t remainingEstimate(const uint32_t start_time, const uint32_t start_sdpos, const uint32_t end_sdpos, const uint32_t sdpos) const { - const float elapsed_data = float(sdpos - start_sdpos), // Ex: 460b - 280b = 180b - total_data = float(end_sdpos - start_sdpos), // Ex: 1000b - 280b = 720b - sd_percent = elapsed_data / total_data, // Ex: 180b / 720b = 0.25 - sd_ratio = (1.0f - sd_percent) / sd_percent; // Ex: (1.0 - 0.25) / 0.25 = 3.0 - - const uint32_t elapsed_time = value - start_time; // Ex: T2 - T1 = 300s - return uint32_t(elapsed_time * sd_ratio); // Ex: 300s * 3.0f = 900s - } - #pragma GCC diagnostic pop }; diff --git a/Marlin/src/libs/stopwatch.cpp b/Marlin/src/libs/stopwatch.cpp index 8790c2b3db..1afa2615e2 100644 --- a/Marlin/src/libs/stopwatch.cpp +++ b/Marlin/src/libs/stopwatch.cpp @@ -33,6 +33,12 @@ uint32_t Stopwatch::accumulator; uint32_t Stopwatch::startTimestamp; uint32_t Stopwatch::stopTimestamp; +#if ANY(REMAINING_TIME_PRIME, REMAINING_TIME_AUTOPRIME) + uint32_t Stopwatch::lap_start_time; // Reckon from this start time + float Stopwatch::lap_start_sdpos, // Reckon from this start file position + Stopwatch::lap_total_data; // Total size from start_sdpos to end of file +#endif + bool Stopwatch::stop() { debug(F("stop")); diff --git a/Marlin/src/libs/stopwatch.h b/Marlin/src/libs/stopwatch.h index 13fa3de4b4..6404d8426f 100644 --- a/Marlin/src/libs/stopwatch.h +++ b/Marlin/src/libs/stopwatch.h @@ -40,6 +40,12 @@ class Stopwatch { static uint32_t startTimestamp; static uint32_t stopTimestamp; + #if ANY(REMAINING_TIME_PRIME, REMAINING_TIME_AUTOPRIME) + static uint32_t lap_start_time; // Reckon from this start time + static float lap_start_sdpos, // Reckon from this start file position + lap_total_data; // Total size from start_sdpos to end of file + #endif + public: /** * @brief Initialize the stopwatch @@ -104,6 +110,57 @@ class Stopwatch { */ static uint32_t duration(); + #if ANY(REMAINING_TIME_PRIME, REMAINING_TIME_AUTOPRIME) + + /** + * @brief Get the estimated remaining time based on the elapsed time + * @details Use the given start time and sdpos values to estimate the + * remaining time as reckoned from duration(). + * Should be superseded by 'M73 R' (SET_REMAINING_TIME). + * Get per-object time estimate with M808 by putting 'M75 R' at the start of the loop. + * The UI should consider a "start_sdpos" of 0 to be unset and show ---. + * + * @param start_time The time to consider the "real" start of the print. Saved time of the first E move with X and/or Y. + * @param sdpos The current sdpos of the print job. + * @param start_sdpos The sdpos of the first printing move (E move with X and/or Y), as a float. + * @param total_data The pre-calculated end_sdpos - start_sdpos as a float. + */ + uint32_t remainingTimeEstimate(const uint32_t start_time, const uint32_t sdpos, const float start_sdpos, const float total_data) const { + const float elapsed_data = float(sdpos) - start_sdpos; // Ex: 460b - 280b = 180b + if (elapsed_data < 200 || total_data == 0) return 0; // ...not yet... + const float //total_data = float(end_sdpos - start_sdpos), // Ex: 999b-280b+1 = 720b + sd_percent = elapsed_data / total_data, // Ex: 180b / 720b = 0.25 + sd_ratio = (1.0f - sd_percent) / sd_percent; // Ex: (1.0 - 0.25) / 0.25 = 3.0 + const uint32_t elapsed_time = duration() - start_time; // Ex: T2 - T1 = 300s + return uint32_t(elapsed_time * sd_ratio); // Ex: 300s * 3.0f = 900s + } + + FORCE_INLINE uint32_t remainingTimeEstimate(const uint32_t sdpos) const { + return remainingTimeEstimate(lap_start_time, sdpos, lap_start_sdpos, lap_total_data); + } + + /** + * Start a completion time estimate given a time, start spos, and end sdpos, + * Use the 'M75 R' command to call primeRemainingTimeEstimate at the first actual printing move. + * TODO: + * + * TODO: Whenever the printer does an E + XY move call primeRemainingTimeEstimate. + * If the flag is still 'false' set it to 'true', record the current + * print time and sdpos to pass to this method. + */ + FORCE_INLINE static void primeRemainingTimeEstimate(const uint32_t start_time, const uint32_t start_sdpos, const uint32_t end_sdpos) { + lap_start_time = start_time; + lap_start_sdpos = float(start_sdpos); + lap_total_data = float(end_sdpos - start_sdpos + 1UL); + } + + // Start estimation using the current time as with print_job_timer.primeRemainingTimeEstimate(...) + FORCE_INLINE void primeRemainingTimeEstimate(const uint32_t start_sdpos, const uint32_t end_sdpos) { + primeRemainingTimeEstimate(duration(), start_sdpos, end_sdpos); + } + + #endif // REMAINING_TIME_PRIME || REMAINING_TIME_AUTOPRIME + #ifdef DEBUG_STOPWATCH /** diff --git a/buildroot/tests/STM32F103RE b/buildroot/tests/STM32F103RE index f97b04c520..fbdee52c21 100755 --- a/buildroot/tests/STM32F103RE +++ b/buildroot/tests/STM32F103RE @@ -15,7 +15,8 @@ opt_set MOTHERBOARD BOARD_STM32F103RE SERIAL_PORT -1 EXTRUDERS 2 \ NOZZLE_CLEAN_END_POINT "{ { 10, 20, 3 } }" opt_enable EEPROM_SETTINGS EEPROM_CHITCHAT SDSUPPORT CONFIGURATION_EMBEDDING \ PAREN_COMMENTS GCODE_MOTION_MODES SINGLENOZZLE TOOLCHANGE_FILAMENT_SWAP TOOLCHANGE_PARK \ - BAUD_RATE_GCODE GCODE_MACROS NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE + BAUD_RATE_GCODE GCODE_MACROS NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE \ + REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER SHOW_REMAINING_TIME REMAINING_TIME_PRIME REMAINING_TIME_AUTOPRIME exec_test $1 $2 "STM32F1R EEPROM_SETTINGS EEPROM_CHITCHAT SDSUPPORT PAREN_COMMENTS GCODE_MOTION_MODES" "$3" # cleanup