2016-07-22 13:28:01 +00:00
# include "Marlin.h"
2020-10-26 07:42:26 +00:00
# include "cmdqueue.h"
2016-07-22 13:28:01 +00:00
# include "cardreader.h"
# include "ultralcd.h"
2021-06-12 13:09:03 +00:00
# include "conv2str.h"
# include "menu.h"
2016-07-22 13:28:01 +00:00
# include "stepper.h"
# include "temperature.h"
# include "language.h"
# ifdef SDSUPPORT
2017-12-10 10:08:50 +00:00
# define LONGEST_FILENAME (longFilename[0] ? longFilename : filename)
2016-07-22 13:28:01 +00:00
CardReader : : CardReader ( )
{
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
sort_count = 0 ;
# endif
2016-07-22 13:28:01 +00:00
filesize = 0 ;
sdpos = 0 ;
sdprinting = false ;
cardOK = false ;
saving = false ;
logging = false ;
workDirDepth = 0 ;
file_subcall_ctr = 0 ;
memset ( workDirParents , 0 , sizeof ( workDirParents ) ) ;
2021-02-06 12:59:11 +00:00
presort_flag = false ;
2016-07-22 13:28:01 +00:00
lastnr = 0 ;
//power to SD reader
# if SDPOWER > -1
SET_OUTPUT ( SDPOWER ) ;
WRITE ( SDPOWER , HIGH ) ;
# endif //SDPOWER
2021-08-26 20:49:19 +00:00
autostart_atmillis . start ( ) ; // reset timer
2016-07-22 13:28:01 +00:00
}
char * createFilename ( char * buffer , const dir_t & p ) //buffer>12characters
{
char * pos = buffer ;
for ( uint8_t i = 0 ; i < 11 ; i + + )
{
if ( p . name [ i ] = = ' ' ) continue ;
if ( i = = 8 )
{
* pos + + = ' . ' ;
}
* pos + + = p . name [ i ] ;
}
* pos + + = 0 ;
return buffer ;
}
2017-12-10 10:08:50 +00:00
/**
+ * Dive into a folder and recurse depth - first to perform a pre - set operation lsAction :
2021-01-22 09:13:44 +00:00
+ * LS_Count - Add + 1 to nrFiles for every file within the parent
+ * LS_GetFilename - Get the filename of the file indexed by nrFiles
+ * LS_SerialPrint - Print the full path and size of each file to serial output
2017-12-10 10:08:50 +00:00
+ */
2021-04-20 04:50:37 +00:00
void CardReader : : lsDive ( const char * prepend , SdFile parent , const char * const match /*=NULL*/ , LsAction lsAction , ls_param lsParams ) {
2021-02-06 12:59:11 +00:00
static uint8_t recursionCnt = 0 ;
2021-02-09 13:00:46 +00:00
// RAII incrementer for the recursionCnt
class _incrementer
{
public :
_incrementer ( ) { recursionCnt + + ; }
~ _incrementer ( ) { recursionCnt - - ; }
} recursionCntIncrementer ;
2017-12-10 10:08:50 +00:00
dir_t p ;
uint8_t cnt = 0 ;
// Read the next entry from a directory
2021-02-09 18:29:06 +00:00
for ( position = parent . curPosition ( ) ; parent . readDir ( p , longFilename ) > 0 ; position = parent . curPosition ( ) ) {
2021-02-09 13:00:46 +00:00
if ( recursionCnt > MAX_DIR_DEPTH )
2021-02-06 12:59:11 +00:00
return ;
2021-04-19 10:31:30 +00:00
uint8_t pn0 = p . name [ 0 ] ;
if ( pn0 = = DIR_NAME_FREE ) break ;
if ( pn0 = = DIR_NAME_DELETED | | pn0 = = ' . ' ) continue ;
if ( longFilename [ 0 ] = = ' . ' ) continue ;
if ( ! DIR_IS_FILE_OR_SUBDIR ( & p ) | | ( p . attributes & DIR_ATT_HIDDEN ) ) continue ;
2021-04-19 11:48:50 +00:00
if ( DIR_IS_SUBDIR ( & p ) & & lsAction = = LS_SerialPrint ) { // If the entry is a directory and the action is LS_SerialPrint
2017-12-10 10:08:50 +00:00
// Get the short name for the item, which we know is a folder
char lfilename [ FILENAME_LENGTH ] ;
createFilename ( lfilename , p ) ;
// Allocate enough stack space for the full path to a folder, trailing slash, and nul
bool prepend_is_empty = ( prepend [ 0 ] = = ' \0 ' ) ;
int len = ( prepend_is_empty ? 1 : strlen ( prepend ) ) + strlen ( lfilename ) + 1 + 1 ;
char path [ len ] ;
// 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.
strcpy ( path , prepend_is_empty ? " / " : prepend ) ; // root slash if prepend is empty
strcat ( path , lfilename ) ; // FILENAME_LENGTH-1 characters maximum
strcat ( path , " / " ) ; // 1 character
// Serial.print(path);
// Get a new directory object using the full path
// and dive recursively into it.
2021-01-22 09:13:44 +00:00
2021-04-20 04:50:37 +00:00
if ( lsParams . LFN )
2021-01-22 09:13:44 +00:00
printf_P ( PSTR ( " DIR_ENTER: %s \" %s \" \n " ) , path , longFilename [ 0 ] ? longFilename : lfilename ) ;
2017-12-10 10:08:50 +00:00
SdFile dir ;
if ( ! dir . open ( parent , lfilename , O_READ ) ) {
2021-04-19 11:48:50 +00:00
//SERIAL_ECHO_START();
//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
//SERIAL_ECHOLN(lfilename);
2017-12-10 10:08:50 +00:00
}
2021-04-19 11:48:50 +00:00
lsDive ( path , dir , NULL , lsAction , lsParams ) ;
2017-12-10 10:08:50 +00:00
// close() is done automatically by destructor of SdFile
2021-01-22 09:13:44 +00:00
2021-04-20 04:50:37 +00:00
if ( lsParams . LFN )
2021-01-22 09:13:44 +00:00
puts_P ( PSTR ( " DIR_EXIT " ) ) ;
2017-12-10 10:08:50 +00:00
}
else {
filenameIsDir = DIR_IS_SUBDIR ( & p ) ;
if ( ! filenameIsDir & & ( p . name [ 8 ] ! = ' G ' | | p . name [ 9 ] = = ' ~ ' ) ) continue ;
switch ( lsAction ) {
case LS_Count :
nrFiles + + ;
break ;
case LS_SerialPrint :
createFilename ( filename , p ) ;
SERIAL_PROTOCOL ( prepend ) ;
SERIAL_PROTOCOL ( filename ) ;
2021-04-19 10:54:43 +00:00
2017-12-11 10:30:49 +00:00
MYSERIAL . write ( ' ' ) ;
2021-04-19 10:54:43 +00:00
SERIAL_PROTOCOL ( p . fileSize ) ;
2021-01-22 09:13:44 +00:00
2021-04-20 04:50:37 +00:00
if ( lsParams . timestamp )
2021-04-19 11:48:50 +00:00
{
crmodDate = p . lastWriteDate ;
crmodTime = p . lastWriteTime ;
if ( crmodDate < p . creationDate | | ( crmodDate = = p . creationDate & & crmodTime < p . creationTime ) ) {
crmodDate = p . creationDate ;
crmodTime = p . creationTime ;
}
printf_P ( PSTR ( " %#lx " ) , ( ( uint32_t ) crmodDate < < 16 ) | crmodTime ) ;
}
2021-04-20 04:50:37 +00:00
if ( lsParams . LFN )
2021-04-19 10:54:43 +00:00
printf_P ( PSTR ( " \" %s \" " ) , LONGEST_FILENAME ) ;
2021-01-22 09:13:44 +00:00
2021-04-19 10:54:43 +00:00
SERIAL_PROTOCOLLN ( ) ;
2021-01-25 09:46:51 +00:00
manage_heater ( ) ;
2017-12-10 10:08:50 +00:00
break ;
case LS_GetFilename :
2021-02-09 18:29:06 +00:00
//SERIAL_ECHOPGM("File: ");
2017-12-10 10:08:50 +00:00
createFilename ( filename , p ) ;
2021-02-09 18:29:06 +00:00
// cluster = parent.curCluster();
// position = parent.curPosition();
2017-12-10 10:08:50 +00:00
/*MYSERIAL.println(filename);
SERIAL_ECHOPGM ( " Write date: " ) ;
writeDate = p . lastWriteDate ;
MYSERIAL . println ( writeDate ) ;
writeTime = p . lastWriteTime ;
SERIAL_ECHOPGM ( " Creation date: " ) ;
MYSERIAL . println ( p . creationDate ) ;
SERIAL_ECHOPGM ( " Access date: " ) ;
MYSERIAL . println ( p . lastAccessDate ) ;
SERIAL_ECHOLNPGM ( " " ) ; */
2020-02-14 08:09:15 +00:00
crmodDate = p . lastWriteDate ;
crmodTime = p . lastWriteTime ;
// There are scenarios when simple modification time is not enough (on MS Windows)
// For example - extract an old g-code from an archive onto the SD card.
// In such case the creation time is current time (which is correct), but the modification time
// stays the same - i.e. old.
// Therefore let's pick the most recent timestamp from both creation and modification timestamps
if ( crmodDate < p . creationDate | | ( crmodDate = = p . creationDate & & crmodTime < p . creationTime ) ) {
crmodDate = p . creationDate ;
crmodTime = p . creationTime ;
}
2017-12-10 10:08:50 +00:00
//writeDate = p.lastAccessDate;
if ( match ! = NULL ) {
if ( strcasecmp ( match , filename ) = = 0 ) return ;
}
else if ( cnt = = nrFiles ) return ;
cnt + + ;
break ;
}
}
} // while readDir
2016-07-22 13:28:01 +00:00
}
2021-04-20 04:50:37 +00:00
void CardReader : : ls ( ls_param params )
2016-07-22 13:28:01 +00:00
{
root . rewind ( ) ;
2021-04-20 04:50:37 +00:00
lsDive ( " " , root , NULL , LS_SerialPrint , params ) ;
2016-07-22 13:28:01 +00:00
}
2021-06-21 05:30:47 +00:00
void CardReader : : initsd ( bool doPresort /* = true*/ )
2016-07-22 13:28:01 +00:00
{
cardOK = false ;
if ( root . isOpen ( ) )
root . close ( ) ;
# ifdef SDSLOW
2021-05-30 11:12:19 +00:00
if ( ! card . init ( SPI_HALF_SPEED )
2016-07-22 13:28:01 +00:00
)
# else
2021-05-30 11:12:19 +00:00
if ( ! card . init ( SPI_FULL_SPEED )
2016-07-22 13:28:01 +00:00
)
# endif
{
SERIAL_ECHO_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ECHOLNRPGM ( _n ( " SD init fail " ) ) ; ////MSG_SD_INIT_FAIL
2016-07-22 13:28:01 +00:00
}
else if ( ! volume . init ( & card ) )
{
SERIAL_ERROR_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORLNRPGM ( _n ( " volume.init failed " ) ) ; ////MSG_SD_VOL_INIT_FAIL
2016-07-22 13:28:01 +00:00
}
else if ( ! root . openRoot ( & volume ) )
{
SERIAL_ERROR_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORLNRPGM ( _n ( " openRoot failed " ) ) ; ////MSG_SD_OPENROOT_FAIL
2016-07-22 13:28:01 +00:00
}
else
{
cardOK = true ;
SERIAL_ECHO_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ECHOLNRPGM ( _n ( " SD card ok " ) ) ; ////MSG_SD_CARD_OK
2016-07-22 13:28:01 +00:00
}
workDir = root ;
curDir = & root ;
2019-09-29 18:09:11 +00:00
workDirDepth = 0 ;
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
2021-06-21 05:30:47 +00:00
if ( doPresort )
presort ( ) ;
2017-12-10 10:08:50 +00:00
# endif
2016-07-22 13:28:01 +00:00
/*
if ( ! workDir . openRoot ( & volume ) )
{
2018-11-01 13:07:16 +00:00
SERIAL_ECHOLNPGM ( MSG_SD_WORKDIR_FAIL ) ;
2016-07-22 13:28:01 +00:00
}
*/
}
2021-02-06 12:59:11 +00:00
void CardReader : : setroot ( bool doPresort )
2016-07-22 13:28:01 +00:00
{
workDir = root ;
2021-02-06 12:59:11 +00:00
workDirDepth = 0 ;
2016-07-22 13:28:01 +00:00
curDir = & workDir ;
2021-02-06 12:59:11 +00:00
# ifdef SDCARD_SORT_ALPHA
if ( doPresort )
presort ( ) ;
else
presort_flag = true ;
# endif
2016-07-22 13:28:01 +00:00
}
void CardReader : : release ( )
{
sdprinting = false ;
cardOK = false ;
2020-08-28 14:32:07 +00:00
SERIAL_ECHO_START ;
SERIAL_ECHOLNRPGM ( _n ( " SD card released " ) ) ; ////MSG_SD_CARD_RELEASED
2016-07-22 13:28:01 +00:00
}
void CardReader : : startFileprint ( )
{
if ( cardOK )
{
sdprinting = true ;
2019-10-20 15:34:54 +00:00
Stopped = false ;
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
2017-12-11 10:30:49 +00:00
//flush_presort();
2017-12-10 10:08:50 +00:00
# endif
2016-07-22 13:28:01 +00:00
}
}
2018-07-17 15:31:40 +00:00
void CardReader : : openLogFile ( const char * name )
2016-07-22 13:28:01 +00:00
{
logging = true ;
2021-01-28 08:37:58 +00:00
openFileWrite ( name ) ;
2016-07-22 13:28:01 +00:00
}
2017-11-27 05:20:51 +00:00
void CardReader : : getDirName ( char * name , uint8_t level )
{
workDirParents [ level ] . getFilename ( name ) ;
}
2022-08-26 07:18:04 +00:00
uint8_t CardReader : : getWorkDirDepth ( ) {
2017-11-27 05:20:51 +00:00
return workDirDepth ;
}
2016-07-22 13:28:01 +00:00
void CardReader : : getAbsFilename ( char * t )
{
uint8_t cnt = 0 ;
* t = ' / ' ; t + + ; cnt + + ;
for ( uint8_t i = 0 ; i < workDirDepth ; i + + )
{
workDirParents [ i ] . getFilename ( t ) ; //SDBaseFile.getfilename!
while ( * t ! = 0 & & cnt < MAXPATHNAMELENGTH )
{ t + + ; cnt + + ; } //crawl counter forward.
}
2022-02-15 06:36:49 +00:00
if ( cnt < MAXPATHNAMELENGTH - FILENAME_LENGTH )
2016-07-22 13:28:01 +00:00
file . getFilename ( t ) ;
else
t [ 0 ] = 0 ;
}
2021-02-07 19:51:44 +00:00
void CardReader : : printAbsFilenameFast ( )
{
SERIAL_PROTOCOL ( ' / ' ) ;
for ( uint8_t i = 0 ; i < getWorkDirDepth ( ) ; i + + )
{
SERIAL_PROTOCOL ( dir_names [ i ] ) ;
SERIAL_PROTOCOL ( ' / ' ) ;
}
SERIAL_PROTOCOL ( LONGEST_FILENAME ) ;
}
2018-08-06 18:49:40 +00:00
/**
* @ brief Dive into subfolder
*
* Method sets curDir to point to root , in case fileName is null .
* Method sets curDir to point to workDir , in case fileName path is relative
* ( doesn ' t start with ' / ' )
* Method sets curDir to point to dir , which is specified by absolute path
* specified by fileName . In such case fileName is updated so it points to
* file name without the path .
*
* @ param [ in , out ] fileName
* expects file name including path
* in case of absolute path , file name without path is returned
*/
2021-02-06 12:59:11 +00:00
bool CardReader : : diveSubfolder ( const char * & fileName )
2018-08-06 18:49:40 +00:00
{
curDir = & root ;
2021-02-06 12:59:11 +00:00
if ( ! fileName )
return 1 ;
2018-08-06 18:49:40 +00:00
const char * dirname_start , * dirname_end ;
if ( fileName [ 0 ] = = ' / ' ) // absolute path
{
2021-02-06 12:59:11 +00:00
setroot ( false ) ;
2018-08-06 18:49:40 +00:00
dirname_start = fileName + 1 ;
while ( * dirname_start )
{
dirname_end = strchr ( dirname_start , ' / ' ) ;
//SERIAL_ECHO("start:");SERIAL_ECHOLN((int)(dirname_start-name));
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name));
if ( dirname_end & & dirname_end > dirname_start )
{
const size_t maxLen = 12 ;
char subdirname [ maxLen + 1 ] ;
2018-08-06 18:57:17 +00:00
const size_t len = ( ( static_cast < size_t > ( dirname_end - dirname_start ) ) > maxLen ) ? maxLen : ( dirname_end - dirname_start ) ;
2018-08-06 18:49:40 +00:00
strncpy ( subdirname , dirname_start , len ) ;
2021-01-31 14:42:01 +00:00
subdirname [ len ] = 0 ;
2021-02-06 12:59:11 +00:00
if ( ! chdir ( subdirname , false ) )
return 0 ;
curDir = & workDir ;
2018-08-06 18:49:40 +00:00
dirname_start = dirname_end + 1 ;
}
else // the reminder after all /fsa/fdsa/ is the filename
{
fileName = dirname_start ;
//SERIAL_ECHOLN("remaider");
//SERIAL_ECHOLN(fname);
break ;
}
}
}
else //relative path
{
curDir = & workDir ;
}
2021-02-06 12:59:11 +00:00
return 1 ;
2018-08-06 18:49:40 +00:00
}
2016-07-22 13:28:01 +00:00
2021-01-28 08:37:58 +00:00
static const char ofKill [ ] PROGMEM = " trying to call sub-gcode files with too many levels. " ;
static const char ofSubroutineCallTgt [ ] PROGMEM = " SUBROUTINE CALL target: \" " ;
static const char ofParent [ ] PROGMEM = " \" parent: \" " ;
static const char ofPos [ ] PROGMEM = " \" pos " ;
static const char ofNowDoingFile [ ] PROGMEM = " Now doing file: " ;
static const char ofNowFreshFile [ ] PROGMEM = " Now fresh file: " ;
static const char ofFileOpened [ ] PROGMEM = " File opened: " ;
static const char ofSize [ ] PROGMEM = " Size: " ;
static const char ofFileSelected [ ] PROGMEM = " File selected " ;
2021-02-10 15:08:28 +00:00
static const char ofSDPrinting [ ] PROGMEM = " SD-PRINTING " ;
2021-01-28 08:37:58 +00:00
static const char ofWritingToFile [ ] PROGMEM = " Writing to file: " ;
void CardReader : : openFileReadFilteredGcode ( const char * name , bool replace_current /* = false*/ ) {
2021-01-14 12:21:58 +00:00
if ( ! cardOK )
return ;
if ( file . isOpen ( ) ) { //replacing current file by new file, or subfile call
if ( ! replace_current ) {
if ( ( int ) file_subcall_ctr > ( int ) SD_PROCEDURE_DEPTH - 1 ) {
// SERIAL_ERROR_START;
// SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
// SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
2021-01-28 08:37:58 +00:00
kill ( ofKill , 1 ) ;
2021-01-14 12:21:58 +00:00
return ;
}
SERIAL_ECHO_START ;
2021-01-28 08:37:58 +00:00
SERIAL_ECHORPGM ( ofSubroutineCallTgt ) ;
2021-01-14 12:21:58 +00:00
SERIAL_ECHO ( name ) ;
2021-01-28 08:37:58 +00:00
SERIAL_ECHORPGM ( ofParent ) ;
2021-01-14 12:21:58 +00:00
//store current filename and position
getAbsFilename ( filenames [ file_subcall_ctr ] ) ;
SERIAL_ECHO ( filenames [ file_subcall_ctr ] ) ;
2021-01-28 08:37:58 +00:00
SERIAL_ECHORPGM ( ofPos ) ;
2021-01-14 12:21:58 +00:00
SERIAL_ECHOLN ( sdpos ) ;
filespos [ file_subcall_ctr ] = sdpos ;
file_subcall_ctr + + ;
} else {
SERIAL_ECHO_START ;
2021-01-28 08:37:58 +00:00
SERIAL_ECHORPGM ( ofNowDoingFile ) ;
2021-01-14 12:21:58 +00:00
SERIAL_ECHOLN ( name ) ;
}
file . close ( ) ;
} else { //opening fresh file
file_subcall_ctr = 0 ; //resetting procedure depth in case user cancels print while in procedure
SERIAL_ECHO_START ;
2021-01-28 08:37:58 +00:00
SERIAL_ECHORPGM ( ofNowFreshFile ) ;
2021-01-14 12:21:58 +00:00
SERIAL_ECHOLN ( name ) ;
2016-07-22 13:28:01 +00:00
}
2021-01-14 12:21:58 +00:00
sdprinting = false ;
const char * fname = name ;
2021-02-10 11:15:57 +00:00
if ( ! diveSubfolder ( fname ) )
return ;
2021-01-14 12:21:58 +00:00
if ( file . openFilteredGcode ( curDir , fname ) ) {
2021-02-10 15:08:28 +00:00
getfilename ( 0 , fname ) ;
2021-01-14 12:21:58 +00:00
filesize = file . fileSize ( ) ;
2021-01-28 08:37:58 +00:00
SERIAL_PROTOCOLRPGM ( ofFileOpened ) ; ////MSG_SD_FILE_OPENED
2021-02-10 15:08:28 +00:00
printAbsFilenameFast ( ) ;
2021-01-28 08:37:58 +00:00
SERIAL_PROTOCOLRPGM ( ofSize ) ; ////MSG_SD_SIZE
2021-01-14 12:21:58 +00:00
SERIAL_PROTOCOLLN ( filesize ) ;
sdpos = 0 ;
2021-01-28 08:37:58 +00:00
SERIAL_PROTOCOLLNRPGM ( ofFileSelected ) ; ////MSG_SD_FILE_SELECTED
2021-02-10 15:08:28 +00:00
lcd_setstatuspgm ( ofFileSelected ) ;
scrollstuff = 0 ;
2021-01-14 12:21:58 +00:00
} else {
SERIAL_PROTOCOLRPGM ( MSG_SD_OPEN_FILE_FAIL ) ;
SERIAL_PROTOCOL ( fname ) ;
SERIAL_PROTOCOLLN ( ' . ' ) ;
}
}
2018-08-06 18:49:40 +00:00
2021-01-28 08:37:58 +00:00
void CardReader : : openFileWrite ( const char * name )
2016-07-22 13:28:01 +00:00
{
2021-01-28 08:37:58 +00:00
if ( ! cardOK )
return ;
if ( file . isOpen ( ) ) { //replacing current file by new file, or subfile call
2021-02-10 10:18:59 +00:00
#if 0
// I doubt chained files support is necessary for file saving:
// Intentionally disabled because it takes a lot of code size while being not used
2018-08-06 18:49:40 +00:00
2021-01-28 08:37:58 +00:00
if ( ( int ) file_subcall_ctr > ( int ) SD_PROCEDURE_DEPTH - 1 ) {
// SERIAL_ERROR_START;
// SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
// SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
kill ( ofKill , 1 ) ;
return ;
}
SERIAL_ECHO_START ;
SERIAL_ECHORPGM ( ofSubroutineCallTgt ) ;
SERIAL_ECHO ( name ) ;
SERIAL_ECHORPGM ( ofParent ) ;
//store current filename and position
getAbsFilename ( filenames [ file_subcall_ctr ] ) ;
SERIAL_ECHO ( filenames [ file_subcall_ctr ] ) ;
SERIAL_ECHORPGM ( ofPos ) ;
SERIAL_ECHOLN ( sdpos ) ;
filespos [ file_subcall_ctr ] = sdpos ;
file_subcall_ctr + + ;
file . close ( ) ;
2021-02-10 10:18:59 +00:00
# else
SERIAL_ECHOLNPGM ( " File already opened " ) ;
# endif
2021-01-28 08:37:58 +00:00
} else { //opening fresh file
file_subcall_ctr = 0 ; //resetting procedure depth in case user cancels print while in procedure
SERIAL_ECHO_START ;
SERIAL_ECHORPGM ( ofNowFreshFile ) ;
SERIAL_ECHOLN ( name ) ;
2016-07-22 13:28:01 +00:00
}
2021-01-28 08:37:58 +00:00
sdprinting = false ;
const char * fname = name ;
2021-02-10 11:23:02 +00:00
if ( ! diveSubfolder ( fname ) )
return ;
2021-01-28 08:37:58 +00:00
//write
if ( ! file . open ( curDir , fname , O_CREAT | O_APPEND | O_WRITE | O_TRUNC ) ) {
SERIAL_PROTOCOLRPGM ( MSG_SD_OPEN_FILE_FAIL ) ;
SERIAL_PROTOCOL ( fname ) ;
SERIAL_PROTOCOLLN ( ' . ' ) ;
} else {
saving = true ;
2021-02-10 15:08:28 +00:00
getfilename ( 0 , fname ) ;
2021-01-28 08:37:58 +00:00
SERIAL_PROTOCOLRPGM ( ofWritingToFile ) ; ////MSG_SD_WRITE_TO_FILE
2021-02-10 15:08:28 +00:00
printAbsFilenameFast ( ) ;
SERIAL_PROTOCOLLN ( ) ;
SERIAL_PROTOCOLLNRPGM ( ofFileSelected ) ; ////MSG_SD_FILE_SELECTED
lcd_setstatuspgm ( ofFileSelected ) ;
scrollstuff = 0 ;
2016-07-22 13:28:01 +00:00
}
}
2018-07-17 15:31:40 +00:00
void CardReader : : removeFile ( const char * name )
2016-07-22 13:28:01 +00:00
{
2018-08-06 18:49:40 +00:00
if ( ! cardOK ) return ;
file . close ( ) ;
sdprinting = false ;
const char * fname = name ;
2021-02-06 12:59:11 +00:00
if ( ! diveSubfolder ( fname ) )
return ;
2018-08-06 18:49:40 +00:00
2016-07-22 13:28:01 +00:00
if ( file . remove ( curDir , fname ) )
{
SERIAL_PROTOCOLPGM ( " File deleted: " ) ;
SERIAL_PROTOCOLLN ( fname ) ;
sdpos = 0 ;
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
presort ( ) ;
# endif
2016-07-22 13:28:01 +00:00
}
else
{
SERIAL_PROTOCOLPGM ( " Deletion failed, File: " ) ;
SERIAL_PROTOCOL ( fname ) ;
2021-01-26 07:24:18 +00:00
SERIAL_PROTOCOLLN ( ' . ' ) ;
2016-07-22 13:28:01 +00:00
}
}
2017-12-06 13:55:53 +00:00
uint32_t CardReader : : getFileSize ( )
{
return filesize ;
}
2021-02-01 12:54:37 +00:00
void CardReader : : getStatus ( bool arg_P )
2016-07-22 13:28:01 +00:00
{
2021-01-31 13:06:20 +00:00
if ( isPrintPaused )
{
if ( saved_printing & & ( saved_printing_type = = PRINTING_TYPE_SD ) )
SERIAL_PROTOCOLLNPGM ( " SD print paused " ) ;
else
SERIAL_PROTOCOLLNPGM ( " Print saved " ) ;
}
else if ( sdprinting )
{
2021-02-01 12:54:37 +00:00
if ( arg_P )
2021-01-31 13:06:20 +00:00
{
2021-02-07 19:51:44 +00:00
printAbsFilenameFast ( ) ;
SERIAL_PROTOCOLLN ( ) ;
2021-01-31 13:06:20 +00:00
}
else
SERIAL_PROTOCOLLN ( LONGEST_FILENAME ) ;
SERIAL_PROTOCOLRPGM ( _N ( " SD printing byte " ) ) ; ////MSG_SD_PRINTING_BYTE
SERIAL_PROTOCOL ( sdpos ) ;
SERIAL_PROTOCOL ( ' / ' ) ;
SERIAL_PROTOCOLLN ( filesize ) ;
uint16_t time = ( _millis ( ) - starttime ) / 60000U ;
SERIAL_PROTOCOL ( itostr2 ( time / 60 ) ) ;
SERIAL_PROTOCOL ( ' : ' ) ;
SERIAL_PROTOCOLLN ( itostr2 ( time % 60 ) ) ;
}
else
SERIAL_PROTOCOLLNPGM ( " Not SD printing " ) ;
2016-07-22 13:28:01 +00:00
}
void CardReader : : write_command ( char * buf )
{
file . writeError = false ;
2021-06-22 09:28:37 +00:00
file . write ( buf ) ; //write command
file . write ( " \r \n " ) ; //write line termination
2016-07-22 13:28:01 +00:00
if ( file . writeError )
{
SERIAL_ERROR_START ;
2018-11-22 16:57:52 +00:00
SERIAL_ERRORLNRPGM ( MSG_SD_ERR_WRITE_TO_FILE ) ;
2016-07-22 13:28:01 +00:00
}
}
2017-03-24 18:47:50 +00:00
# define CHUNK_SIZE 64
void CardReader : : write_command_no_newline ( char * buf )
{
file . write ( buf , CHUNK_SIZE ) ;
if ( file . writeError )
{
SERIAL_ERROR_START ;
2018-11-22 16:57:52 +00:00
SERIAL_ERRORLNRPGM ( MSG_SD_ERR_WRITE_TO_FILE ) ;
2021-01-26 07:24:18 +00:00
SERIAL_PROTOCOLLNPGM ( " An error while writing to the SD Card. " ) ;
2017-03-24 18:47:50 +00:00
}
}
2016-07-22 13:28:01 +00:00
void CardReader : : checkautostart ( bool force )
{
2022-04-09 11:10:55 +00:00
// The SD start is delayed because otherwise the serial cannot answer
// fast enough to make contact with the host software.
static bool autostart_stilltocheck = true ;
2016-07-22 13:28:01 +00:00
if ( ! force )
{
if ( ! autostart_stilltocheck )
return ;
2021-08-26 20:49:19 +00:00
if ( autostart_atmillis . expired ( 5000 ) )
2016-07-22 13:28:01 +00:00
return ;
}
2022-04-09 11:10:55 +00:00
autostart_stilltocheck = false ;
2016-07-22 13:28:01 +00:00
if ( ! cardOK )
{
initsd ( ) ;
if ( ! cardOK ) //fail
return ;
}
char autoname [ 30 ] ;
sprintf_P ( autoname , PSTR ( " auto%i.g " ) , lastnr ) ;
for ( int8_t i = 0 ; i < ( int8_t ) strlen ( autoname ) ; i + + )
autoname [ i ] = tolower ( autoname [ i ] ) ;
dir_t p ;
root . rewind ( ) ;
bool found = false ;
while ( root . readDir ( p , NULL ) > 0 )
{
for ( int8_t i = 0 ; i < ( int8_t ) strlen ( ( char * ) p . name ) ; i + + )
p . name [ i ] = tolower ( p . name [ i ] ) ;
//Serial.print((char*)p.name);
//Serial.print(" ");
//Serial.println(autoname);
if ( p . name [ 9 ] ! = ' ~ ' ) //skip safety copies
if ( strncmp ( ( char * ) p . name , autoname , 5 ) = = 0 )
{
char cmd [ 30 ] ;
// M23: Select SD file
sprintf_P ( cmd , PSTR ( " M23 %s " ) , autoname ) ;
enquecommand ( cmd ) ;
// M24: Start/resume SD print
enquecommand_P ( PSTR ( " M24 " ) ) ;
found = true ;
}
}
if ( ! found )
lastnr = - 1 ;
else
lastnr + + ;
}
void CardReader : : closefile ( bool store_location )
{
file . sync ( ) ;
file . close ( ) ;
saving = false ;
logging = false ;
if ( store_location )
{
//future: store printer state, filename and position for continuing a stopped print
// so one can unplug the printer and continue printing the next day.
}
}
void CardReader : : getfilename ( uint16_t nr , const char * const match /*=NULL*/ )
{
curDir = & workDir ;
nrFiles = nr ;
curDir - > rewind ( ) ;
2021-04-19 11:48:50 +00:00
lsDive ( " " , * curDir , match , LS_GetFilename ) ;
2016-07-22 13:28:01 +00:00
}
2022-02-04 09:47:56 +00:00
void CardReader : : getfilename_simple ( uint16_t entry , const char * const match /*=NULL*/ )
2017-12-11 10:30:49 +00:00
{
curDir = & workDir ;
nrFiles = 0 ;
2022-02-04 09:47:56 +00:00
curDir - > seekSet ( ( uint32_t ) entry < < 5 ) ;
2021-04-19 11:48:50 +00:00
lsDive ( " " , * curDir , match , LS_GetFilename ) ;
2017-12-11 10:30:49 +00:00
}
2021-02-09 18:31:02 +00:00
void CardReader : : getfilename_next ( uint32_t position , const char * const match /*=NULL*/ )
{
curDir = & workDir ;
nrFiles = 1 ;
curDir - > seekSet ( position ) ;
2021-04-19 11:48:50 +00:00
lsDive ( " " , * curDir , match , LS_GetFilename ) ;
2021-02-09 18:31:02 +00:00
}
2016-07-22 13:28:01 +00:00
uint16_t CardReader : : getnrfilenames ( )
{
curDir = & workDir ;
nrFiles = 0 ;
curDir - > rewind ( ) ;
2021-04-19 11:48:50 +00:00
lsDive ( " " , * curDir , NULL , LS_Count ) ;
2016-07-22 13:28:01 +00:00
//SERIAL_ECHOLN(nrFiles);
return nrFiles ;
}
2021-02-06 12:59:11 +00:00
bool CardReader : : chdir ( const char * relpath , bool doPresort )
2016-07-22 13:28:01 +00:00
{
SdFile newfile ;
SdFile * parent = & root ;
if ( workDir . isOpen ( ) )
parent = & workDir ;
2021-02-04 15:52:42 +00:00
if ( ! newfile . open ( * parent , relpath , O_READ ) | | ( ( workDirDepth + 1 ) > = MAX_DIR_DEPTH ) )
2016-07-22 13:28:01 +00:00
{
SERIAL_ECHO_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ECHORPGM ( _n ( " Cannot enter subdir: " ) ) ; ////MSG_SD_CANT_ENTER_SUBDIR
2016-07-22 13:28:01 +00:00
SERIAL_ECHOLN ( relpath ) ;
2021-02-04 15:52:42 +00:00
return 0 ;
2016-07-22 13:28:01 +00:00
}
else
{
2021-02-06 12:59:11 +00:00
strcpy ( dir_names [ workDirDepth ] , relpath ) ;
puts ( relpath ) ;
2016-07-22 13:28:01 +00:00
if ( workDirDepth < MAX_DIR_DEPTH ) {
2022-08-26 07:18:04 +00:00
for ( uint8_t d = + + workDirDepth ; d - - ; )
2016-07-22 13:28:01 +00:00
workDirParents [ d + 1 ] = workDirParents [ d ] ;
workDirParents [ 0 ] = * parent ;
}
workDir = newfile ;
2021-02-06 12:59:11 +00:00
# ifdef SDCARD_SORT_ALPHA
if ( doPresort )
2017-12-11 10:30:49 +00:00
presort ( ) ;
2021-02-06 12:59:11 +00:00
else
presort_flag = true ;
# endif
2021-02-04 15:52:42 +00:00
return 1 ;
2016-07-22 13:28:01 +00:00
}
}
void CardReader : : updir ( )
{
if ( workDirDepth > 0 )
{
- - workDirDepth ;
workDir = workDirParents [ 0 ] ;
2022-08-26 07:18:04 +00:00
for ( uint8_t d = 0 ; d < workDirDepth ; d + + )
2018-07-25 13:51:00 +00:00
{
workDirParents [ d ] = workDirParents [ d + 1 ] ;
}
2017-12-11 10:30:49 +00:00
# ifdef SDCARD_SORT_ALPHA
2018-07-25 13:51:00 +00:00
presort ( ) ;
2017-12-11 10:30:49 +00:00
# endif
2016-07-22 13:28:01 +00:00
}
}
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
/**
* Get the name of a file in the current directory by sort - index
*/
2021-02-26 17:17:14 +00:00
void CardReader : : getfilename_sorted ( const uint16_t nr , uint8_t sdSort ) {
2021-02-03 17:18:13 +00:00
if ( nr < sort_count )
2022-02-04 09:47:56 +00:00
getfilename_simple ( sort_entries [ ( sdSort = = SD_SORT_ALPHA ) ? ( sort_count - nr - 1 ) : nr ] ) ;
2019-12-16 22:44:47 +00:00
else
2022-07-03 07:48:19 +00:00
getfilename_afterMaxSorting ( nr ) ;
}
void CardReader : : getfilename_afterMaxSorting ( uint16_t entry , const char * const match /*=NULL*/ )
{
curDir = & workDir ;
nrFiles = entry - sort_count + 1 ;
curDir - > seekSet ( lastSortedFilePosition < < 5 ) ;
lsDive ( " " , * curDir , match , LS_GetFilename ) ;
2017-12-10 10:08:50 +00: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 ( ) {
2022-07-03 19:26:46 +00:00
// Throw away old sort index
flush_presort ( ) ;
2017-12-11 10:30:49 +00:00
if ( farm_mode | | IS_SD_INSERTED = = false ) return ; //sorting is not used in farm mode
2017-12-10 10:08:50 +00:00
uint8_t sdSort = eeprom_read_byte ( ( uint8_t * ) EEPROM_SD_SORT ) ;
2017-12-11 10:30:49 +00:00
KEEPALIVE_STATE ( IN_HANDLER ) ;
2017-12-10 10:08:50 +00:00
// If there are files, sort up to the limit
uint16_t fileCnt = getnrfilenames ( ) ;
if ( fileCnt > 0 ) {
// Never sort more than the max allowed
// If you use folders to organize, 20 may be enough
2017-12-11 10:30:49 +00:00
if ( fileCnt > SDSORT_LIMIT ) {
2022-07-03 19:26:46 +00:00
if ( sdSort ! = SD_SORT_NONE ) {
lcd_show_fullscreen_message_and_wait_P ( _i ( " Some files will not be sorted. Max. No. of files in 1 folder for sorting is 100. " ) ) ; ////MSG_FILE_CNT c=20 r=6
}
2017-12-11 10:30:49 +00:00
fileCnt = SDSORT_LIMIT ;
}
2017-12-10 10:08:50 +00:00
2022-07-03 19:26:46 +00:00
sort_count = fileCnt ;
// Init sort order.
for ( uint16_t i = 0 ; i < fileCnt ; i + + ) {
if ( ! IS_SD_INSERTED ) return ;
manage_heater ( ) ;
if ( i = = 0 )
getfilename ( 0 ) ;
else
getfilename_next ( position ) ;
sort_entries [ i ] = position > > 5 ;
}
2017-12-10 10:08:50 +00:00
2022-07-03 19:26:46 +00:00
if ( ( fileCnt > 1 ) & & ( sdSort ! = SD_SORT_NONE ) ) {
2017-12-11 10:30:49 +00:00
2022-07-02 19:36:33 +00:00
# ifdef SORTING_SPEEDTEST
LongTimer sortingSpeedtestTimer ;
sortingSpeedtestTimer . start ( ) ;
# endif //SORTING_SPEEDTEST
2022-07-03 07:48:19 +00:00
lastSortedFilePosition = position > > 5 ;
2021-02-26 17:17:14 +00:00
2022-07-03 19:26:46 +00:00
// 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.
char name1 [ LONG_FILENAME_LENGTH ] ;
uint16_t crmod_time_bckp ;
uint16_t crmod_date_bckp ;
2022-07-02 19:36:33 +00:00
# ifdef INSERTSORT
2021-03-08 19:56:51 +00:00
# define _SORT_CMP_NODIR() (strcasecmp(name1, name2) < 0) //true if lowercase(name1) < lowercase(name2)
# define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp > crmodTime)) || (crmod_date_bckp > crmodDate))
# if HAS_FOLDER_SORTING
# define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs < 0 ? dir1 : !dir1))
# define _SORT_CMP_TIME_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_TIME_NODIR() : (fs < 0 ? dir1 : !dir1))
# endif
uint16_t counter = 0 ;
menu_progressbar_init ( fileCnt * fileCnt / 2 , _i ( " Sorting files " ) ) ;
for ( uint16_t i = 1 ; i < fileCnt ; + + i ) {
// if (!IS_SD_INSERTED) return;
menu_progressbar_update ( counter ) ;
counter + = i ;
/// pop the position
2022-07-02 20:16:57 +00:00
const uint16_t o1 = sort_entries [ i ] ;
getfilename_simple ( o1 ) ;
2021-03-08 19:56:51 +00:00
strcpy ( name1 , LONGEST_FILENAME ) ; // save (or getfilename below will trounce it)
crmod_date_bckp = crmodDate ;
crmod_time_bckp = crmodTime ;
# if HAS_FOLDER_SORTING
bool dir1 = filenameIsDir ;
# endif
/// find proper place
uint16_t j = i ;
for ( ; j > 0 ; - - j ) {
if ( ! IS_SD_INSERTED ) return ;
# ifdef SORTING_DUMP
for ( uint16_t z = 0 ; z < fileCnt ; z + + ) {
2022-07-02 20:16:57 +00:00
printf_P ( PSTR ( " %2u " ) , sort_entries [ z ] ) ;
2021-03-08 19:56:51 +00:00
}
MYSERIAL . println ( ) ;
# endif
manage_heater ( ) ;
2022-07-02 20:16:57 +00:00
const uint16_t o2 = sort_entries [ j - 1 ] ;
2021-03-08 19:56:51 +00:00
2022-07-02 20:16:57 +00:00
getfilename_simple ( o2 ) ;
2021-03-08 19:56:51 +00:00
char * name2 = LONGEST_FILENAME ; // use the string in-place
// Sort the current pair according to settings.
if (
# if HAS_FOLDER_SORTING
( sdSort = = SD_SORT_TIME & & _SORT_CMP_TIME_DIR ( FOLDER_SORTING ) ) | | ( sdSort = = SD_SORT_ALPHA & & ! _SORT_CMP_DIR ( FOLDER_SORTING ) )
# else
( sdSort = = SD_SORT_TIME & & _SORT_CMP_TIME_NODIR ( ) ) | | ( sdSort = = SD_SORT_ALPHA & & ! _SORT_CMP_NODIR ( ) )
# endif
)
{
break ;
} else {
# ifdef SORTING_DUMP
puts_P ( PSTR ( " shift " ) ) ;
# endif
2022-07-02 20:16:57 +00:00
sort_entries [ j ] = o2 ;
2021-03-08 19:56:51 +00:00
}
}
/// place the position
2022-07-02 20:16:57 +00:00
sort_entries [ j ] = o1 ;
2021-03-08 19:56:51 +00:00
}
2019-11-26 09:29:57 +00:00
# else //Bubble Sort
2021-02-26 17:17:14 +00:00
# define _SORT_CMP_NODIR() (strcasecmp(name1, name2) < 0) //true if lowercase(name1) < lowercase(name2)
# define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp > crmodTime)) || (crmod_date_bckp > crmodDate))
# if HAS_FOLDER_SORTING
# define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs < 0 ? dir1 : !dir1))
# define _SORT_CMP_TIME_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_TIME_NODIR() : (fs < 0 ? dir1 : !dir1))
# endif
2021-02-25 18:25:32 +00:00
uint16_t counter = 0 ;
menu_progressbar_init ( 0.5 * ( fileCnt - 1 ) * ( fileCnt ) , _i ( " Sorting files " ) ) ;
2017-12-11 10:30:49 +00:00
2017-12-10 10:08:50 +00:00
for ( uint16_t i = fileCnt ; - - i ; ) {
2017-12-11 10:30:49 +00:00
if ( ! IS_SD_INSERTED ) return ;
2017-12-10 10:08:50 +00:00
bool didSwap = false ;
2021-02-25 18:25:32 +00:00
menu_progressbar_update ( counter ) ;
2017-12-10 10:08:50 +00:00
counter + + ;
for ( uint16_t j = 0 ; j < i ; + + j ) {
2017-12-11 10:30:49 +00:00
if ( ! IS_SD_INSERTED ) return ;
2019-11-26 10:18:11 +00:00
# ifdef SORTING_DUMP
for ( uint16_t z = 0 ; z < fileCnt ; z + + )
{
2022-07-02 20:16:57 +00:00
printf_P ( PSTR ( " %2u " ) , sort_entries [ z ] ) ;
2019-11-26 10:18:11 +00:00
}
MYSERIAL . println ( ) ;
# endif
2017-12-11 10:30:49 +00:00
manage_heater ( ) ;
2022-07-02 20:16:57 +00:00
const uint16_t o1 = sort_entries [ j ] , o2 = sort_entries [ j + 1 ] ;
2017-12-10 10:08:50 +00:00
2017-12-11 10:30:49 +00:00
counter + + ;
2022-07-02 20:16:57 +00:00
getfilename_simple ( o1 ) ;
2017-12-10 10:08:50 +00:00
strcpy ( name1 , LONGEST_FILENAME ) ; // save (or getfilename below will trounce it)
2020-02-14 08:09:15 +00:00
crmod_date_bckp = crmodDate ;
crmod_time_bckp = crmodTime ;
2017-12-10 10:08:50 +00:00
# if HAS_FOLDER_SORTING
bool dir1 = filenameIsDir ;
# endif
2022-07-02 20:16:57 +00:00
getfilename_simple ( o2 ) ;
2017-12-10 10:08:50 +00:00
char * name2 = LONGEST_FILENAME ; // use the string in-place
// Sort the current pair according to settings.
if (
# if HAS_FOLDER_SORTING
2021-02-26 17:17:14 +00:00
( sdSort = = SD_SORT_TIME & & _SORT_CMP_TIME_DIR ( FOLDER_SORTING ) ) | | ( sdSort = = SD_SORT_ALPHA & & ! _SORT_CMP_DIR ( FOLDER_SORTING ) )
2017-12-10 10:08:50 +00:00
# else
2021-02-26 17:17:14 +00:00
( sdSort = = SD_SORT_TIME & & _SORT_CMP_TIME_NODIR ( ) ) | | ( sdSort = = SD_SORT_ALPHA & & ! _SORT_CMP_NODIR ( ) )
2017-12-10 10:08:50 +00:00
# endif
)
{
2021-02-26 06:57:49 +00:00
# ifdef SORTING_DUMP
puts_P ( PSTR ( " swap " ) ) ;
# endif
2022-07-02 20:16:57 +00:00
sort_entries [ j ] = o2 ;
sort_entries [ j + 1 ] = o1 ;
2017-12-10 10:08:50 +00:00
didSwap = true ;
}
}
if ( ! didSwap ) break ;
} //end of bubble sort loop
2017-12-11 10:30:49 +00:00
# endif
2022-07-02 19:36:33 +00:00
# ifdef SORTING_SPEEDTEST
printf_P ( PSTR ( " sortingSpeedtestTimer:%lu \n " ) , sortingSpeedtestTimer . elapsed ( ) ) ;
# endif //SORTING_SPEEDTEST
2021-02-26 06:57:49 +00:00
# ifdef SORTING_DUMP
for ( uint16_t z = 0 ; z < fileCnt ; z + + )
2022-07-02 20:16:57 +00:00
printf_P ( PSTR ( " %2u " ) , sort_entries [ z ] ) ;
2021-02-26 06:57:49 +00:00
SERIAL_PROTOCOLLN ( ) ;
# endif
2021-02-25 18:25:32 +00:00
menu_progressbar_finish ( ) ;
2017-12-10 10:08:50 +00:00
}
}
2021-02-04 09:35:15 +00:00
2017-12-11 10:30:49 +00:00
KEEPALIVE_STATE ( NOT_BUSY ) ;
2017-12-10 10:08:50 +00:00
}
void CardReader : : flush_presort ( ) {
2022-07-03 07:20:03 +00:00
sort_count = 0 ;
2022-07-03 07:48:19 +00:00
lastSortedFilePosition = 0 ;
2017-12-10 10:08:50 +00:00
}
# endif // SDCARD_SORT_ALPHA
2016-07-22 13:28:01 +00:00
void CardReader : : printingHasFinished ( )
{
st_synchronize ( ) ;
2022-08-06 21:29:34 +00:00
file . close ( ) ;
2016-07-22 13:28:01 +00:00
if ( file_subcall_ctr > 0 ) //heading up to a parent file that called current as a procedure.
{
file_subcall_ctr - - ;
2021-01-28 08:37:58 +00:00
openFileReadFilteredGcode ( filenames [ file_subcall_ctr ] , true ) ;
2016-07-22 13:28:01 +00:00
setIndex ( filespos [ file_subcall_ctr ] ) ;
startFileprint ( ) ;
}
else
{
sdprinting = false ;
if ( SD_FINISHED_STEPPERRELEASE )
{
2017-12-06 13:55:53 +00:00
finishAndDisableSteppers ( ) ;
//enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND));
2016-07-22 13:28:01 +00:00
}
autotempShutdown ( ) ;
2017-12-10 10:08:50 +00:00
# ifdef SDCARD_SORT_ALPHA
2017-12-12 14:26:48 +00:00
//presort();
2017-12-10 10:08:50 +00:00
# endif
2016-07-22 13:28:01 +00:00
}
}
bool CardReader : : ToshibaFlashAir_GetIP ( uint8_t * ip )
{
memset ( ip , 0 , 4 ) ;
return card . readExtMemory ( 1 , 1 , 0x400 + 0x150 , 4 , ip ) ;
}
# endif //SDSUPPORT