| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 | #include "cmdqueue.h"#include "cardreader.h"#include "ultralcd.h"extern bool Stopped;// Reserve BUFSIZE lines of length MAX_CMD_SIZE plus CMDBUFFER_RESERVE_FRONT.char cmdbuffer[BUFSIZE * (MAX_CMD_SIZE + 1) + CMDBUFFER_RESERVE_FRONT];// Head of the circular buffer, where to read.size_t bufindr = 0;// Tail of the buffer, where to write.static size_t bufindw = 0;// Number of lines in cmdbuffer.int buflen = 0;// Flag for processing the current command inside the main Arduino loop().// If a new command was pushed to the front of a command buffer while// processing another command, this replaces the command on the top.// Therefore don't remove the command from the queue in the loop() function.bool cmdbuffer_front_already_processed = false;// Used for temporarely preventing accidental adding of Serial commands to the queue.// For now only check_file and the fancheck pause use this.bool cmdqueue_serial_disabled = false;int serial_count = 0;  //index of character read from serial lineboolean comment_mode = false;char *strchr_pointer; // just a pointer to find chars in the command string like X, Y, Z, E, etcShortTimer farm_incomplete_command_timeout_timer;long gcode_N = 0;long gcode_LastN = 0;long Stopped_gcode_LastN = 0;uint32_t sdpos_atomic = 0;// Pop the currently processed command from the queue.// It is expected, that there is at least one command in the queue.bool cmdqueue_pop_front(){    if (buflen > 0) {#ifdef CMDBUFFER_DEBUG        SERIAL_ECHOPGM("Dequeing ");        SERIAL_ECHO(cmdbuffer+bufindr+CMDHDRSIZE);        SERIAL_ECHOLNPGM("");        SERIAL_ECHOPGM("Old indices: buflen ");        SERIAL_ECHO(buflen);        SERIAL_ECHOPGM(", bufindr ");        SERIAL_ECHO(bufindr);        SERIAL_ECHOPGM(", bufindw ");        SERIAL_ECHO(bufindw);        SERIAL_ECHOPGM(", serial_count ");        SERIAL_ECHO(serial_count);        SERIAL_ECHOPGM(", bufsize ");        SERIAL_ECHO(sizeof(cmdbuffer));        SERIAL_ECHOLNPGM("");#endif /* CMDBUFFER_DEBUG */        if (-- buflen == 0) {            // Empty buffer.            if (serial_count == 0)                // No serial communication is pending. Reset both pointers to zero.                bufindw = 0;            bufindr = bufindw;        } else {            // There is at least one ready line in the buffer.            // First skip the current command ID and iterate up to the end of the string.            for (bufindr += CMDHDRSIZE; cmdbuffer[bufindr] != 0; ++ bufindr) ;            // Second, skip the end of string null character and iterate until a nonzero command ID is found.            for (++ bufindr; bufindr < sizeof(cmdbuffer) && cmdbuffer[bufindr] == 0; ++ bufindr) ;            // If the end of the buffer was empty,            if (bufindr == sizeof(cmdbuffer)) {                // skip to the start and find the nonzero command.                for (bufindr = 0; cmdbuffer[bufindr] == 0; ++ bufindr) ;            }#ifdef CMDBUFFER_DEBUG            SERIAL_ECHOPGM("New indices: buflen ");            SERIAL_ECHO(buflen);            SERIAL_ECHOPGM(", bufindr ");            SERIAL_ECHO(bufindr);            SERIAL_ECHOPGM(", bufindw ");            SERIAL_ECHO(bufindw);            SERIAL_ECHOPGM(", serial_count ");            SERIAL_ECHO(serial_count);            SERIAL_ECHOPGM(" new command on the top: ");            SERIAL_ECHO(cmdbuffer+bufindr+CMDHDRSIZE);            SERIAL_ECHOLNPGM("");#endif /* CMDBUFFER_DEBUG */        }        return true;    }    return false;}void cmdqueue_reset(){	while (buflen)	{		// printf_P(PSTR("dumping: \"%s\" of type %hu\n"), cmdbuffer+bufindr+CMDHDRSIZE, CMDBUFFER_CURRENT_TYPE);		ClearToSend();		cmdqueue_pop_front();	}	bufindr = 0;	bufindw = 0;	//commands are removed from command queue after process_command() function is finished	//reseting command queue and enqueing new commands during some (usually long running) command processing would cause that new commands are immediately removed from queue (or damaged)	//this will ensure that all new commands which are enqueued after cmdqueue reset, will be always executed	cmdbuffer_front_already_processed = true; }// How long a string could be pushed to the front of the command queue?// If yes, adjust bufindr to the new position, where the new command could be enqued.// len_asked does not contain the zero terminator size.static bool cmdqueue_could_enqueue_front(size_t len_asked){    // MAX_CMD_SIZE has to accommodate the zero terminator.    if (len_asked >= MAX_CMD_SIZE)        return false;    // Remove the currently processed command from the queue.    if (! cmdbuffer_front_already_processed) {        cmdqueue_pop_front();        cmdbuffer_front_already_processed = true;    }    if (bufindr == bufindw && buflen > 0)        // Full buffer.        return false;    // Adjust the end of the write buffer based on whether a partial line is in the receive buffer.    int endw = (serial_count > 0) ? (bufindw + MAX_CMD_SIZE + 1) : bufindw;    if (bufindw < bufindr) {        int bufindr_new = bufindr - len_asked - (1 + CMDHDRSIZE);        // Simple case. There is a contiguous space between the write buffer and the read buffer.        if (endw <= bufindr_new) {            bufindr = bufindr_new;            return true;        }    } else {        // Otherwise the free space is split between the start and end.        if (len_asked + (1 + CMDHDRSIZE) <= bufindr) {            // Could fit at the start.            bufindr -= len_asked + (1 + CMDHDRSIZE);            return true;        }        int bufindr_new = sizeof(cmdbuffer) - len_asked - (1 + CMDHDRSIZE);        if (endw <= bufindr_new) {            memset(cmdbuffer, 0, bufindr);            bufindr = bufindr_new;            return true;        }    }    return false;}// Could one enqueue a command of length len_asked into the buffer,// while leaving CMDBUFFER_RESERVE_FRONT at the start?// If yes, adjust bufindw to the new position, where the new command could be enqued.// len_asked does not contain the zero terminator size.// This function may update bufindw, therefore for the power panic to work, this function must be called// with the interrupts disabled!static bool cmdqueue_could_enqueue_back(size_t len_asked, bool atomic_update = false){    // MAX_CMD_SIZE has to accommodate the zero terminator.    if (len_asked >= MAX_CMD_SIZE)        return false;    if (bufindr == bufindw && buflen > 0)        // Full buffer.        return false;    if (serial_count > 0) {        // If there is some data stored starting at bufindw, len_asked is certainly smaller than        // the allocated data buffer. Try to reserve a new buffer and to move the already received        // serial data.        // How much memory to reserve for the commands pushed to the front?        // End of the queue, when pushing to the end.        size_t endw = bufindw + len_asked + (1 + CMDHDRSIZE);        if (bufindw < bufindr)            // Simple case. There is a contiguous space between the write buffer and the read buffer.            return endw + CMDBUFFER_RESERVE_FRONT <= bufindr;        // Otherwise the free space is split between the start and end.        if (// Could one fit to the end, including the reserve?            endw + CMDBUFFER_RESERVE_FRONT <= sizeof(cmdbuffer) ||            // Could one fit to the end, and the reserve to the start?            (endw <= sizeof(cmdbuffer) && CMDBUFFER_RESERVE_FRONT <= bufindr))            return true;        // Could one fit both to the start?        if (len_asked + (1 + CMDHDRSIZE) + CMDBUFFER_RESERVE_FRONT <= bufindr) {            // Mark the rest of the buffer as used.            memset(cmdbuffer+bufindw, 0, sizeof(cmdbuffer)-bufindw);            // and point to the start.            // Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.            if (atomic_update)                cli();            bufindw = 0;            if (atomic_update)                sei();            return true;        }    } else {        // How much memory to reserve for the commands pushed to the front?        // End of the queue, when pushing to the end.        size_t endw = bufindw + len_asked + (1 + CMDHDRSIZE);        if (bufindw < bufindr)            // Simple case. There is a contiguous space between the write buffer and the read buffer.            return endw + CMDBUFFER_RESERVE_FRONT <= bufindr;        // Otherwise the free space is split between the start and end.        if (// Could one fit to the end, including the reserve?            endw + CMDBUFFER_RESERVE_FRONT <= sizeof(cmdbuffer) ||            // Could one fit to the end, and the reserve to the start?            (endw <= sizeof(cmdbuffer) && CMDBUFFER_RESERVE_FRONT <= bufindr))            return true;        // Could one fit both to the start?        if (len_asked + (1 + CMDHDRSIZE) + CMDBUFFER_RESERVE_FRONT <= bufindr) {            // Mark the rest of the buffer as used.            memset(cmdbuffer+bufindw, 0, sizeof(cmdbuffer)-bufindw);            // and point to the start.            // Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.            if (atomic_update)                cli();            bufindw = 0;            if (atomic_update)                sei();            return true;        }    }    return false;}#ifdef CMDBUFFER_DEBUGvoid cmdqueue_dump_to_serial_single_line(int nr, const char *p){    SERIAL_ECHOPGM("Entry nr: ");    SERIAL_ECHO(nr);    SERIAL_ECHOPGM(", type: ");    int type = *p;    SERIAL_ECHO(type);    SERIAL_ECHOPGM(", size: ");    unsigned int size = *(unsigned int*)(p + 1);    SERIAL_ECHO(size);    SERIAL_ECHOPGM(", cmd: ");    SERIAL_ECHO(p + CMDHDRSIZE);    SERIAL_ECHOLNPGM("");}void cmdqueue_dump_to_serial(){    if (buflen == 0) {        SERIAL_ECHOLNPGM("The command buffer is empty.");    } else {        SERIAL_ECHOPGM("Content of the buffer: entries ");        SERIAL_ECHO(buflen);        SERIAL_ECHOPGM(", indr ");        SERIAL_ECHO(bufindr);        SERIAL_ECHOPGM(", indw ");        SERIAL_ECHO(bufindw);        SERIAL_ECHOLNPGM("");        int nr = 0;        if (bufindr < bufindw) {            for (const char *p = cmdbuffer + bufindr; p < cmdbuffer + bufindw; ++ nr) {                cmdqueue_dump_to_serial_single_line(nr, p);                // Skip the command.                for (p += CMDHDRSIZE; *p != 0; ++ p);                // Skip the gaps.                for (++p; p < cmdbuffer + bufindw && *p == 0; ++ p);            }        } else {            for (const char *p = cmdbuffer + bufindr; p < cmdbuffer + sizeof(cmdbuffer); ++ nr) {                cmdqueue_dump_to_serial_single_line(nr, p);                // Skip the command.                for (p += CMDHDRSIZE; *p != 0; ++ p);                // Skip the gaps.                for (++p; p < cmdbuffer + sizeof(cmdbuffer) && *p == 0; ++ p);            }            for (const char *p = cmdbuffer; p < cmdbuffer + bufindw; ++ nr) {                cmdqueue_dump_to_serial_single_line(nr, p);                // Skip the command.                for (p += CMDHDRSIZE; *p != 0; ++ p);                // Skip the gaps.                for (++p; p < cmdbuffer + bufindw && *p == 0; ++ p);            }        }        SERIAL_ECHOLNPGM("End of the buffer.");    }}#endif /* CMDBUFFER_DEBUG *///adds an command to the main command buffer//thats really done in a non-safe way.//needs overworking someday// Currently the maximum length of a command piped through this function is around 20 charactersvoid enquecommand(const char *cmd, bool from_progmem){    size_t len = from_progmem ? strlen_P(cmd) : strlen(cmd);    // Does cmd fit the queue while leaving sufficient space at the front for the chained commands?    // If it fits, it may move bufindw, so it points to a contiguous buffer, which fits cmd.    if (cmdqueue_could_enqueue_back(len)) {        // This is dangerous if a mixing of serial and this happens        // This may easily be tested: If serial_count > 0, we have a problem.        cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_UI;        if (from_progmem)            strcpy_P(cmdbuffer + bufindw + CMDHDRSIZE, cmd);        else            strcpy(cmdbuffer + bufindw + CMDHDRSIZE, cmd);        SERIAL_ECHO_START;        SERIAL_ECHORPGM(MSG_Enqueing);        SERIAL_ECHO(cmdbuffer + bufindw + CMDHDRSIZE);        SERIAL_ECHOLNPGM("\"");        bufindw += len + (CMDHDRSIZE + 1);        if (bufindw == sizeof(cmdbuffer))            bufindw = 0;        ++ buflen;#ifdef CMDBUFFER_DEBUG        cmdqueue_dump_to_serial();#endif /* CMDBUFFER_DEBUG */    } else {        SERIAL_ERROR_START;        SERIAL_ECHORPGM(MSG_Enqueing);        if (from_progmem)            SERIAL_PROTOCOLRPGM(cmd);        else            SERIAL_ECHO(cmd);        SERIAL_ECHOLNPGM("\" failed: Buffer full!");#ifdef CMDBUFFER_DEBUG        cmdqueue_dump_to_serial();#endif /* CMDBUFFER_DEBUG */    }}bool cmd_buffer_empty(){	return (buflen == 0);}void enquecommand_front(const char *cmd, bool from_progmem){    size_t len = from_progmem ? strlen_P(cmd) : strlen(cmd);    // Does cmd fit the queue? This call shall move bufindr, so the command may be copied.    if (cmdqueue_could_enqueue_front(len)) {        cmdbuffer[bufindr] = CMDBUFFER_CURRENT_TYPE_UI;        if (from_progmem)            strcpy_P(cmdbuffer + bufindr + CMDHDRSIZE, cmd);        else            strcpy(cmdbuffer + bufindr + CMDHDRSIZE, cmd);        ++ buflen;        SERIAL_ECHO_START;        SERIAL_ECHOPGM("Enqueing to the front: \"");        SERIAL_ECHO(cmdbuffer + bufindr + CMDHDRSIZE);        SERIAL_ECHOLNPGM("\"");#ifdef CMDBUFFER_DEBUG        cmdqueue_dump_to_serial();#endif /* CMDBUFFER_DEBUG */    } else {        SERIAL_ERROR_START;        SERIAL_ECHOPGM("Enqueing to the front: \"");        if (from_progmem)            SERIAL_PROTOCOLRPGM(cmd);        else            SERIAL_ECHO(cmd);        SERIAL_ECHOLNPGM("\" failed: Buffer full!");#ifdef CMDBUFFER_DEBUG        cmdqueue_dump_to_serial();#endif /* CMDBUFFER_DEBUG */    }}// Mark the command at the top of the command queue as new.// Therefore it will not be removed from the queue.void repeatcommand_front(){    cmdbuffer_front_already_processed = true;} void proc_commands() {	if (buflen)	{		process_commands();		if (!cmdbuffer_front_already_processed)			cmdqueue_pop_front();		cmdbuffer_front_already_processed = false;	}}void get_command(){    // Test and reserve space for the new command string.    if (! cmdqueue_could_enqueue_back(MAX_CMD_SIZE - 1, true))      return;	if (MYSERIAL.available() == RX_BUFFER_SIZE - 1) { //compare number of chars buffered in rx buffer with rx buffer size		MYSERIAL.flush();		SERIAL_ECHOLNPGM("Full RX Buffer");   //if buffer was full, there is danger that reading of last gcode will not be completed	}  // start of serial line processing loop  while (((MYSERIAL.available() > 0 && !saved_printing) || (MYSERIAL.available() > 0 && isPrintPaused)) && !cmdqueue_serial_disabled) {  //is print is saved (crash detection or filament detection), dont process data from serial line	    char serial_char = MYSERIAL.read();    farm_incomplete_command_timeout_timer.start();    if (serial_char < 0)        // Ignore extended ASCII characters. These characters have no meaning in the G-code apart from the file names        // and Marlin does not support such file names anyway.        // Serial characters with a highest bit set to 1 are generated when the USB cable is unplugged, leading        // to a hang-up of the print process from an SD card.        continue;    if(serial_char == '\n' ||       serial_char == '\r' ||       serial_count >= (MAX_CMD_SIZE - 1) )    {      if(!serial_count) { //if empty line        comment_mode = false; //for new command        return;      }      cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0; //terminate string      if(!comment_mode){		  		  gcode_N = 0;		  // Line numbers must be first in buffer		  if ((strstr(cmdbuffer+bufindw+CMDHDRSIZE, "PRUSA") == NULL) &&			  (cmdbuffer[bufindw+CMDHDRSIZE] == 'N')) {			  // Line number met. When sending a G-code over a serial line, each line may be stamped with its index,			  // and Marlin tests, whether the successive lines are stamped with an increasing line number ID			  gcode_N = (strtol(cmdbuffer+bufindw+CMDHDRSIZE+1, NULL, 10));			  if(gcode_N != gcode_LastN+1 && (strstr_P(cmdbuffer+bufindw+CMDHDRSIZE, PSTR("M110")) == NULL) ) {				  // M110 - set current line number.				  // Line numbers not sent in succession.				  SERIAL_ERROR_START;				  SERIAL_ERRORRPGM(_n("Line Number is not Last Line Number+1, Last Line: "));////MSG_ERR_LINE_NO				  SERIAL_ERRORLN(gcode_LastN);				  //Serial.println(gcode_N);				  FlushSerialRequestResend();				  serial_count = 0;				  return;			  }			  if((strchr_pointer = strchr(cmdbuffer+bufindw+CMDHDRSIZE, '*')) != NULL)			  {				  byte checksum = 0;				  char *p = cmdbuffer+bufindw+CMDHDRSIZE;				  while (p != strchr_pointer)					  checksum = checksum^(*p++);				  if (int(strtol(strchr_pointer+1, NULL, 10)) != int(checksum)) {					  SERIAL_ERROR_START;					  SERIAL_ERRORRPGM(_n("checksum mismatch, Last Line: "));////MSG_ERR_CHECKSUM_MISMATCH					  SERIAL_ERRORLN(gcode_LastN);					  FlushSerialRequestResend();					  serial_count = 0;					  return;				  }				  // If no errors, remove the checksum and continue parsing.				  *strchr_pointer = 0;			  }			  else			  {				  SERIAL_ERROR_START;				  SERIAL_ERRORRPGM(_n("No Checksum with line number, Last Line: "));////MSG_ERR_NO_CHECKSUM				  SERIAL_ERRORLN(gcode_LastN);				  FlushSerialRequestResend();				  serial_count = 0;				  return;			  }			  // Don't parse N again with code_seen('N')			  cmdbuffer[bufindw + CMDHDRSIZE] = '$';			  //if no errors, continue parsing			  gcode_LastN = gcode_N;		}        // if we don't receive 'N' but still see '*'        if ((cmdbuffer[bufindw + CMDHDRSIZE] != 'N') && (cmdbuffer[bufindw + CMDHDRSIZE] != '$') && (strchr(cmdbuffer+bufindw+CMDHDRSIZE, '*') != NULL))        {            SERIAL_ERROR_START;            SERIAL_ERRORRPGM(_n("No Line Number with checksum, Last Line: "));////MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM            SERIAL_ERRORLN(gcode_LastN);			FlushSerialRequestResend();            serial_count = 0;            return;        }        if ((strchr_pointer = strchr(cmdbuffer+bufindw+CMDHDRSIZE, 'G')) != NULL) {              if (! IS_SD_PRINTING) {                      usb_printing_counter = 10;                      is_usb_printing = true;              }            if (Stopped == true) {                int gcode = strtol(strchr_pointer+1, NULL, 10);                if (gcode >= 0 && gcode <= 3) {                    SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED);                    LCD_MESSAGERPGM(_T(MSG_STOPPED));                }            }        } // end of 'G' command        //If command was e-stop process now        if(strcmp(cmdbuffer+bufindw+CMDHDRSIZE, "M112") == 0)          kill(MSG_M112_KILL, 2);                // Store the current line into buffer, move to the next line.		// Store type of entry        cmdbuffer[bufindw] = gcode_N ? CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR : CMDBUFFER_CURRENT_TYPE_USB;#ifdef CMDBUFFER_DEBUG        SERIAL_ECHO_START;        SERIAL_ECHOPGM("Storing a command line to buffer: ");        SERIAL_ECHO(cmdbuffer+bufindw+CMDHDRSIZE);        SERIAL_ECHOLNPGM("");#endif /* CMDBUFFER_DEBUG */        bufindw += strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);        if (bufindw == sizeof(cmdbuffer))            bufindw = 0;        ++ buflen;#ifdef CMDBUFFER_DEBUG        SERIAL_ECHOPGM("Number of commands in the buffer: ");        SERIAL_ECHO(buflen);        SERIAL_ECHOLNPGM("");#endif /* CMDBUFFER_DEBUG */      } // end of 'not comment mode'      serial_count = 0; //clear buffer      // Don't call cmdqueue_could_enqueue_back if there are no characters waiting      // in the queue, as this function will reserve the memory.      if (MYSERIAL.available() == 0 || ! cmdqueue_could_enqueue_back(MAX_CMD_SIZE-1, true))          return;    } // end of "end of line" processing    else {      // Not an "end of line" symbol. Store the new character into a buffer.      if(serial_char == ';') comment_mode = true;      if(!comment_mode) cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;    }  } // end of serial line processing loop    if(farm_mode && (serial_count > 0)){        if (farm_incomplete_command_timeout_timer.expired(800)) {            cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0;                        bufindw += strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);            if (bufindw == sizeof(cmdbuffer))                bufindw = 0;            ++ buflen;                        serial_count = 0;                        SERIAL_ECHOPGM("TIMEOUT:");            //memset(cmdbuffer, 0 , sizeof(cmdbuffer));            return;        }    }  #ifdef SDSUPPORT  if(!card.sdprinting || serial_count!=0){    // If there is a half filled buffer from serial line, wait until return before    // continuing with the serial line.     return;  }  //'#' stops reading from SD to the buffer prematurely, so procedural macro calls are possible  // if it occurs, stop_buffering is triggered and the buffer is ran dry.  // this character _can_ occur in serial com, due to checksums. however, no checksums are used in SD printing  static bool stop_buffering=false;  if(buflen==0) stop_buffering=false;  union {    struct {        char lo;        char hi;    } lohi;    uint16_t value;  } sd_count;  sd_count.value = 0;  // Reads whole lines from the SD card. Never leaves a half-filled line in the cmdbuffer.  while( !card.eof() && !stop_buffering) {    int16_t n=card.getFilteredGcodeChar();    char serial_char = (char)n;    if( serial_char == '\n'     || serial_char == '\r'     || ((serial_char == '#' || serial_char == ':') )     || serial_count >= (MAX_CMD_SIZE - 1)     || n==-1    ){      if(serial_char=='#')        stop_buffering=true;      if(!serial_count)      {        // This is either an empty line, or a line with just a comment.        // Continue to the following line, and continue accumulating the number of bytes        // read from the sdcard into sd_count,         // so that the lenght of the already read empty lines and comments will be added        // to the following non-empty line.         return; // prevent cycling indefinitely - let manage_heaters do their job      }      // The new command buffer could be updated non-atomically, because it is not yet considered      // to be inside the active queue.      sd_count.value = card.get_sdpos() - sdpos_atomic;      cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_SDCARD;      cmdbuffer[bufindw+1] = sd_count.lohi.lo;      cmdbuffer[bufindw+2] = sd_count.lohi.hi;      cmdbuffer[bufindw+serial_count+CMDHDRSIZE] = 0; //terminate string      // Calculate the length before disabling the interrupts.      uint8_t len = strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE);//      SERIAL_ECHOPGM("SD cmd(");//      MYSERIAL.print(sd_count.value, DEC);//      SERIAL_ECHOPGM(") ");//      SERIAL_ECHOLN(cmdbuffer+bufindw+CMDHDRSIZE);//      SERIAL_ECHOPGM("cmdbuffer:");//      MYSERIAL.print(cmdbuffer);//      SERIAL_ECHOPGM("buflen:");//      MYSERIAL.print(buflen+1);      sd_count.value = 0;      cli();      // This block locks the interrupts globally for 3.56 us,      // which corresponds to a maximum repeat frequency of 280.70 kHz.      // This blocking is safe in the context of a 10kHz stepper driver interrupt      // or a 115200 Bd serial line receive interrupt, which will not trigger faster than 12kHz.      ++ buflen;      bufindw += len;      sdpos_atomic = card.get_sdpos();      if (bufindw == sizeof(cmdbuffer))          bufindw = 0;      sei();      comment_mode = false; //for new command      serial_count = 0; //clear buffer          if(card.eof()) break;      // The following line will reserve buffer space if available.      if (! cmdqueue_could_enqueue_back(MAX_CMD_SIZE-1, true))          return;    }    else    {        // there are no comments coming from the filtered file        cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;    }  }  if(card.eof())  {      // file was fully buffered, but commands might still need to be planned!      // do *not* clear sdprinting until all SD commands are consumed to ensure      // SD state can be resumed from a saved printing state. sdprinting is only      // cleared by printingHasFinished after peforming all remaining moves.      if(!cmdqueue_calc_sd_length())      {          SERIAL_PROTOCOLLNRPGM(_n("Done printing file"));////MSG_FILE_PRINTED          stoptime=_millis();          char time[30];          unsigned long t=(stoptime-starttime-pause_time)/1000;          pause_time = 0;          int hours, minutes;          minutes=(t/60)%60;          hours=t/60/60;          save_statistics(total_filament_used, t);          sprintf_P(time, PSTR("%i hours %i minutes"),hours, minutes);          SERIAL_ECHO_START;          SERIAL_ECHOLN(time);          lcd_setstatus(time);          card.printingHasFinished();          card.checkautostart(true);          if (farm_mode)          {              prusa_statistics(6);              lcd_commands_type = LcdCommands::FarmModeConfirm;          }      }  }  #endif //SDSUPPORT}uint16_t cmdqueue_calc_sd_length(){    if (buflen == 0)        return 0;    union {        struct {            char lo;            char hi;        } lohi;        uint16_t value;    } sdlen_single;    uint16_t sdlen = 0;    for (size_t _buflen = buflen, _bufindr = bufindr;;) {        if (cmdbuffer[_bufindr] == CMDBUFFER_CURRENT_TYPE_SDCARD) {            sdlen_single.lohi.lo = cmdbuffer[_bufindr + 1];            sdlen_single.lohi.hi = cmdbuffer[_bufindr + 2];            sdlen += sdlen_single.value;        }        if (-- _buflen == 0)            break;        // First skip the current command ID and iterate up to the end of the string.        for (_bufindr += CMDHDRSIZE; cmdbuffer[_bufindr] != 0; ++ _bufindr) ;        // Second, skip the end of string null character and iterate until a nonzero command ID is found.        for (++ _bufindr; _bufindr < sizeof(cmdbuffer) && cmdbuffer[_bufindr] == 0; ++ _bufindr) ;        // If the end of the buffer was empty,        if (_bufindr == sizeof(cmdbuffer)) {            // skip to the start and find the nonzero command.            for (_bufindr = 0; cmdbuffer[_bufindr] == 0; ++ _bufindr) ;        }    }    return sdlen;}
 |