Parcourir la source

SD Card Alpha Sorting

First iteration of alphabetical sorting for SD cards, both
slow+efficient and fast+rammy. Option for folders to sort first, last,
or not at all.
Scott Lahteine il y a 9 ans
Parent
révision
14187dae6c
5 fichiers modifiés avec 208 ajouts et 41 suppressions
  1. 1
    0
      Marlin/Configuration_adv.h
  2. 3
    1
      Marlin/SdFatConfig.h
  3. 163
    24
      Marlin/cardreader.cpp
  4. 23
    5
      Marlin/cardreader.h
  5. 18
    11
      Marlin/ultralcd.cpp

+ 1
- 0
Marlin/Configuration_adv.h Voir le fichier

@@ -290,6 +290,7 @@
290 290
 #define SD_FINISHED_STEPPERRELEASE true  //if sd support and the file is finished: disable steppers?
291 291
 #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place.
292 292
 
293
+#define SDCARD_SORT_ALPHA // Sort in ASCII order by pre-reading the folder and making a lookup table!
293 294
 #define SDCARD_RATHERRECENTFIRST  //reverse file order of sd card menu display. Its sorted practically after the file system block order.
294 295
 // if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that.
295 296
 // using:

+ 3
- 1
Marlin/SdFatConfig.h Voir le fichier

@@ -111,10 +111,12 @@ uint8_t const SOFT_SPI_SCK_PIN = 13;
111 111
 /**
112 112
  * Defines for long (vfat) filenames
113 113
  */
114
+/** Number of UTF-16 characters per entry */
115
+#define FILENAME_LENGTH 13
114 116
 /** Number of VFAT entries used. Every entry has 13 UTF-16 characters */
115 117
 #define MAX_VFAT_ENTRIES (2)
116 118
 /** Total size of the buffer used to store the long filenames */
117
-#define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1)
119
+#define LONG_FILENAME_LENGTH (FILENAME_LENGTH*MAX_VFAT_ENTRIES+1)
118 120
 #endif  // SdFatConfig_h
119 121
 
120 122
 

+ 163
- 24
Marlin/cardreader.cpp Voir le fichier

@@ -11,6 +11,10 @@
11 11
 
12 12
 CardReader::CardReader()
13 13
 {
14
+  #if SORT_USES_MORE_RAM
15
+   sortnames = NULL;
16
+   sort_count = 0;
17
+  #endif
14 18
    filesize = 0;
15 19
    sdpos = 0;
16 20
    sdprinting = false;
@@ -53,15 +57,15 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
53 57
 void  CardReader::lsDive(const char *prepend,SdFile parent)
54 58
 {
55 59
   dir_t p;
56
- uint8_t cnt=0;
60
+  uint8_t cnt=0;
57 61
  
58
-  while (parent.readDir(p, longFilename) > 0)
62
+  while (parent.readDir(p, diveFilename) > 0)
59 63
   {
60 64
     if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint
61 65
     {
62 66
 
63
-      char path[13*2];
64
-      char lfilename[13];
67
+      char path[FILENAME_LENGTH*2];
68
+      char lfilename[FILENAME_LENGTH];
65 69
       createFilename(lfilename,p);
66 70
       
67 71
       path[0]=0;
@@ -87,25 +91,22 @@ void  CardReader::lsDive(const char *prepend,SdFile parent)
87 91
       }
88 92
       lsDive(path,dir);
89 93
       //close done automatically by destructor of SdFile
90
-
91
-      
92 94
     }
93 95
     else
94 96
     {
95 97
       if (p.name[0] == DIR_NAME_FREE) break;
96 98
       if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.'|| p.name[0] == '_') continue;
97
-      if (longFilename[0] != '\0' &&
98
-          (longFilename[0] == '.' || longFilename[0] == '_')) continue;
99
+      if (diveFilename[0] != '\0' &&
100
+          (diveFilename[0] == '.' || diveFilename[0] == '_')) continue;
99 101
       if ( p.name[0] == '.')
100 102
       {
101 103
         if ( p.name[1] != '.')
102 104
         continue;
103 105
       }
104
-      
106
+
105 107
       if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
106 108
       filenameIsDir=DIR_IS_SUBDIR(&p);
107
-      
108
-      
109
+
109 110
       if(!filenameIsDir)
110 111
       {
111 112
         if(p.name[8]!='G') continue;
@@ -124,10 +125,8 @@ void  CardReader::lsDive(const char *prepend,SdFile parent)
124 125
       } 
125 126
       else if(lsAction==LS_GetFilename)
126 127
       {
127
-        if(cnt==nrFiles)
128
-          return;
128
+        if (cnt == nrFiles) return;
129 129
         cnt++;
130
-        
131 130
       }
132 131
     }
133 132
   }
@@ -136,9 +135,6 @@ void  CardReader::lsDive(const char *prepend,SdFile parent)
136 135
 void CardReader::ls() 
137 136
 {
138 137
   lsAction=LS_SerialPrint;
139
-  if(lsAction==LS_Count)
140
-  nrFiles=0;
141
-
142 138
   root.rewind();
143 139
   lsDive("",root);
144 140
 }
@@ -177,6 +173,9 @@ void CardReader::initsd()
177 173
   }
178 174
   workDir=root;
179 175
   curDir=&root;
176
+  #ifdef SDCARD_SORT_ALPHA
177
+    presort();
178
+  #endif
180 179
   /*
181 180
   if(!workDir.openRoot(&volume))
182 181
   {
@@ -193,8 +192,10 @@ void CardReader::setroot()
193 192
     SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
194 193
   }*/
195 194
   workDir=root;
196
-  
197 195
   curDir=&workDir;
196
+  #ifdef SDCARD_SORT_ALPHA
197
+    presort();
198
+  #endif
198 199
 }
199 200
 void CardReader::release()
200 201
 {
@@ -235,7 +236,7 @@ void CardReader::getAbsFilename(char *t)
235 236
     while(*t!=0 && cnt< MAXPATHNAMELENGTH) 
236 237
     {t++;cnt++;}  //crawl counter forward.
237 238
   }
238
-  if(cnt<MAXPATHNAMELENGTH-13)
239
+  if(cnt<MAXPATHNAMELENGTH-FILENAME_LENGTH)
239 240
     file.getFilename(t);
240 241
   else
241 242
     t[0]=0;
@@ -305,7 +306,7 @@ void CardReader::openFile(char* name,bool read, bool replace_current/*=true*/)
305 306
       //SERIAL_ECHO("end  :");SERIAL_ECHOLN((int)(dirname_end-name));
306 307
       if(dirname_end>0 && dirname_end>dirname_start)
307 308
       {
308
-        char subdirname[13];
309
+        char subdirname[FILENAME_LENGTH];
309 310
         strncpy(subdirname, dirname_start, dirname_end-dirname_start);
310 311
         subdirname[dirname_end-dirname_start]=0;
311 312
         SERIAL_ECHOLN(subdirname);
@@ -401,7 +402,7 @@ void CardReader::removeFile(char* name)
401 402
       //SERIAL_ECHO("end  :");SERIAL_ECHOLN((int)(dirname_end-name));
402 403
       if(dirname_end>0 && dirname_end>dirname_start)
403 404
       {
404
-        char subdirname[13];
405
+        char subdirname[FILENAME_LENGTH];
405 406
         strncpy(subdirname, dirname_start, dirname_end-dirname_start);
406 407
         subdirname[dirname_end-dirname_start]=0;
407 408
         SERIAL_ECHOLN(subdirname);
@@ -439,6 +440,9 @@ void CardReader::removeFile(char* name)
439 440
       SERIAL_PROTOCOLPGM("File deleted:");
440 441
       SERIAL_PROTOCOLLN(fname);
441 442
       sdpos = 0;
443
+      #ifdef SDCARD_SORT_ALPHA
444
+        presort();
445
+      #endif
442 446
     }
443 447
     else
444 448
     {
@@ -577,7 +581,7 @@ void CardReader::chdir(const char * relpath)
577 581
 {
578 582
   SdFile newfile;
579 583
   SdFile *parent=&root;
580
-  
584
+
581 585
   if(workDir.isOpen())
582 586
     parent=&workDir;
583 587
   
@@ -595,21 +599,156 @@ void CardReader::chdir(const char * relpath)
595 599
       workDirParents[0]=*parent;
596 600
     }
597 601
     workDir=newfile;
602
+    #ifdef SDCARD_SORT_ALPHA
603
+      presort();
604
+    #endif
598 605
   }
599 606
 }
600 607
 
601 608
 void CardReader::updir()
602 609
 {
603
-  if(workDirDepth > 0)
610
+  if (workDirDepth > 0)
604 611
   {
605 612
     --workDirDepth;
606 613
     workDir = workDirParents[0];
607
-    int d;
608 614
     for (int d = 0; d < workDirDepth; d++)
609 615
       workDirParents[d] = workDirParents[d+1];
616
+    #ifdef SDCARD_SORT_ALPHA
617
+      presort();
618
+    #endif
610 619
   }
611 620
 }
612 621
 
622
+#ifdef SDCARD_SORT_ALPHA
623
+
624
+/**
625
+ * Get the name of a file in the current directory by sort-index
626
+ */
627
+void CardReader::getfilename_sorted(const uint8_t nr) {
628
+  #if SORT_USES_MORE_RAM
629
+    getfilename(nr < sort_count ? sort_order[nr] : nr);
630
+  #else
631
+    getfilename(nr < SORT_LIMIT ? sort_order[nr] : nr);
632
+  #endif
633
+}
634
+
635
+/**
636
+ * Read all the files and produce a sort key
637
+ *
638
+ * We can do this in 3 ways...
639
+ *  - Minimal RAM: Read two filenames at a time sorting along...
640
+ *  - Some RAM: Buffer the directory and return filenames from RAM
641
+ *  - Some RAM: Buffer the directory just for this sort
642
+ */
643
+void CardReader::presort()
644
+{
645
+  #if SORT_USES_MORE_RAM
646
+    flush_presort();
647
+  #endif
648
+
649
+  uint16_t fileCnt = getnrfilenames();
650
+  if (fileCnt > 0) {
651
+
652
+    if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT;
653
+
654
+    #if SORT_USES_MORE_RAM
655
+      sortnames = malloc(fileCnt * sizeof(char*));
656
+      sort_count = fileCnt;
657
+    #elif SORT_USES_RAM
658
+      char *sortnames[fileCnt];
659
+      #if FOLDER_SORTING != 0
660
+        uint8_t isdir[fileCnt];
661
+      #endif
662
+    #else
663
+      char sortname[LONG_FILENAME_LENGTH+1];
664
+    #endif
665
+
666
+    if (fileCnt > 1) {
667
+
668
+      // Init sort order [and get filenames]
669
+      for (int i=0; i<fileCnt; i++) {
670
+        int byte=i/8, bit=1<<(i%8);
671
+        sort_order[i] = i;
672
+        #if SORT_USES_RAM
673
+          getfilename(i);
674
+          char *name = diveFilename[0] ? diveFilename : filename;
675
+          // SERIAL_ECHOPGM("--- ");
676
+          // SERIAL_ECHOLN(name);
677
+          sortnames[i] = (char*)malloc(strlen(name) + 1);
678
+          strcpy(sortnames[i], name);
679
+          #if FOLDER_SORTING != 0
680
+            isdir[i] = filenameIsDir;
681
+          #endif
682
+        #endif
683
+      }
684
+
685
+      // Bubble Sort
686
+      for (uint8_t i=fileCnt; --i;) {
687
+        bool cmp, didSwap = false;
688
+        for (uint8_t j=0; j<i; ++j) {
689
+          int s1 = j, s2 = j+1, o1 = sort_order[s1], o2 = sort_order[s2];
690
+          #if SORT_USES_RAM
691
+            #if FOLDER_SORTING != 0
692
+              cmp = (isdir[o1] == isdir[o2]) ? (strcasecmp(sortnames[o1], sortnames[o2]) > 0) : isdir[FOLDER_SORTING > 0 ? o1 : o2];
693
+            #else
694
+              cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0);
695
+            #endif
696
+          #else
697
+            getfilename(o1);
698
+            #if FOLDER_SORTING != 0
699
+              bool dir1 = filenameIsDir;
700
+            #endif
701
+            char *name = diveFilename[0] ? diveFilename : filename;
702
+            strcpy(sortname, name);
703
+            getfilename(o2);
704
+            name = diveFilename[0] ? diveFilename : filename;
705
+            #if FOLDER_SORTING != 0
706
+              cmp = (dir1 == filenameIsDir) ? (strcasecmp(sortname, name) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1);
707
+            #else
708
+              cmp = strcasecmp(sortname, name) > 0);
709
+            #endif
710
+          #endif
711
+          if (cmp) {
712
+            // SERIAL_ECHOPGM("Swap ");
713
+            // SERIAL_ECHOLN(sortnames[o1]);
714
+            // SERIAL_ECHOPGM(" for ");
715
+            // SERIAL_ECHOLN(sortnames[o2]);
716
+            sort_order[s1] = o2;
717
+            sort_order[s2] = o1;
718
+            didSwap = true;
719
+          }
720
+        }
721
+        if (!didSwap) break;
722
+      }
723
+
724
+      #if SORT_USES_RAM && !SORT_USES_MORE_RAM
725
+        for (int i=0; i < fileCnt; ++i) free(sortnames[i]);
726
+      #endif
727
+    }
728
+    else {
729
+      sort_order[0] = 0;
730
+    }
731
+
732
+  }
733
+}
734
+
735
+void CardReader::flush_presort() {
736
+  #if SORT_USES_MORE_RAM
737
+    if (sort_count > 0) {
738
+      for (int i=0; i < sort_count; ++i) {
739
+        free(sortnames[i]);
740
+        sort_order[i] = i;
741
+      }
742
+      free(sortnames);
743
+      sortnames = NULL;
744
+      sort_count = 0;
745
+    }
746
+  #else
747
+    for (int i=SORT_LIMIT; --i;) sort_order[i] = i;
748
+  #endif
749
+}
750
+
751
+#endif
613 752
 
614 753
 void CardReader::printingHasFinished()
615 754
 {

+ 23
- 5
Marlin/cardreader.h Voir le fichier

@@ -3,7 +3,11 @@
3 3
 
4 4
 #ifdef SDSUPPORT
5 5
 
6
-#define MAX_DIR_DEPTH 10
6
+#define MAX_DIR_DEPTH 10          // Maximum folder depth
7
+#define SORT_USES_RAM false       // Buffer while sorting, else re-read from SD
8
+#define SORT_USES_MORE_RAM false  // Always keep the directory in RAM
9
+#define SORT_LIMIT 256            // Maximum number of sorted items
10
+#define FOLDER_SORTING -1         // -1=above  0=none  1=below
7 11
 
8 12
 #include "SdFile.h"
9 13
 enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename};
@@ -39,6 +43,12 @@ public:
39 43
   void updir();
40 44
   void setroot();
41 45
 
46
+#ifdef SDCARD_SORT_ALPHA
47
+  void presort();
48
+  void flush_presort();
49
+  void getfilename_sorted(const uint8_t nr);
50
+#endif
51
+
42 52
 
43 53
   FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
44 54
   FORCE_INLINE bool eof() { return sdpos>=filesize ;};
@@ -51,19 +61,27 @@ public:
51 61
   bool saving;
52 62
   bool logging;
53 63
   bool sdprinting ;  
54
-  bool cardOK ;
55
-  char filename[13];
56
-  char longFilename[LONG_FILENAME_LENGTH];
64
+  bool cardOK;
65
+  char filename[FILENAME_LENGTH];
66
+  char diveFilename[LONG_FILENAME_LENGTH];
57 67
   bool filenameIsDir;
58 68
   int lastnr; //last number of the autostart;
59 69
 private:
60 70
   SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH];
61 71
   uint16_t workDirDepth;
72
+#ifdef SDCARD_SORT_ALPHA
73
+  #if SORT_USES_MORE_RAM
74
+    uint16_t sort_count;
75
+    char **sortnames;
76
+  #else
77
+    uint8_t sort_order[SORT_LIMIT];
78
+  #endif
79
+#endif
62 80
   Sd2Card card;
63 81
   SdVolume volume;
64 82
   SdFile file;
65 83
   #define SD_PROCEDURE_DEPTH 1
66
-  #define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
84
+  #define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
67 85
   uint8_t file_subcall_ctr;
68 86
   uint32_t filespos[SD_PROCEDURE_DEPTH];
69 87
   char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];

+ 18
- 11
Marlin/ultralcd.cpp Voir le fichier

@@ -972,9 +972,9 @@ void lcd_sdcard_menu()
972 972
     card.getWorkDirName();
973 973
     if(card.filename[0]=='/')
974 974
     {
975
-#if SDCARDDETECT == -1
975
+      #if SDCARDDETECT == -1
976 976
         MENU_ITEM(function, LCD_STR_REFRESH MSG_REFRESH, lcd_sd_refresh);
977
-#endif
977
+      #endif
978 978
     }else{
979 979
         MENU_ITEM(function, LCD_STR_FOLDER "..", lcd_sd_updir);
980 980
     }
@@ -983,16 +983,23 @@ void lcd_sdcard_menu()
983 983
     {
984 984
         if (_menuItemNr == _lineNr)
985 985
         {
986
-            #ifndef SDCARD_RATHERRECENTFIRST
987
-              card.getfilename(i);
986
+            #if defined(SDCARD_RATHERRECENTFIRST) && !defined(SDCARD_SORT_ALPHA)
987
+              int nr = fileCnt-1-i;
988 988
             #else
989
-              card.getfilename(fileCnt-1-i);
989
+              int nr = i;
990 990
             #endif
991
-            if (card.filenameIsDir)
992
-            {
993
-                MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename);
994
-            }else{
995
-                MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename);
991
+
992
+            #ifdef SDCARD_SORT_ALPHA
993
+              card.getfilename_sorted(nr);
994
+            #else
995
+              card.getfilename(nr);
996
+            #endif
997
+
998
+            if (card.filenameIsDir) {
999
+              MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.diveFilename);
1000
+            }
1001
+            else {
1002
+              MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.diveFilename);
996 1003
             }
997 1004
         }else{
998 1005
             MENU_ITEM_DUMMY();
@@ -1198,7 +1205,7 @@ void lcd_init()
1198 1205
   #endif // SR_LCD_2W_NL
1199 1206
 #endif//!NEWPANEL
1200 1207
 
1201
-#if defined (SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
1208
+#if defined(SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
1202 1209
     pinMode(SDCARDDETECT,INPUT);
1203 1210
     WRITE(SDCARDDETECT, HIGH);
1204 1211
     lcd_oldcardstatus = IS_SD_INSERTED;

Chargement…
Annuler
Enregistrer