"One Click" Print newest file (#25781)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
parent
d17d86d6a5
commit
2e24637bd6
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
44
Marlin/src/lcd/menu/menu_one_click_print.cpp
Normal file
44
Marlin/src/lcd/menu/menu_one_click_print.cpp
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue