2016-03-25 07:19:46 +01:00
/**
2016-03-24 19:01:20 +01:00
* Marlin 3 D Printer Firmware
2019-06-28 06:57:50 +02:00
* Copyright ( c ) 2019 MarlinFirmware [ https : //github.com/MarlinFirmware/Marlin]
2016-03-24 19:01:20 +01:00
*
* Based on Sprinter and grbl .
2019-06-28 06:57:50 +02:00
* Copyright ( c ) 2011 Camiel Gubbels / Erik van der Zalm
2016-03-24 19:01:20 +01:00
*
* 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 < http : //www.gnu.org/licenses/>.
*
*/
2017-09-06 13:28:32 +02:00
# include "../inc/MarlinConfig.h"
2016-08-03 04:36:58 +02:00
2017-09-06 13:28:32 +02:00
# if ENABLED(SDSUPPORT)
2012-03-03 16:51:47 +01:00
2017-09-06 13:28:32 +02:00
# include "cardreader.h"
2016-08-03 04:36:58 +02:00
2017-09-06 13:28:32 +02:00
# include "../Marlin.h"
# include "../lcd/ultralcd.h"
2018-05-12 08:38:02 +02:00
# include "../module/planner.h"
2017-09-06 13:28:32 +02:00
# include "../module/printcounter.h"
# include "../core/language.h"
2017-09-08 05:33:16 +02:00
# include "../gcode/queue.h"
2019-06-29 08:34:54 +02:00
# include "../module/configuration_store.h"
2017-09-06 13:28:32 +02:00
2018-10-31 01:44:12 +01:00
# if ENABLED(EMERGENCY_PARSER)
# include "../feature/emergency_parser.h"
# endif
2018-04-22 02:41:26 +02:00
# if ENABLED(POWER_LOSS_RECOVERY)
# include "../feature/power_loss_recovery.h"
# endif
2018-02-23 20:42:15 +01:00
# if ENABLED(ADVANCED_PAUSE_FEATURE)
# include "../feature/pause.h"
# endif
2018-11-17 05:39:16 +01:00
// public:
card_flags_t CardReader : : flag ;
2018-11-20 12:36:19 +01:00
char CardReader : : filename [ FILENAME_LENGTH ] , CardReader : : longFilename [ LONG_FILENAME_LENGTH ] ;
2018-11-17 05:39:16 +01:00
int8_t CardReader : : autostart_index ;
2019-02-28 02:57:48 +01:00
# if ENABLED(BINARY_FILE_TRANSFER) && NUM_SERIAL > 1
2019-02-28 03:14:17 +01:00
int8_t CardReader : : transfer_port_index ;
2018-11-17 05:39:16 +01:00
# endif
// private:
SdFile CardReader : : root , CardReader : : workDir , CardReader : : workDirParents [ MAX_DIR_DEPTH ] ;
uint8_t CardReader : : workDirDepth ;
# if ENABLED(SDCARD_SORT_ALPHA)
uint16_t CardReader : : sort_count ;
# if ENABLED(SDSORT_GCODE)
bool CardReader : : sort_alpha ;
int CardReader : : sort_folders ;
//bool CardReader::sort_reverse;
# endif
# if ENABLED(SDSORT_DYNAMIC_RAM)
uint8_t * CardReader : : sort_order ;
# else
uint8_t CardReader : : sort_order [ SDSORT_LIMIT ] ;
# endif
# if ENABLED(SDSORT_USES_RAM)
# if ENABLED(SDSORT_CACHE_NAMES)
# if ENABLED(SDSORT_DYNAMIC_RAM)
char * * CardReader : : sortshort , * * CardReader : : sortnames ;
# else
2018-11-20 12:36:19 +01:00
char CardReader : : sortshort [ SDSORT_LIMIT ] [ FILENAME_LENGTH ] ;
2019-05-28 02:50:48 +02:00
char CardReader : : sortnames [ SDSORT_LIMIT ] [ SORTED_LONGNAME_STORAGE ] ;
2018-11-17 05:39:16 +01:00
# endif
# elif DISABLED(SDSORT_USES_STACK)
2019-05-28 02:50:48 +02:00
char CardReader : : sortnames [ SDSORT_LIMIT ] [ SORTED_LONGNAME_STORAGE ] ;
2018-11-17 05:39:16 +01:00
# endif
# if HAS_FOLDER_SORTING
# if ENABLED(SDSORT_DYNAMIC_RAM)
uint8_t * CardReader : : isDir ;
# elif ENABLED(SDSORT_CACHE_NAMES) || DISABLED(SDSORT_USES_STACK)
uint8_t CardReader : : isDir [ ( SDSORT_LIMIT + 7 ) > > 3 ] ;
# endif
# endif
# endif // SDSORT_USES_RAM
# endif // SDCARD_SORT_ALPHA
Sd2Card CardReader : : sd2card ;
SdVolume CardReader : : volume ;
SdFile CardReader : : file ;
uint8_t CardReader : : file_subcall_ctr ;
uint32_t CardReader : : filespos [ SD_PROCEDURE_DEPTH ] ;
2018-11-20 12:36:19 +01:00
char CardReader : : proc_filenames [ SD_PROCEDURE_DEPTH ] [ MAXPATHNAMELENGTH ] ;
2018-11-17 05:39:16 +01:00
uint32_t CardReader : : filesize , CardReader : : sdpos ;
LsAction CardReader : : lsAction ; //stored for recursion.
uint16_t CardReader : : nrFiles ; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
char * CardReader : : diveDirName ;
2015-03-02 16:06:01 +01:00
CardReader : : CardReader ( ) {
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
sort_count = 0 ;
# if ENABLED(SDSORT_GCODE)
sort_alpha = true ;
sort_folders = FOLDER_SORTING ;
//sort_reverse = false;
# endif
# endif
2019-09-15 10:10:59 +02:00
flag . sdprinting = flag . mounted = flag . saving = flag . logging = false ;
2018-11-17 05:39:16 +01:00
filesize = sdpos = 0 ;
2015-03-02 16:06:01 +01:00
file_subcall_ctr = 0 ;
2017-11-15 07:06:20 +01:00
workDirDepth = 0 ;
2016-10-22 17:07:18 +02:00
ZERO ( workDirParents ) ;
2011-12-22 14:55:45 +01:00
2018-05-01 08:01:02 +02:00
// Disable autostart until card is initialized
autostart_index = - 1 ;
2015-05-13 11:02:19 +02:00
2019-08-02 09:19:39 +02:00
# if PIN_EXISTS(SDPOWER)
OUT_WRITE ( SDPOWER_PIN , HIGH ) ; // Power the SD reader
2018-05-01 08:01:02 +02:00
# endif
2011-11-06 21:39:53 +01:00
}
2018-11-17 05:39:16 +01:00
char * createFilename ( char * buffer , const dir_t & p ) {
2015-03-02 16:06:01 +01:00
char * pos = buffer ;
for ( uint8_t i = 0 ; i < 11 ; i + + ) {
if ( p . name [ i ] = = ' ' ) continue ;
if ( i = = 8 ) * pos + + = ' . ' ;
* pos + + = p . name [ i ] ;
2011-11-19 13:13:34 +01:00
}
2015-03-02 16:06:01 +01:00
* pos + + = 0 ;
2011-11-19 13:13:34 +01:00
return buffer ;
}
2015-05-15 12:19:07 +02:00
/**
* Dive into a folder and recurse depth - first to perform a pre - set operation lsAction :
* LS_Count - Add + 1 to nrFiles for every file within the parent
2017-07-30 07:02:41 +02:00
* LS_GetFilename - Get the filename of the file indexed by nrFile_index
2017-04-12 21:35:31 +02:00
* LS_SerialPrint - Print the full path and size of each file to serial output
2015-05-15 12:19:07 +02:00
*/
2017-09-06 13:28:32 +02:00
2017-07-30 07:02:41 +02:00
uint16_t nrFile_index ;
2019-05-09 18:45:55 +02:00
void CardReader : : lsDive ( const char * prepend , SdFile parent , const char * const match /*=nullptr*/ ) {
2011-11-19 13:13:34 +01:00
dir_t p ;
2015-03-02 16:06:01 +01:00
uint8_t cnt = 0 ;
2011-11-19 13:13:34 +01:00
2015-05-15 12:19:07 +02:00
// Read the next entry from a directory
2018-05-24 08:18:56 +02:00
while ( parent . readDir ( & p , longFilename ) > 0 ) {
2015-05-15 12:19:07 +02:00
// If the entry is a directory and the action is LS_SerialPrint
if ( DIR_IS_SUBDIR ( & p ) & & lsAction ! = LS_Count & & lsAction ! = LS_GetFilename ) {
// Get the short name for the item, which we know is a folder
2018-11-20 12:36:19 +01:00
char dosFilename [ FILENAME_LENGTH ] ;
2018-05-24 08:18:56 +02:00
createFilename ( dosFilename , p ) ;
2015-03-02 16:06:01 +01:00
2015-07-16 21:10:38 +02:00
// Allocate enough stack space for the full path to a folder, trailing slash, and nul
2018-06-10 05:03:49 +02:00
const bool prepend_is_empty = ( ! prepend | | prepend [ 0 ] = = ' \0 ' ) ;
const int len = ( prepend_is_empty ? 1 : strlen ( prepend ) ) + strlen ( dosFilename ) + 1 + 1 ;
2015-07-16 21:10:38 +02:00
char path [ len ] ;
2015-05-15 12:19:07 +02:00
// Append the FOLDERNAME12/ to the passed string.
// It contains the full path to the "parent" argument.
// We now have the full path to the item in this folder.
2015-07-16 21:42:57 +02:00
strcpy ( path , prepend_is_empty ? " / " : prepend ) ; // root slash if prepend is empty
2018-11-17 05:39:16 +01:00
strcat ( path , dosFilename ) ; // FILENAME_LENGTH characters maximum
strcat ( path , " / " ) ; // 1 character
2015-03-02 16:06:01 +01:00
2015-05-15 12:19:07 +02:00
// Serial.print(path);
2015-03-02 16:06:01 +01:00
2015-05-15 12:19:07 +02:00
// Get a new directory object using the full path
// and dive recursively into it.
2011-11-19 13:13:34 +01:00
SdFile dir ;
2018-05-24 08:18:56 +02:00
if ( ! dir . open ( & parent , dosFilename , O_READ ) ) {
2015-03-02 16:06:01 +01:00
if ( lsAction = = LS_SerialPrint ) {
2019-02-24 05:53:01 +01:00
SERIAL_ECHO_START ( ) ;
2019-09-26 10:47:26 +02:00
SERIAL_ECHOLNPAIR ( MSG_SD_CANT_OPEN_SUBDIR , dosFilename ) ;
2011-11-19 13:13:34 +01:00
}
}
2019-02-24 05:53:01 +01:00
lsDive ( path , dir ) ;
2015-05-15 12:19:07 +02:00
// close() is done automatically by destructor of SdFile
2011-11-19 13:13:34 +01:00
}
2015-03-02 16:06:01 +01:00
else {
2016-03-15 18:47:58 +01:00
uint8_t pn0 = p . name [ 0 ] ;
2014-12-18 08:07:36 +01:00
if ( pn0 = = DIR_NAME_FREE ) break ;
2015-03-02 16:56:11 +01:00
if ( pn0 = = DIR_NAME_DELETED | | pn0 = = ' . ' ) continue ;
2015-05-15 12:19:07 +02:00
if ( longFilename [ 0 ] = = ' . ' ) continue ;
2014-12-18 16:49:16 +01:00
2016-11-26 13:26:28 +01:00
if ( ! DIR_IS_FILE_OR_SUBDIR ( & p ) | | ( p . attributes & DIR_ATT_HIDDEN ) ) continue ;
2015-03-02 16:06:01 +01:00
2018-11-17 05:39:16 +01:00
flag . filenameIsDir = DIR_IS_SUBDIR ( & p ) ;
2015-03-02 16:06:01 +01:00
2018-11-17 05:39:16 +01:00
if ( ! flag . filenameIsDir & & ( p . name [ 8 ] ! = ' G ' | | p . name [ 9 ] = = ' ~ ' ) ) continue ;
2015-03-02 16:06:01 +01:00
2017-07-30 07:02:41 +02:00
switch ( lsAction ) { // 1 based file count
2015-05-15 12:19:07 +02:00
case LS_Count :
nrFiles + + ;
break ;
2017-04-12 21:35:31 +02:00
2015-05-15 12:19:07 +02:00
case LS_SerialPrint :
createFilename ( filename , p ) ;
2019-02-24 05:53:01 +01:00
if ( prepend ) SERIAL_ECHO ( prepend ) ;
SERIAL_ECHO ( filename ) ;
SERIAL_CHAR ( ' ' ) ;
SERIAL_ECHOLN ( p . fileSize ) ;
2015-05-15 12:19:07 +02:00
break ;
2017-04-12 21:35:31 +02:00
2015-05-15 12:19:07 +02:00
case LS_GetFilename :
createFilename ( filename , p ) ;
2019-05-09 18:45:55 +02:00
if ( match ! = nullptr ) {
2015-05-15 12:19:07 +02:00
if ( strcasecmp ( match , filename ) = = 0 ) return ;
}
2017-07-30 07:02:41 +02:00
else if ( cnt = = nrFile_index ) return ; // 0 based index
2015-05-15 12:19:07 +02:00
cnt + + ;
break ;
2011-11-19 14:34:27 +01:00
}
2015-05-15 12:19:07 +02:00
2011-11-19 13:13:34 +01:00
}
2015-05-15 12:19:07 +02:00
} // while readDir
2011-11-19 13:13:34 +01:00
}
2019-02-24 05:53:01 +01:00
void CardReader : : ls ( ) {
2015-03-02 16:06:01 +01:00
lsAction = LS_SerialPrint ;
2011-11-19 13:13:34 +01:00
root . rewind ( ) ;
2019-05-09 18:45:55 +02:00
lsDive ( nullptr , root ) ;
2011-11-19 13:13:34 +01:00
}
2015-07-31 07:21:18 +02:00
# if ENABLED(LONG_FILENAME_HOST_SUPPORT)
2015-05-18 02:36:32 +02:00
/**
* Get a long pretty path based on a DOS 8.3 path
*/
2019-02-24 05:53:01 +01:00
void CardReader : : printLongPath ( char * path ) {
2015-05-18 02:36:32 +02:00
lsAction = LS_GetFilename ;
int i , pathLen = strlen ( path ) ;
2019-02-24 05:53:01 +01:00
// SERIAL_ECHOPGM("Full Path: "); SERIAL_ECHOLN(path);
2015-05-18 02:36:32 +02:00
// Zero out slashes to make segments
for ( i = 0 ; i < pathLen ; i + + ) if ( path [ i ] = = ' / ' ) path [ i ] = ' \0 ' ;
SdFile diveDir = root ; // start from the root for segment 1
for ( i = 0 ; i < pathLen ; ) {
if ( path [ i ] = = ' \0 ' ) i + + ; // move past a single nul
char * segment = & path [ i ] ; // The segment after most slashes
// If a segment is empty (extra-slash) then exit
if ( ! * segment ) break ;
// Go to the next segment
while ( path [ + + i ] ) { }
2019-02-24 05:53:01 +01:00
// SERIAL_ECHOPGM("Looking for segment: "); SERIAL_ECHOLN(segment);
2015-05-18 02:36:32 +02:00
// Find the item, setting the long filename
diveDir . rewind ( ) ;
2019-05-09 18:45:55 +02:00
lsDive ( nullptr , diveDir , segment ) ;
2015-05-18 02:36:32 +02:00
// Print /LongNamePart to serial output
2019-02-24 05:53:01 +01:00
SERIAL_CHAR ( ' / ' ) ;
SERIAL_ECHO ( longFilename [ 0 ] ? longFilename : " ??? " ) ;
2015-05-18 02:36:32 +02:00
// If the filename was printed then that's it
2018-11-17 05:39:16 +01:00
if ( ! flag . filenameIsDir ) break ;
2015-05-18 02:36:32 +02:00
2019-02-24 05:53:01 +01:00
// SERIAL_ECHOPGM("Opening dir: "); SERIAL_ECHOLN(segment);
2015-05-18 02:36:32 +02:00
// Open the sub-item as the new dive parent
SdFile dir ;
2018-05-24 08:18:56 +02:00
if ( ! dir . open ( & diveDir , segment , O_READ ) ) {
2019-02-24 05:53:01 +01:00
SERIAL_EOL ( ) ;
SERIAL_ECHO_START ( ) ;
2019-09-26 10:47:26 +02:00
SERIAL_ECHOPAIR ( MSG_SD_CANT_OPEN_SUBDIR , segment ) ;
2015-05-18 02:36:32 +02:00
break ;
}
diveDir . close ( ) ;
diveDir = dir ;
} // while i<pathLen
2019-02-24 05:53:01 +01:00
SERIAL_EOL ( ) ;
2015-05-18 02:36:32 +02:00
}
# endif // LONG_FILENAME_HOST_SUPPORT
2018-03-11 11:57:31 +01:00
/**
* Echo the DOS 8.3 filename ( and long filename , if any )
*/
2019-02-24 05:53:01 +01:00
void CardReader : : printFilename ( ) {
2018-03-11 11:57:31 +01:00
if ( file . isOpen ( ) ) {
2018-11-20 12:36:19 +01:00
char dosFilename [ FILENAME_LENGTH ] ;
2019-09-14 13:42:28 +02:00
file . getDosName ( dosFilename ) ;
2019-02-24 05:53:01 +01:00
SERIAL_ECHO ( dosFilename ) ;
2018-03-11 11:57:31 +01:00
# if ENABLED(LONG_FILENAME_HOST_SUPPORT)
2018-05-24 08:18:56 +02:00
getfilename ( 0 , dosFilename ) ;
2018-03-11 11:57:31 +01:00
if ( longFilename [ 0 ] ) {
2019-02-24 05:53:01 +01:00
SERIAL_ECHO ( ' ' ) ;
SERIAL_ECHO ( longFilename ) ;
2018-03-11 11:57:31 +01:00
}
# endif
}
else
2019-02-24 05:53:01 +01:00
SERIAL_ECHOPGM ( " (no file) " ) ;
2018-03-11 11:57:31 +01:00
2019-02-24 05:53:01 +01:00
SERIAL_EOL ( ) ;
2018-03-11 11:57:31 +01:00
}
2019-09-15 10:10:59 +02:00
void CardReader : : mount ( ) {
flag . mounted = false ;
2015-03-02 16:06:01 +01:00
if ( root . isOpen ( ) ) root . close ( ) ;
2015-12-14 05:23:01 +01:00
# ifndef SPI_SPEED
2015-03-02 16:06:01 +01:00
# define SPI_SPEED SPI_FULL_SPEED
2014-03-27 18:02:17 +01:00
# endif
2015-03-02 16:06:01 +01:00
2018-05-24 08:18:56 +02:00
if ( ! sd2card . init ( SPI_SPEED , SDSS )
2015-03-02 16:06:01 +01:00
# if defined(LCD_SDSS) && (LCD_SDSS != SDSS)
2018-05-24 08:18:56 +02:00
& & ! sd2card . init ( SPI_SPEED , LCD_SDSS )
2015-03-02 16:06:01 +01:00
# endif
) {
2018-05-24 08:18:56 +02:00
//if (!sd2card.init(SPI_HALF_SPEED,SDSS))
2018-11-29 23:58:58 +01:00
SERIAL_ECHO_MSG ( MSG_SD_INIT_FAIL ) ;
2011-11-20 14:43:47 +01:00
}
2018-11-29 23:58:58 +01:00
else if ( ! volume . init ( & sd2card ) )
SERIAL_ERROR_MSG ( MSG_SD_VOL_INIT_FAIL ) ;
else if ( ! root . openRoot ( & volume ) )
SERIAL_ERROR_MSG ( MSG_SD_OPENROOT_FAIL ) ;
2015-03-02 16:06:01 +01:00
else {
2019-09-15 10:10:59 +02:00
flag . mounted = true ;
2018-11-29 23:58:58 +01:00
SERIAL_ECHO_MSG ( MSG_SD_CARD_OK ) ;
2019-07-06 05:25:59 +02:00
# if ENABLED(EEPROM_SETTINGS) && NONE(FLASH_EEPROM_EMULATION, SPI_EEPROM, I2C_EEPROM)
settings . first_load ( ) ;
2019-07-04 02:11:30 +02:00
# endif
2011-11-20 14:43:47 +01:00
}
2017-11-15 07:06:20 +01:00
setroot ( ) ;
2018-12-08 22:02:14 +01:00
ui . refresh ( ) ;
2011-11-06 21:39:53 +01:00
}
2011-12-26 09:20:33 +01:00
2015-03-02 16:06:01 +01:00
void CardReader : : release ( ) {
2018-11-07 03:53:07 +01:00
stopSDPrint ( ) ;
2019-09-15 10:10:59 +02:00
flag . mounted = false ;
2011-11-06 22:48:15 +01:00
}
2016-02-21 02:35:35 +01:00
void CardReader : : openAndPrintFile ( const char * name ) {
2016-03-29 11:34:33 +02:00
char cmd [ 4 + strlen ( name ) + 1 ] ; // Room for "M23 ", filename, and null
2016-02-21 02:35:35 +01:00
sprintf_P ( cmd , PSTR ( " M23 %s " ) , name ) ;
for ( char * c = & cmd [ 4 ] ; * c ; c + + ) * c = tolower ( * c ) ;
2019-06-19 07:00:19 +02:00
queue . enqueue_one_now ( cmd ) ;
2019-06-22 07:37:21 +02:00
queue . enqueue_now_P ( PSTR ( " M24 " ) ) ;
2016-02-21 02:35:35 +01:00
}
2015-03-02 16:06:01 +01:00
void CardReader : : startFileprint ( ) {
2019-09-15 10:10:59 +02:00
if ( isMounted ( ) ) {
2018-11-17 05:39:16 +01:00
flag . sdprinting = true ;
2018-03-14 05:00:31 +01:00
# if SD_RESORT
2017-02-09 14:02:25 +01:00
flush_presort ( ) ;
# endif
}
2011-11-06 22:48:15 +01:00
}
2018-03-07 08:42:11 +01:00
void CardReader : : stopSDPrint (
# if SD_RESORT
const bool re_sort /*=false*/
# endif
) {
2018-02-23 20:42:15 +01:00
# if ENABLED(ADVANCED_PAUSE_FEATURE)
did_pause_print = 0 ;
# endif
2018-11-17 05:39:16 +01:00
flag . sdprinting = flag . abort_sd_printing = false ;
2016-08-12 12:21:10 +02:00
if ( isFileOpen ( ) ) file . close ( ) ;
2018-03-07 08:42:11 +01:00
# if SD_RESORT
if ( re_sort ) presort ( ) ;
# endif
2016-06-18 03:27:14 +02:00
}
2018-05-24 08:18:56 +02:00
void CardReader : : openLogFile ( char * const path ) {
2018-11-17 05:39:16 +01:00
flag . logging = true ;
2018-05-24 08:18:56 +02:00
openFile ( path , false ) ;
2013-03-16 23:02:57 +01:00
}
2011-11-19 13:13:34 +01:00
2017-11-15 07:15:57 +01:00
void appendAtom ( SdFile & file , char * & dst , uint8_t & cnt ) {
2019-09-14 13:42:28 +02:00
file . getDosName ( dst ) ;
2017-11-15 07:15:57 +01:00
while ( * dst & & cnt < MAXPATHNAMELENGTH ) { dst + + ; cnt + + ; }
if ( cnt < MAXPATHNAMELENGTH ) { * dst = ' / ' ; dst + + ; cnt + + ; }
}
2015-03-02 16:06:01 +01:00
void CardReader : : getAbsFilename ( char * t ) {
2017-11-15 07:15:57 +01:00
* t + + = ' / ' ; // Root folder
uint8_t cnt = 1 ;
for ( uint8_t i = 0 ; i < workDirDepth ; i + + ) // Loop to current work dir
appendAtom ( workDirParents [ i ] , t , cnt ) ;
2018-11-17 05:39:16 +01:00
if ( cnt < MAXPATHNAMELENGTH - ( FILENAME_LENGTH ) - 1 ) { // Leave room for filename and nul
2017-11-15 07:15:57 +01:00
appendAtom ( file , t , cnt ) ;
- - t ;
2013-10-22 10:02:18 +02:00
}
2017-11-15 07:15:57 +01:00
* t = ' \0 ' ;
2013-10-22 10:02:18 +02:00
}
2018-05-24 08:18:56 +02:00
void CardReader : : openFile ( char * const path , const bool read , const bool subcall /*=false*/ ) {
2016-08-12 12:22:02 +02:00
2019-09-15 10:10:59 +02:00
if ( ! isMounted ( ) ) return ;
2016-08-12 12:22:02 +02:00
uint8_t doing = 0 ;
2017-11-15 07:15:57 +01:00
if ( isFileOpen ( ) ) { // Replacing current file or doing a subroutine
if ( subcall ) {
2015-10-03 08:08:58 +02:00
if ( file_subcall_ctr > SD_PROCEDURE_DEPTH - 1 ) {
2018-11-29 23:58:58 +01:00
SERIAL_ERROR_MSG ( " trying to call sub-gcode files with too many levels. MAX level is: " STRINGIFY ( SD_PROCEDURE_DEPTH ) ) ;
2018-10-19 04:20:56 +02:00
kill ( ) ;
2015-10-03 08:08:58 +02:00
return ;
}
2015-03-02 16:06:01 +01:00
2017-11-15 07:15:57 +01:00
// Store current filename (based on workDirParents) and position
2016-03-29 12:15:01 +02:00
getAbsFilename ( proc_filenames [ file_subcall_ctr ] ) ;
2017-11-15 07:15:57 +01:00
filespos [ file_subcall_ctr ] = sdpos ;
2015-03-02 16:06:01 +01:00
2017-06-09 17:51:23 +02:00
SERIAL_ECHO_START ( ) ;
2019-03-05 13:46:19 +01:00
SERIAL_ECHOLNPAIR ( " SUBROUTINE CALL target: \" " , path , " \" parent: \" " , proc_filenames [ file_subcall_ctr ] , " \" pos " , sdpos ) ;
2015-10-03 08:08:58 +02:00
file_subcall_ctr + + ;
2016-03-29 13:57:19 +02:00
}
2017-11-15 07:06:20 +01:00
else
2016-08-12 12:22:02 +02:00
doing = 1 ;
2013-10-22 10:02:18 +02:00
}
2018-11-29 23:58:58 +01:00
else if ( subcall ) // Returning from a subcall?
SERIAL_ECHO_MSG ( " END SUBROUTINE " ) ;
2017-11-15 07:15:57 +01:00
else { // Opening fresh file
2016-08-12 12:22:02 +02:00
doing = 2 ;
file_subcall_ctr = 0 ; // Reset procedure depth in case user cancels print while in procedure
}
if ( doing ) {
2017-06-09 17:51:23 +02:00
SERIAL_ECHO_START ( ) ;
2016-08-12 12:22:02 +02:00
SERIAL_ECHOPGM ( " Now " ) ;
2017-11-15 07:06:20 +01:00
serialprintPGM ( doing = = 1 ? PSTR ( " doing " ) : PSTR ( " fresh " ) ) ;
2018-05-24 08:18:56 +02:00
SERIAL_ECHOLNPAIR ( " file: " , path ) ;
2013-10-22 10:02:18 +02:00
}
2016-08-12 12:21:10 +02:00
stopSDPrint ( ) ;
2015-03-02 16:06:01 +01:00
2018-05-24 08:18:56 +02:00
SdFile * curDir ;
2019-05-02 22:54:24 +02:00
const char * const fname = diveToFile ( curDir , path ) ;
2018-05-24 08:18:56 +02:00
if ( ! fname ) return ;
2015-03-02 16:06:01 +01:00
if ( read ) {
if ( file . open ( curDir , fname , O_READ ) ) {
2011-11-06 22:48:15 +01:00
filesize = file . fileSize ( ) ;
2017-11-15 07:06:20 +01:00
sdpos = 0 ;
2019-03-05 13:46:19 +01:00
SERIAL_ECHOLNPAIR ( MSG_SD_FILE_OPENED , fname , MSG_SD_SIZE , filesize ) ;
2018-11-29 23:58:58 +01:00
SERIAL_ECHOLNPGM ( MSG_SD_FILE_SELECTED ) ;
2018-03-11 11:57:31 +01:00
2014-12-18 08:07:36 +01:00
getfilename ( 0 , fname ) ;
2018-11-18 05:21:44 +01:00
ui . set_status ( longFilename [ 0 ] ? longFilename : fname ) ;
2018-03-11 11:57:31 +01:00
//if (longFilename[0]) {
2018-11-29 23:58:58 +01:00
// SERIAL_ECHOPAIR(MSG_SD_FILE_LONG_NAME, longFilename);
2018-03-11 11:57:31 +01:00
//}
2011-11-06 22:48:15 +01:00
}
2019-03-05 13:46:19 +01:00
else
SERIAL_ECHOLNPAIR ( MSG_SD_OPEN_FILE_FAIL , fname , " . " ) ;
2011-11-06 22:48:15 +01:00
}
2015-03-02 16:06:01 +01:00
else { //write
2019-03-05 13:46:19 +01:00
if ( ! file . open ( curDir , fname , O_CREAT | O_APPEND | O_WRITE | O_TRUNC ) )
SERIAL_ECHOLNPAIR ( MSG_SD_OPEN_FILE_FAIL , fname , " . " ) ;
2015-03-02 16:06:01 +01:00
else {
2018-11-17 05:39:16 +01:00
flag . saving = true ;
2018-10-31 01:44:12 +01:00
getfilename ( 0 , fname ) ;
# if ENABLED(EMERGENCY_PARSER)
emergency_parser . disable ( ) ;
# endif
2018-11-29 23:58:58 +01:00
SERIAL_ECHOLNPAIR ( MSG_SD_WRITE_TO_FILE , fname ) ;
2018-11-18 05:21:44 +01:00
ui . set_status ( fname ) ;
2011-11-06 22:48:15 +01:00
}
}
}
2017-11-15 07:06:20 +01:00
void CardReader : : removeFile ( const char * const name ) {
2019-09-15 10:10:59 +02:00
if ( ! isMounted ( ) ) return ;
2015-03-02 16:06:01 +01:00
2018-11-17 03:47:07 +01:00
//stopSDPrint();
2015-03-02 16:06:01 +01:00
2018-05-24 08:18:56 +02:00
SdFile * curDir ;
2019-05-02 22:54:24 +02:00
const char * const fname = diveToFile ( curDir , name ) ;
2018-05-24 08:18:56 +02:00
if ( ! fname ) return ;
2015-03-02 16:06:01 +01:00
if ( file . remove ( curDir , fname ) ) {
2018-11-29 23:58:58 +01:00
SERIAL_ECHOLNPAIR ( " File deleted: " , fname ) ;
2015-03-02 16:06:01 +01:00
sdpos = 0 ;
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
presort ( ) ;
# endif
2015-03-02 16:06:01 +01:00
}
2019-03-05 13:46:19 +01:00
else
SERIAL_ECHOLNPAIR ( " Deletion failed, File: " , fname , " . " ) ;
2012-03-03 21:58:12 +01:00
}
2019-02-24 05:53:01 +01:00
void CardReader : : report_status ( ) {
2019-01-12 03:42:11 +01:00
if ( isPrinting ( ) ) {
2019-02-24 05:53:01 +01:00
SERIAL_ECHOPGM ( MSG_SD_PRINTING_BYTE ) ;
SERIAL_ECHO ( sdpos ) ;
SERIAL_CHAR ( ' / ' ) ;
SERIAL_ECHOLN ( filesize ) ;
2011-11-06 22:48:15 +01:00
}
2017-11-15 07:06:20 +01:00
else
2019-02-24 05:53:01 +01:00
SERIAL_ECHOLNPGM ( MSG_SD_NOT_PRINTING ) ;
2011-11-06 22:48:15 +01:00
}
2015-03-02 16:06:01 +01:00
void CardReader : : write_command ( char * buf ) {
2011-11-06 21:39:53 +01:00
char * begin = buf ;
2019-05-09 18:45:55 +02:00
char * npos = nullptr ;
2011-11-06 21:39:53 +01:00
char * end = buf + strlen ( buf ) - 1 ;
file . writeError = false ;
2019-05-09 18:45:55 +02:00
if ( ( npos = strchr ( buf , ' N ' ) ) ! = nullptr ) {
2011-11-06 21:39:53 +01:00
begin = strchr ( npos , ' ' ) + 1 ;
end = strchr ( npos , ' * ' ) - 1 ;
}
end [ 1 ] = ' \r ' ;
end [ 2 ] = ' \n ' ;
end [ 3 ] = ' \0 ' ;
file . write ( begin ) ;
2018-11-29 23:58:58 +01:00
if ( file . writeError ) SERIAL_ERROR_MSG ( MSG_SD_ERR_WRITE_TO_FILE ) ;
2011-11-06 21:39:53 +01:00
}
2018-05-01 08:01:02 +02:00
//
// Run the next autostart file. Called:
// - On boot after successful card init
// - After finishing the previous autostart file
// - From the LCD command to run the autostart file
//
2015-03-02 16:06:01 +01:00
2018-05-01 08:01:02 +02:00
void CardReader : : checkautostart ( ) {
2015-03-02 16:06:01 +01:00
2018-11-17 05:39:16 +01:00
if ( autostart_index < 0 | | flag . sdprinting ) return ;
2011-11-06 21:39:53 +01:00
2019-09-15 10:10:59 +02:00
if ( ! isMounted ( ) ) mount ( ) ;
2019-07-06 05:25:59 +02:00
# if ENABLED(EEPROM_SETTINGS) && NONE(FLASH_EEPROM_EMULATION, SPI_EEPROM, I2C_EEPROM)
else settings . first_load ( ) ;
2019-07-04 02:11:30 +02:00
# endif
2019-09-15 10:10:59 +02:00
if ( isMounted ( )
2018-07-03 06:21:28 +02:00
# if ENABLED(POWER_LOSS_RECOVERY)
2018-11-17 03:47:07 +01:00
& & ! recovery . valid ( ) // Don't run auto#.g when a resume file exists
2018-07-03 06:21:28 +02:00
# endif
) {
2018-10-16 12:47:02 +02:00
char autoname [ 8 ] ;
sprintf_P ( autoname , PSTR ( " auto%c.g " ) , autostart_index + ' 0 ' ) ;
2018-05-01 08:01:02 +02:00
dir_t p ;
root . rewind ( ) ;
2019-05-09 18:45:55 +02:00
while ( root . readDir ( & p , nullptr ) > 0 ) {
2018-05-01 08:01:02 +02:00
for ( int8_t i = ( int8_t ) strlen ( ( char * ) p . name ) ; i - - ; ) p . name [ i ] = tolower ( p . name [ i ] ) ;
if ( p . name [ 9 ] ! = ' ~ ' & & strncmp ( ( char * ) p . name , autoname , 5 ) = = 0 ) {
openAndPrintFile ( autoname ) ;
autostart_index + + ;
return ;
}
2011-11-06 21:39:53 +01:00
}
}
2018-05-01 08:01:02 +02:00
autostart_index = - 1 ;
}
void CardReader : : beginautostart ( ) {
autostart_index = 0 ;
setroot ( ) ;
2011-11-06 21:39:53 +01:00
}
2018-05-01 08:01:02 +02:00
void CardReader : : closefile ( const bool store_location ) {
2011-11-06 22:48:15 +01:00
file . sync ( ) ;
2011-11-06 21:39:53 +01:00
file . close ( ) ;
2018-11-17 05:39:16 +01:00
flag . saving = flag . logging = false ;
2018-11-17 03:47:07 +01:00
sdpos = 0 ;
2018-10-31 01:44:12 +01:00
# if ENABLED(EMERGENCY_PARSER)
emergency_parser . enable ( ) ;
# endif
2015-03-02 16:06:01 +01:00
if ( store_location ) {
2014-02-17 11:58:36 +01:00
//future: store printer state, filename and position for continuing a stopped print
2013-10-22 10:04:08 +02:00
// so one can unplug the printer and continue printing the next day.
}
2011-11-06 21:39:53 +01:00
}
2015-03-02 16:06:01 +01:00
/**
* Get the name of a file in the current directory by index
2018-05-24 08:18:56 +02:00
* with optional name to match .
2015-03-02 16:06:01 +01:00
*/
2019-05-09 18:45:55 +02:00
void CardReader : : getfilename ( uint16_t nr , const char * const match /*=nullptr*/ ) {
2017-02-09 14:02:25 +01:00
# if ENABLED(SDSORT_CACHE_NAMES)
2019-05-09 18:45:55 +02:00
if ( match ! = nullptr ) {
2017-02-09 14:02:25 +01:00
while ( nr < sort_count ) {
if ( strcasecmp ( match , sortshort [ nr ] ) = = 0 ) break ;
nr + + ;
}
}
if ( nr < sort_count ) {
strcpy ( filename , sortshort [ nr ] ) ;
strcpy ( longFilename , sortnames [ nr ] ) ;
2018-11-17 05:39:16 +01:00
flag . filenameIsDir = TEST ( isDir [ nr > > 3 ] , nr & 0x07 ) ;
2017-02-09 14:02:25 +01:00
return ;
}
# endif // SDSORT_CACHE_NAMES
2015-03-02 16:06:01 +01:00
lsAction = LS_GetFilename ;
2017-07-30 07:02:41 +02:00
nrFile_index = nr ;
2018-05-24 08:18:56 +02:00
workDir . rewind ( ) ;
2019-05-09 18:45:55 +02:00
lsDive ( nullptr , workDir , match ) ;
2011-11-06 21:39:53 +01:00
}
2019-09-17 00:49:55 +02:00
uint16_t CardReader : : countFilesInWorkDir ( ) {
2015-03-02 16:06:01 +01:00
lsAction = LS_Count ;
nrFiles = 0 ;
2018-05-24 08:18:56 +02:00
workDir . rewind ( ) ;
2019-05-09 18:45:55 +02:00
lsDive ( nullptr , workDir ) ;
2011-11-20 14:43:47 +01:00
//SERIAL_ECHOLN(nrFiles);
2011-11-19 13:13:34 +01:00
return nrFiles ;
2011-11-06 21:39:53 +01:00
}
2018-05-24 08:18:56 +02:00
/**
2018-11-17 03:47:07 +01:00
* Dive to the given DOS 8.3 file path , with optional echo of the dive paths .
*
* On exit , curDir contains an SdFile reference to the file ' s directory .
*
* Returns a pointer to the last segment ( filename ) of the given DOS 8.3 path .
*
2019-05-09 18:45:55 +02:00
* A nullptr result indicates an unrecoverable error .
2018-05-24 08:18:56 +02:00
*/
2019-05-02 22:54:24 +02:00
const char * CardReader : : diveToFile ( SdFile * & curDir , const char * const path , const bool echo /*=false*/ ) {
// Track both parent and subfolder
static SdFile newDir1 , newDir2 ;
SdFile * sub = & newDir1 , * startDir ;
const char * dirname_start = path ;
2019-05-09 18:45:55 +02:00
if ( path [ 0 ] = = ' / ' ) {
2019-05-02 22:54:24 +02:00
curDir = & root ;
workDirDepth = 0 ;
dirname_start + + ;
}
else
2019-05-09 18:45:55 +02:00
curDir = & workDir ;
2019-05-02 22:54:24 +02:00
startDir = curDir ;
2015-03-02 16:06:01 +01:00
2019-05-02 22:54:24 +02:00
// Start dive
2018-05-24 08:18:56 +02:00
while ( dirname_start ) {
2019-05-02 22:54:24 +02:00
// Find next sub
2018-05-24 08:18:56 +02:00
char * const dirname_end = strchr ( dirname_start , ' / ' ) ;
if ( dirname_end < = dirname_start ) break ;
2019-05-02 22:54:24 +02:00
// Set subDirName
2018-05-24 08:18:56 +02:00
const uint8_t len = dirname_end - dirname_start ;
2018-06-06 05:07:52 +02:00
char dosSubdirname [ len + 1 ] ;
2018-05-24 08:18:56 +02:00
strncpy ( dosSubdirname , dirname_start , len ) ;
dosSubdirname [ len ] = 0 ;
if ( echo ) SERIAL_ECHOLN ( dosSubdirname ) ;
2019-05-02 22:54:24 +02:00
// Open curDir
if ( ! sub - > open ( curDir , dosSubdirname , O_READ ) ) {
2019-03-05 13:46:19 +01:00
SERIAL_ECHOLNPAIR ( MSG_SD_OPEN_FILE_FAIL , dosSubdirname , " . " ) ;
2019-05-09 18:45:55 +02:00
return nullptr ;
2018-05-24 08:18:56 +02:00
}
2019-05-02 22:54:24 +02:00
// Close curDir if not at starting-point
if ( curDir ! = startDir ) curDir - > close ( ) ;
// curDir now subDir
curDir = sub ;
// Update workDirParents and workDirDepth
if ( workDirDepth < MAX_DIR_DEPTH ) workDirParents [ workDirDepth + + ] = * curDir ;
// Point sub pointer to unused newDir
sub = ( curDir ! = & newDir1 ) ? & newDir1 : & newDir2 ;
// dirname_start point to next sub
2018-05-24 08:18:56 +02:00
dirname_start = dirname_end + 1 ;
2011-11-20 14:43:47 +01:00
}
2018-05-24 08:18:56 +02:00
return dirname_start ;
}
void CardReader : : chdir ( const char * relpath ) {
SdFile newDir ;
SdFile * parent = workDir . isOpen ( ) ? & workDir : & root ;
if ( newDir . open ( parent , relpath , O_READ ) ) {
2017-11-15 07:15:57 +01:00
workDir = newDir ;
2019-09-17 00:49:55 +02:00
flag . workDirIsRoot = false ;
2016-03-29 12:18:53 +02:00
if ( workDirDepth < MAX_DIR_DEPTH )
2017-11-15 07:15:57 +01:00
workDirParents [ workDirDepth + + ] = workDir ;
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
presort ( ) ;
# endif
2011-11-20 14:43:47 +01:00
}
2018-05-24 08:18:56 +02:00
else {
SERIAL_ECHO_START ( ) ;
2018-11-29 23:58:58 +01:00
SERIAL_ECHOLNPAIR ( MSG_SD_CANT_ENTER_SUBDIR , relpath ) ;
2018-05-24 08:18:56 +02:00
}
2011-11-20 14:43:47 +01:00
}
2017-11-26 08:14:18 +01:00
int8_t CardReader : : updir ( ) {
if ( workDirDepth > 0 ) { // At least 1 dir has been saved
workDir = - - workDirDepth ? workDirParents [ workDirDepth - 1 ] : root ; // Use parent, or root if none
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
presort ( ) ;
# endif
}
2019-09-17 00:49:55 +02:00
if ( ! workDirDepth ) flag . workDirIsRoot = true ;
2017-11-26 08:14:18 +01:00
return workDirDepth ;
2014-11-24 23:03:20 +01:00
}
2018-05-24 08:18:56 +02:00
void CardReader : : setroot ( ) {
workDir = root ;
2019-09-17 00:49:55 +02:00
flag . workDirIsRoot = true ;
2018-05-24 08:18:56 +02:00
# if ENABLED(SDCARD_SORT_ALPHA)
presort ( ) ;
# endif
}
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
/**
* Get the name of a file in the current directory by sort - index
*/
void CardReader : : getfilename_sorted ( const uint16_t nr ) {
getfilename (
# if ENABLED(SDSORT_GCODE)
sort_alpha & &
# endif
( nr < sort_count ) ? sort_order [ nr ] : nr
) ;
}
2018-11-17 05:39:16 +01:00
# if ENABLED(SDSORT_USES_RAM)
# if ENABLED(SDSORT_DYNAMIC_RAM)
// Use dynamic method to copy long filename
# define SET_SORTNAME(I) (sortnames[I] = strdup(longest_filename()))
# if ENABLED(SDSORT_CACHE_NAMES)
// When caching also store the short name, since
// we're replacing the getfilename() behavior.
# define SET_SORTSHORT(I) (sortshort[I] = strdup(filename))
# else
# define SET_SORTSHORT(I) NOOP
# endif
# else
// Copy filenames into the static array
2019-05-28 02:50:48 +02:00
# define _SET_SORTNAME(I) strncpy(sortnames[I], longest_filename(), SORTED_LONGNAME_MAXLEN)
# if SORTED_LONGNAME_MAXLEN == LONG_FILENAME_LENGTH
// Short name sorting always use LONG_FILENAME_LENGTH with no trailing nul
# define SET_SORTNAME(I) _SET_SORTNAME(I)
2018-11-17 05:39:16 +01:00
# else
2019-05-28 02:50:48 +02:00
// Copy multiple name blocks. Add a nul for the longest case.
# define SET_SORTNAME(I) do{ _SET_SORTNAME(I); sortnames[I][SORTED_LONGNAME_MAXLEN] = '\0'; }while(0)
2018-11-17 05:39:16 +01:00
# endif
# if ENABLED(SDSORT_CACHE_NAMES)
# define SET_SORTSHORT(I) strcpy(sortshort[I], filename)
# else
# define SET_SORTSHORT(I) NOOP
# endif
# endif
# endif
2017-02-09 14:02:25 +01:00
/**
* Read all the files and produce a sort key
*
* We can do this in 3 ways . . .
* - Minimal RAM : Read two filenames at a time sorting along . . .
* - Some RAM : Buffer the directory just for this sort
* - Most RAM : Buffer the directory and return filenames from RAM
*/
void CardReader : : presort ( ) {
2018-03-07 08:42:11 +01:00
// Throw away old sort index
flush_presort ( ) ;
2017-02-09 14:02:25 +01:00
// Sorting may be turned off
# if ENABLED(SDSORT_GCODE)
if ( ! sort_alpha ) return ;
# endif
// If there are files, sort up to the limit
2019-09-17 00:49:55 +02:00
uint16_t fileCnt = countFilesInWorkDir ( ) ;
2017-02-09 14:02:25 +01:00
if ( fileCnt > 0 ) {
// Never sort more than the max allowed
// If you use folders to organize, 20 may be enough
2019-06-19 06:25:56 +02:00
NOMORE ( fileCnt , uint16_t ( SDSORT_LIMIT ) ) ;
2017-02-09 14:02:25 +01:00
2017-02-09 14:05:34 +01:00
// Sort order is always needed. May be static or dynamic.
# if ENABLED(SDSORT_DYNAMIC_RAM)
sort_order = new uint8_t [ fileCnt ] ;
# endif
2017-02-09 14:02:25 +01:00
// Use RAM to store the entire directory during pre-sort.
// SDSORT_LIMIT should be set to prevent over-allocation.
# if ENABLED(SDSORT_USES_RAM)
2017-02-09 14:05:34 +01:00
// If using dynamic ram for names, allocate on the heap.
# if ENABLED(SDSORT_CACHE_NAMES)
# if ENABLED(SDSORT_DYNAMIC_RAM)
sortshort = new char * [ fileCnt ] ;
sortnames = new char * [ fileCnt ] ;
2017-02-09 14:02:25 +01:00
# endif
2017-02-09 14:05:34 +01:00
# elif ENABLED(SDSORT_USES_STACK)
2019-05-28 02:50:48 +02:00
char sortnames [ fileCnt ] [ SORTED_LONGNAME_STORAGE ] ;
2017-02-09 14:05:34 +01:00
# endif
// Folder sorting needs 1 bit per entry for flags.
# if HAS_FOLDER_SORTING
# if ENABLED(SDSORT_DYNAMIC_RAM)
isDir = new uint8_t [ ( fileCnt + 7 ) > > 3 ] ;
# elif ENABLED(SDSORT_USES_STACK)
2017-02-09 14:02:25 +01:00
uint8_t isDir [ ( fileCnt + 7 ) > > 3 ] ;
# endif
# endif
# else // !SDSORT_USES_RAM
// By default re-read the names from SD for every compare
// retaining only two filenames at a time. This is very
// slow but is safest and uses minimal RAM.
2018-11-20 12:36:19 +01:00
char name1 [ LONG_FILENAME_LENGTH ] ;
2017-02-09 14:02:25 +01:00
# endif
if ( fileCnt > 1 ) {
// Init sort order.
for ( uint16_t i = 0 ; i < fileCnt ; i + + ) {
2019-04-03 05:24:23 +02:00
sort_order [ i ] = (
# if ENABLED(SDCARD_RATHERRECENTFIRST)
fileCnt - 1 -
# endif
i ) ;
2017-02-09 14:02:25 +01:00
// If using RAM then read all filenames now.
# if ENABLED(SDSORT_USES_RAM)
getfilename ( i ) ;
2018-11-17 05:39:16 +01:00
SET_SORTNAME ( i ) ;
SET_SORTSHORT ( i ) ;
2017-02-09 14:02:25 +01:00
// char out[30];
2018-11-17 05:39:16 +01:00
// sprintf_P(out, PSTR("---- %i %s %s"), i, flag.filenameIsDir ? "D" : " ", sortnames[i]);
2017-02-09 14:02:25 +01:00
// SERIAL_ECHOLN(out);
# if HAS_FOLDER_SORTING
const uint16_t bit = i & 0x07 , ind = i > > 3 ;
if ( bit = = 0 ) isDir [ ind ] = 0x00 ;
2018-11-17 05:39:16 +01:00
if ( flag . filenameIsDir ) isDir [ ind ] | = _BV ( bit ) ;
2017-02-09 14:02:25 +01:00
# endif
# endif
}
// Bubble Sort
for ( uint16_t i = fileCnt ; - - i ; ) {
bool didSwap = false ;
for ( uint16_t j = 0 ; j < i ; + + j ) {
const uint16_t o1 = sort_order [ j ] , o2 = sort_order [ j + 1 ] ;
// Compare names from the array or just the two buffered names
# if ENABLED(SDSORT_USES_RAM)
# define _SORT_CMP_NODIR() (strcasecmp(sortnames[o1], sortnames[o2]) > 0)
# else
# define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0)
# endif
# if HAS_FOLDER_SORTING
# if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
const uint8_t ind1 = o1 > > 3 , bit1 = o1 & 0x07 ,
ind2 = o2 > > 3 , bit2 = o2 & 0x07 ;
# define _SORT_CMP_DIR(fs) \
( ( ( isDir [ ind1 ] & _BV ( bit1 ) ) ! = 0 ) = = ( ( isDir [ ind2 ] & _BV ( bit2 ) ) ! = 0 ) \
? _SORT_CMP_NODIR ( ) \
: ( isDir [ fs > 0 ? ind1 : ind2 ] & ( fs > 0 ? _BV ( bit1 ) : _BV ( bit2 ) ) ) ! = 0 )
# else
2018-11-17 05:39:16 +01:00
# define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
2017-02-09 14:02:25 +01:00
# endif
# endif
// The most economical method reads names as-needed
// throughout the loop. Slow if there are many.
# if DISABLED(SDSORT_USES_RAM)
getfilename ( o1 ) ;
2018-07-13 08:44:27 +02:00
strcpy ( name1 , longest_filename ( ) ) ; // save (or getfilename below will trounce it)
2017-02-09 14:02:25 +01:00
# if HAS_FOLDER_SORTING
2018-11-17 05:39:16 +01:00
bool dir1 = flag . filenameIsDir ;
2017-02-09 14:02:25 +01:00
# endif
getfilename ( o2 ) ;
2018-07-13 08:44:27 +02:00
char * name2 = longest_filename ( ) ; // use the string in-place
2017-02-09 14:02:25 +01:00
# endif // !SDSORT_USES_RAM
// Sort the current pair according to settings.
if (
# if HAS_FOLDER_SORTING
# if ENABLED(SDSORT_GCODE)
sort_folders ? _SORT_CMP_DIR ( sort_folders ) : _SORT_CMP_NODIR ( )
# else
_SORT_CMP_DIR ( FOLDER_SORTING )
# endif
# else
_SORT_CMP_NODIR ( )
# endif
) {
sort_order [ j ] = o2 ;
sort_order [ j + 1 ] = o1 ;
didSwap = true ;
}
}
if ( ! didSwap ) break ;
}
2017-02-09 14:05:34 +01:00
// Using RAM but not keeping names around
# if ENABLED(SDSORT_USES_RAM) && DISABLED(SDSORT_CACHE_NAMES)
# if ENABLED(SDSORT_DYNAMIC_RAM)
for ( uint16_t i = 0 ; i < fileCnt ; + + i ) free ( sortnames [ i ] ) ;
# if HAS_FOLDER_SORTING
free ( isDir ) ;
# endif
# endif
# endif
2017-02-09 14:02:25 +01:00
}
else {
sort_order [ 0 ] = 0 ;
2019-03-17 05:43:06 +01:00
# if BOTH(SDSORT_USES_RAM, SDSORT_CACHE_NAMES)
2017-02-09 14:05:34 +01:00
# if ENABLED(SDSORT_DYNAMIC_RAM)
sortnames = new char * [ 1 ] ;
2019-03-17 05:43:06 +01:00
sortshort = new char * [ 1 ] ;
2017-02-09 14:05:34 +01:00
isDir = new uint8_t [ 1 ] ;
# endif
2018-11-17 05:39:16 +01:00
getfilename ( 0 ) ;
SET_SORTNAME ( 0 ) ;
SET_SORTSHORT ( 0 ) ;
isDir [ 0 ] = flag . filenameIsDir ? 0x01 : 0x00 ;
2017-02-09 14:02:25 +01:00
# endif
}
sort_count = fileCnt ;
}
}
void CardReader : : flush_presort ( ) {
2017-02-09 14:05:34 +01:00
if ( sort_count > 0 ) {
# if ENABLED(SDSORT_DYNAMIC_RAM)
delete sort_order ;
# if ENABLED(SDSORT_CACHE_NAMES)
for ( uint8_t i = 0 ; i < sort_count ; + + i ) {
free ( sortshort [ i ] ) ; // strdup
free ( sortnames [ i ] ) ; // strdup
}
delete sortshort ;
delete sortnames ;
# endif
# endif
sort_count = 0 ;
}
2017-02-09 14:02:25 +01:00
}
# endif // SDCARD_SORT_ALPHA
2017-09-06 13:28:32 +02:00
uint16_t CardReader : : get_num_Files ( ) {
return
# if ENABLED(SDCARD_SORT_ALPHA) && SDSORT_USES_RAM && SDSORT_CACHE_NAMES
nrFiles // no need to access the SD card for filenames
# else
2019-09-17 00:49:55 +02:00
countFilesInWorkDir ( )
2017-09-06 13:28:32 +02:00
# endif
;
}
2017-07-30 07:02:41 +02:00
2015-03-02 16:06:01 +01:00
void CardReader : : printingHasFinished ( ) {
2018-05-12 08:38:02 +02:00
planner . synchronize ( ) ;
2016-07-14 18:59:42 +02:00
file . close ( ) ;
2015-03-02 16:06:01 +01:00
if ( file_subcall_ctr > 0 ) { // Heading up to a parent file that called current as a procedure.
file_subcall_ctr - - ;
2016-03-29 12:15:01 +02:00
openFile ( proc_filenames [ file_subcall_ctr ] , true , true ) ;
2015-03-02 16:06:01 +01:00
setIndex ( filespos [ file_subcall_ctr ] ) ;
startFileprint ( ) ;
}
else {
2018-11-07 03:53:07 +01:00
stopSDPrint ( ) ;
2018-04-22 02:41:26 +02:00
# if ENABLED(POWER_LOSS_RECOVERY)
2018-06-30 05:41:02 +02:00
removeJobRecoveryFile ( ) ;
2018-04-22 02:41:26 +02:00
# endif
2017-12-25 03:34:50 +01:00
# if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND)
2018-05-09 07:17:53 +02:00
planner . finish_and_disable ( ) ;
2017-12-25 03:34:50 +01:00
# endif
2018-11-29 02:28:31 +01:00
2016-07-14 18:59:42 +02:00
print_job_timer . stop ( ) ;
2019-07-17 10:12:39 +02:00
queue . enqueue_now_P ( print_job_timer . duration ( ) > 60 ? PSTR ( " M31 " ) : PSTR ( " M117 " ) ) ;
2018-11-29 02:28:31 +01:00
2017-02-09 14:02:25 +01:00
# if ENABLED(SDCARD_SORT_ALPHA)
presort ( ) ;
# endif
2018-11-29 02:28:31 +01:00
2019-07-17 10:12:39 +02:00
# if ENABLED(LCD_SET_PROGRESS_MANUALLY)
ui . set_progress_done ( ) ;
2018-04-07 03:13:19 +02:00
# endif
2018-11-29 02:28:31 +01:00
2017-10-26 22:50:18 +02:00
# if ENABLED(SD_REPRINT_LAST_SELECTED_FILE)
2018-11-11 19:16:24 +01:00
ui . reselect_last_file ( ) ;
2017-10-26 22:50:18 +02:00
# endif
2015-03-02 16:06:01 +01:00
}
2011-11-26 11:51:38 +01:00
}
2015-03-02 16:06:01 +01:00
2018-02-26 22:38:27 +01:00
# if ENABLED(AUTO_REPORT_SD_STATUS)
uint8_t CardReader : : auto_report_sd_interval = 0 ;
millis_t CardReader : : next_sd_report_ms ;
# if NUM_SERIAL > 1
2019-02-24 05:53:01 +01:00
int8_t CardReader : : auto_report_port ;
2018-02-26 22:38:27 +01:00
# endif
void CardReader : : auto_report_sd_status ( ) {
millis_t current_ms = millis ( ) ;
if ( auto_report_sd_interval & & ELAPSED ( current_ms , next_sd_report_ms ) ) {
next_sd_report_ms = current_ms + 1000UL * auto_report_sd_interval ;
2019-02-24 05:53:01 +01:00
PORT_REDIRECT ( auto_report_port ) ;
report_status ( ) ;
2018-02-26 22:38:27 +01:00
}
}
# endif // AUTO_REPORT_SD_STATUS
2018-04-22 02:41:26 +02:00
# if ENABLED(POWER_LOSS_RECOVERY)
2018-11-17 03:47:07 +01:00
bool CardReader : : jobRecoverFileExists ( ) {
2019-08-02 09:22:09 +02:00
const bool exists = recovery . file . open ( & root , recovery . filename , O_READ ) ;
2018-11-17 03:47:07 +01:00
if ( exists ) recovery . file . close ( ) ;
return exists ;
}
2018-04-22 02:41:26 +02:00
void CardReader : : openJobRecoveryFile ( const bool read ) {
2019-09-15 10:10:59 +02:00
if ( ! isMounted ( ) ) return ;
2018-11-17 03:47:07 +01:00
if ( recovery . file . isOpen ( ) ) return ;
2019-08-02 09:22:09 +02:00
if ( ! recovery . file . open ( & root , recovery . filename , read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC ) )
SERIAL_ECHOLNPAIR ( MSG_SD_OPEN_FILE_FAIL , recovery . filename , " . " ) ;
2018-07-03 06:21:28 +02:00
else if ( ! read )
2019-08-02 09:22:09 +02:00
SERIAL_ECHOLNPAIR ( MSG_SD_WRITE_TO_FILE , recovery . filename ) ;
2018-04-22 02:41:26 +02:00
}
2018-11-17 03:47:07 +01:00
// Removing the job recovery file currently requires closing
// the file being printed, so during SD printing the file should
// be zeroed and written instead of deleted.
2018-04-22 02:41:26 +02:00
void CardReader : : removeJobRecoveryFile ( ) {
2018-07-03 06:21:28 +02:00
if ( jobRecoverFileExists ( ) ) {
2019-04-16 01:53:39 +02:00
recovery . init ( ) ;
2019-08-02 09:22:09 +02:00
removeFile ( recovery . filename ) ;
2018-07-03 06:21:28 +02:00
# if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
2018-11-29 23:58:58 +01:00
SERIAL_ECHOPGM ( " Power-loss file delete " ) ;
2018-07-03 06:21:28 +02:00
serialprintPGM ( jobRecoverFileExists ( ) ? PSTR ( " failed. \n " ) : PSTR ( " d. \n " ) ) ;
# endif
}
2018-04-22 02:41:26 +02:00
}
# endif // POWER_LOSS_RECOVERY
2017-05-09 19:35:43 +02:00
# endif // SDSUPPORT