From ab965376ff70b3f80b180a47db67ee6dea33d5a6 Mon Sep 17 00:00:00 2001
From: bkubicek <bkubicek@x201.(none)>
Date: Tue, 22 Oct 2013 10:02:18 +0200
Subject: [PATCH] Sub-file calls. by overloading M32 it is now possible to
 execute gcode files from other gcode files, with a fixed recursion level.
 This can be used e.g. for having a real start.g and end.g somewhere on the sd
 card, which are then called from the normal print file. Another usecase would
 be to have macro-files for nozzle-change and layerchange. I have not tested
 the speedwise performance. The testing was done with pronterface.

syntax:
normal call from sd card will open the new file and continue executing there.
M32 !/path/filename#
this however will call the new file and return to the caller file.
M32 P !/path/filename#
with the optional "S<position>" the  file starting position can be set.
this is for continuing prints from a previous location.
---
 Marlin/Marlin_main.cpp | 43 +++++++++++++++-----
 Marlin/cardreader.cpp  | 89 +++++++++++++++++++++++++++++++++++++-----
 Marlin/cardreader.h    |  9 ++++-
 3 files changed, 121 insertions(+), 20 deletions(-)

diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index c53a5b5830..4bea72b158 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -90,7 +90,10 @@
 // M29  - Stop SD write
 // M30  - Delete file from SD (M30 filename.g)
 // M31  - Output time since last M109 or SD card start to serial
-// M32  - Select file and start SD print (Can be used when printing from SD card)
+// M32  - Select file and start SD print (Can be used _while_ printing from SD card files): 
+//        syntax "M32 /path/filename#", or "M32 S<startpos bytes> !filename#"
+//        Call gcode file : "M32 P !filename#" and return to caller file after finishing (simiarl to #include).
+//        The '#' is necessary when calling from within sd files, as it stops buffer prereading
 // M42  - Change pin status via gcode Use M42 Px Sy to set pin x to value y, when omitting Px the onboard led will be used.
 // M80  - Turn on Power Supply
 // M81  - Turn off Power Supply
@@ -1467,19 +1470,41 @@ void process_commands()
         card.removeFile(strchr_pointer + 4);
       }
       break;
-    case 32: //M32 - Select file and start SD print
+    case 32: //M32 - Select file and start SD print 
+    {
       if(card.sdprinting) {
         st_synchronize();
-        card.closefile();
-        card.sdprinting = false;
+
       }
-      starpos = (strchr(strchr_pointer + 4,'*'));
+      starpos = (strchr(strchr_pointer + 4,'*')); 
+      
+      char* namestartpos = (strchr(strchr_pointer + 4,'!'));   //find ! to indicate filename string start.
+      if(namestartpos==NULL)
+      {
+        namestartpos=strchr_pointer + 4; //default name position, 4 letters after the M
+      }
+      else
+        namestartpos++; //to skip the '!'
+        
       if(starpos!=NULL)
         *(starpos-1)='\0';
-      card.openFile(strchr_pointer + 4,true);
-      card.startFileprint();
-      starttime=millis();
-      break;
+            
+      bool call_procedure=(code_seen('P'));
+      
+      if(strchr_pointer>namestartpos) 
+        call_procedure=false;  //false alert, 'P' found within filename
+      
+      if( card.cardOK ) 
+      {
+        card.openFile(namestartpos,true,!call_procedure);
+        if(code_seen('S'))
+          if(strchr_pointer<namestartpos) //only if "S" is occuring _before_ the filename
+            card.setIndex(code_value_long());
+        card.startFileprint();
+        if(!call_procedure)
+          starttime=millis(); //procedure calls count as normal print time.
+      }
+    } break;
     case 928: //M928 - Start SD write
       starpos = (strchr(strchr_pointer + 5,'*'));
       if(starpos != NULL){
diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp
index 1eeec9193f..f01f4606ee 100644
--- a/Marlin/cardreader.cpp
+++ b/Marlin/cardreader.cpp
@@ -19,6 +19,7 @@ CardReader::CardReader()
    logging = false;
    autostart_atmillis=0;
    workDirDepth = 0;
+   file_subcall_ctr=0;
    memset(workDirParents, 0, sizeof(workDirParents));
 
    autostart_stilltocheck=true; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
@@ -224,14 +225,71 @@ void CardReader::openLogFile(char* name)
   openFile(name, false);
 }
 
-void CardReader::openFile(char* name,bool read)
+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.
+  }
+  if(cnt<MAXPATHNAMELENGTH-13)
+    file.getFilename(t);
+  else
+    t[0]=0;
+}
+
+void CardReader::openFile(char* name,bool read, bool replace_current/*=true*/)
 {
   if(!cardOK)
     return;
-  file.close();
+  if(file.isOpen())  //replaceing 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);
+       kill();
+       return;
+     }
+     
+     SERIAL_ECHO_START;
+     SERIAL_ECHOPGM("SUBROUTINE CALL target:\"");
+     SERIAL_ECHO(name);
+     SERIAL_ECHOPGM("\" parent:\"");
+     
+     //store current filename and position
+     getAbsFilename(filenames[file_subcall_ctr]);
+     
+     SERIAL_ECHO(filenames[file_subcall_ctr]);
+     SERIAL_ECHOPGM("\" pos");
+     SERIAL_ECHOLN(sdpos);
+     filespos[file_subcall_ctr]=sdpos;
+     file_subcall_ctr++;
+    }
+    else
+    {
+     SERIAL_ECHO_START;
+     SERIAL_ECHOPGM("Now doing file: ");
+     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;
+    SERIAL_ECHOPGM("Now fresh file: ");
+    SERIAL_ECHOLN(name);
+  }
   sdprinting = false;
   
-  
+ 
   SdFile myDir;
   curDir=&root;
   char *fname=name;
@@ -547,14 +605,25 @@ void CardReader::updir()
 void CardReader::printingHasFinished()
 {
     st_synchronize();
-    quickStop();
-    file.close();
-    sdprinting = false;
-    if(SD_FINISHED_STEPPERRELEASE)
+    if(file_subcall_ctr>0) //heading up to a parent file that called current as a procedure.
     {
-        //finishAndDisableSteppers();
-        enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND));
+      file.close();
+      file_subcall_ctr--;
+      openFile(filenames[file_subcall_ctr],true,true);
+      setIndex(filespos[file_subcall_ctr]);
+      startFileprint();
+    }
+    else
+    {
+      quickStop();
+      file.close();
+      sdprinting = false;
+      if(SD_FINISHED_STEPPERRELEASE)
+      {
+          //finishAndDisableSteppers();
+          enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND));
+      }
+      autotempShutdown();
     }
-    autotempShutdown();
 }
 #endif //SDSUPPORT
diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h
index 6e59645295..07c7090ce9 100644
--- a/Marlin/cardreader.h
+++ b/Marlin/cardreader.h
@@ -18,7 +18,7 @@ public:
   //this is to delay autostart and hence the initialisaiton of the sd card to some seconds after the normal init, so the device is available quick after a reset
 
   void checkautostart(bool x); 
-  void openFile(char* name,bool read);
+  void openFile(char* name,bool read,bool replace_current=true);
   void openLogFile(char* name);
   void removeFile(char* name);
   void closefile();
@@ -31,6 +31,8 @@ public:
   void getfilename(const uint8_t nr);
   uint16_t getnrfilenames();
   
+  void getAbsFilename(char *t);
+  
 
   void ls();
   void chdir(const char * relpath);
@@ -60,6 +62,11 @@ private:
   Sd2Card card;
   SdVolume volume;
   SdFile file;
+  #define SD_PROCEDURE_DEPTH 1
+  #define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
+  uint8_t file_subcall_ctr;
+  uint32_t filespos[SD_PROCEDURE_DEPTH];
+  char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
   uint32_t filesize;
   //int16_t n;
   unsigned long autostart_atmillis;