"One Click" Print newest file (#25781)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
ellensp 2023-05-15 13:48:55 +12:00 committed by GitHub
parent d17d86d6a5
commit 2e24637bd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 176 additions and 20 deletions

View file

@ -1675,6 +1675,7 @@
//#define NO_SD_AUTOSTART // Remove auto#.g file support completely to save some Flash, SRAM
//#define MENU_ADDAUTOSTART // Add a menu option to run auto#.g files
//#define ONE_CLICK_PRINT // Prompt to print the newest file on inserted media
//#define BROWSE_MEDIA_ON_INSERT // Open the file browser when media is inserted
//#define MEDIA_MENU_AT_TOP // Force the media menu to be listed on the top of the main menu

View file

@ -4044,6 +4044,19 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
// Multi-Stepping Limit
static_assert(WITHIN(MULTISTEPPING_LIMIT, 1, 128) && IS_POWER_OF_2(MULTISTEPPING_LIMIT), "MULTISTEPPING_LIMIT must be 1, 2, 4, 8, 16, 32, 64, or 128.");
// One Click Print
#if ENABLED(ONE_CLICK_PRINT)
#if !HAS_MEDIA
#error "SD Card or Flash Drive is required for ONE_CLICK_PRINT."
#elif ENABLED(BROWSE_MEDIA_ON_INSERT)
#error "ONE_CLICK_PRINT is incompatible with BROWSE_MEDIA_ON_INSERT."
#elif DISABLED(NO_SD_AUTOSTART)
#error "NO_SD_AUTOSTART must be enabled for ONE_CLICK_PRINT."
#elif !defined(HAS_MARLINUI_MENU)
#error "ONE_CLICK_PRINT needs a display that has Marlin UI menus."
#endif
#endif
// Misc. Cleanup
#undef _TEST_PWM
#undef _NUM_AXES_STR

View file

@ -424,7 +424,7 @@ void MarlinUI::init() {
#if !HAS_GRAPHICAL_TFT
void _wrap_string(uint8_t &col, uint8_t &row, const char * const string, read_byte_cb_t cb_read_byte, bool wordwrap/*=false*/) {
void _wrap_string(uint8_t &col, uint8_t &row, const char * const string, read_byte_cb_t cb_read_byte, const bool wordwrap/*=false*/) {
SETCURSOR(col, row);
if (!string) return;

View file

@ -259,6 +259,10 @@ void _lcd_draw_homing();
void touch_screen_calibration();
#endif
#if ENABLED(ONE_CLICK_PRINT)
void one_click_print();
#endif
extern uint8_t screen_history_depth;
inline void clear_menu_history() { screen_history_depth = 0; }

View file

@ -73,14 +73,11 @@ class MenuItem_sdfile : public MenuItem_sdbase {
#endif
#if ENABLED(SD_MENU_CONFIRM_START)
MenuItem_submenu::action(fstr, []{
char * const longest = card.longest_filename();
char buffer[strlen(longest) + 2];
buffer[0] = ' ';
strcpy(buffer + 1, longest);
char * const filename = card.longest_filename();
MenuItem_confirm::select_screen(
GET_TEXT_F(MSG_BUTTON_PRINT), GET_TEXT_F(MSG_BUTTON_CANCEL),
sdcard_start_selected_file, nullptr,
GET_TEXT_F(MSG_START_PRINT), buffer, F("?")
GET_TEXT_F(MSG_START_PRINT), filename, F("?")
);
});
#else

View file

@ -0,0 +1,44 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(ONE_CLICK_PRINT)
#include "menu.h"
void one_click_print() {
ui.goto_screen([]{
char * const filename = card.longest_filename();
MenuItem_confirm::select_screen(
GET_TEXT_F(MSG_BUTTON_PRINT), GET_TEXT_F(MSG_BUTTON_CANCEL),
[]{
card.openAndPrintFile(card.filename);
ui.return_to_status();
ui.reset_status();
}, nullptr,
GET_TEXT_F(MSG_START_PRINT), filename, F("?")
);
});
}
#endif // ONE_CLICK_PRINT

View file

@ -56,6 +56,10 @@
#include "../feature/pause.h"
#endif
#if ENABLED(ONE_CLICK_PRINT)
#include "../../src/lcd/menu/menu.h"
#endif
#define DEBUG_OUT EITHER(DEBUG_CARDREADER, MARLIN_DEV_MODE)
#include "../core/debug_out.h"
#include "../libs/hex_print.h"
@ -290,7 +294,7 @@ void CardReader::printListing(MediaFile parent, const char * const prepend, cons
while (parent.readDir(&p, longFilename) > 0) {
if (DIR_IS_SUBDIR(&p)) {
size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
const size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
// Allocate enough stack space for the full path including / separator
char path[lenPrepend + FILENAME_LENGTH];
if (prepend) { strcpy(path, prepend); path[lenPrepend - 1] = '/'; }
@ -545,20 +549,28 @@ void CardReader::manage_media() {
if (!stat) return; // Exit if no media is present
if (old_stat != 2) return; // First mount?
DEBUG_ECHOLNPGM("First mount.");
// Load settings the first time media is inserted (not just during init)
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
bool do_auto = true; UNUSED(do_auto);
// Check for PLR file.
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
// First mount on boot? Load emulated EEPROM and look for PLR file.
if (old_stat == 2) {
DEBUG_ECHOLNPGM("First mount.");
// Look for auto0.g on the next idle()
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
// Load settings the first time media is inserted (not just during init)
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
// Check for PLR file. Skip One-Click and auto#.g if found
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
}
// Find the newest file and prompt to print it.
TERN_(ONE_CLICK_PRINT, if (do_auto && one_click_check()) do_auto = false);
// Also for the first mount run auto#.g for machine init.
// (Skip if PLR or One-Click Print was invoked.)
if (old_stat == 2) {
// Look for auto0.g on the next idle()
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
}
}
/**
@ -887,6 +899,81 @@ void CardReader::write_command(char * const buf) {
}
#endif
#if ENABLED(ONE_CLICK_PRINT)
/**
* Select the newest file and ask the user if they want to print it.
*/
bool CardReader::one_click_check() {
const bool found = selectNewestFile();
if (found) {
//SERIAL_ECHO_MSG(" OCP File: ", longest_filename(), "\n");
//ui.init();
one_click_print();
}
return found;
}
/**
* Recurse the entire directory to find the newest file.
* This may take a very long time so watch out for watchdog reset.
* It may be best to only look at root for reasonable boot and mount times.
*/
void CardReader::diveToNewestFile(MediaFile parent, uint32_t &compareDateTime, MediaFile &outdir, char * const outname) {
// Iterate the given parent dir
parent.rewind();
for (dir_t p; parent.readDir(&p, longFilename) > 0;) {
// If the item is a dir, recurse into it
if (DIR_IS_SUBDIR(&p)) {
// Get the name of the dir for opening
char dirname[FILENAME_LENGTH];
createFilename(dirname, p);
// Open the item in a new MediaFile
MediaFile child; // child.close() in destructor
if (child.open(&parent, dirname, O_READ))
diveToNewestFile(child, compareDateTime, outdir, outname);
}
else if (is_visible_entity(p)) {
// Get the newer of the modified/created date and time
const uint32_t modDateTime = uint32_t(p.lastWriteDate) << 16 | p.lastWriteTime,
createDateTime = uint32_t(p.creationDate) << 16 | p.creationTime,
newerDateTime = _MAX(modDateTime, createDateTime);
// If a newer item is found overwrite the outdir and outname
if (newerDateTime > compareDateTime) {
compareDateTime = newerDateTime;
outdir = parent;
createFilename(outname, p);
}
}
}
}
/**
* Recurse the entire directory to find the newest file.
* Make the found file the current selection.
*/
bool CardReader::selectNewestFile() {
uint32_t dateTimeStorage = 0;
MediaFile foundDir;
char foundName[FILENAME_LENGTH];
foundName[0] = '\0';
diveToNewestFile(root, dateTimeStorage, foundDir, foundName);
if (foundName[0]) {
workDir = foundDir;
workDir.rewind();
selectByName(workDir, foundName);
//workDir.close(); // Not needed?
return true;
}
return false;
}
#endif // ONE_CLICK_PRINT
void CardReader::closefile(const bool store_location/*=false*/) {
file.sync();
file.close();

View file

@ -128,6 +128,12 @@ public:
static void autofile_cancel() { autofile_index = 0; }
#endif
#if ENABLED(ONE_CLICK_PRINT)
static bool one_click_check(); // Check for the newest file and prompt to run it.
static void diveToNewestFile(MediaFile parent, uint32_t &compareDateTime, MediaFile &outdir, char * const outname);
static bool selectNewestFile();
#endif
// Basic file ops
static void openFileRead(const char * const path, const uint8_t subcall=0);
static void openFileWrite(const char * const path);

View file

@ -104,6 +104,9 @@
#if ENABLED(AUTO_BED_LEVELING_UBL)
#define HAS_MENU_UBL
#endif
#if ENABLED(ONE_CLICK_PRINT)
#define HAS_MENU_ONE_CLICK_PRINT
#endif
#endif
#if HAS_GRAPHICAL_TFT

View file

@ -25,8 +25,8 @@ opt_set MOTHERBOARD BOARD_MKS_TINYBEE \
LCD_INFO_SCREEN_STYLE 0 \
DISPLAY_CHARSET_HD44780 WESTERN \
NEOPIXEL_TYPE NEO_RGB
opt_enable FYSETC_MINI_12864_2_1 SDSUPPORT
opt_enable LED_CONTROL_MENU LED_USER_PRESET_STARTUP LED_COLOR_PRESETS NEOPIXEL_LED
opt_enable FYSETC_MINI_12864_2_1 SDSUPPORT ONE_CLICK_PRINT NO_SD_AUTOSTART \
NEOPIXEL_LED LED_CONTROL_MENU LED_USER_PRESET_STARTUP LED_COLOR_PRESETS
exec_test $1 $2 "MKS TinyBee with NeoPixel LCD, SD and Speaker" "$3"
# cleanup

View file

@ -81,6 +81,7 @@ HAS_MENU_LED = build_src_filter=+<src/lcd/menu/menu_le
HAS_MENU_MEDIA = build_src_filter=+<src/lcd/menu/menu_media.cpp>
HAS_MENU_MIXER = build_src_filter=+<src/lcd/menu/menu_mixer.cpp>
HAS_MENU_MMU2 = build_src_filter=+<src/lcd/menu/menu_mmu2.cpp>
HAS_MENU_ONE_CLICK_PRINT = build_src_filter=+<src/lcd/menu/menu_one_click_print.cpp>
HAS_MENU_PASSWORD = build_src_filter=+<src/lcd/menu/menu_password.cpp>
HAS_MENU_POWER_MONITOR = build_src_filter=+<src/lcd/menu/menu_power_monitor.cpp>
HAS_MENU_CUTTER = build_src_filter=+<src/lcd/menu/menu_spindle_laser.cpp>