2017-09-14 14:19:49 +00:00
# include "cmdqueue.h"
# include "cardreader.h"
# include "ultralcd.h"
// 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.
2018-07-25 19:18:29 +00:00
size_t bufindr = 0 ;
2017-09-14 14:19:49 +00:00
// Tail of the buffer, where to write.
2018-07-25 19:18:29 +00:00
static size_t bufindw = 0 ;
2017-09-14 14:19:49 +00:00
// 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 ;
2020-10-26 07:42:26 +00:00
// 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 ;
2017-09-14 14:19:49 +00:00
int serial_count = 0 ; //index of character read from serial line
2021-06-24 17:02:18 +00:00
bool comment_mode = false ;
2017-09-14 14:19:49 +00:00
char * strchr_pointer ; // just a pointer to find chars in the command string like X, Y, Z, E, etc
2022-01-18 17:06:35 +00:00
ShortTimer serialTimeoutTimer ;
2017-09-14 14:19:49 +00:00
long gcode_N = 0 ;
long gcode_LastN = 0 ;
2017-09-20 14:04:02 +00:00
uint32_t sdpos_atomic = 0 ;
2017-09-14 14:19:49 +00:00
// 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 ( )
{
2020-10-26 07:42:26 +00:00
while ( buflen )
{
2022-02-02 19:58:21 +00:00
// printf_P(PSTR("dumping: \"%s\" of type %u\n"), cmdbuffer+bufindr+CMDHDRSIZE, CMDBUFFER_CURRENT_TYPE);
2020-10-26 07:42:26 +00:00
ClearToSend ( ) ;
cmdqueue_pop_front ( ) ;
}
bufindr = 0 ;
bufindw = 0 ;
2019-02-21 13:28:05 +00:00
//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
2020-10-26 07:42:26 +00:00
cmdbuffer_front_already_processed = true ;
2017-09-14 14:19:49 +00:00
}
// 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.
2018-07-25 19:18:29 +00:00
static bool cmdqueue_could_enqueue_front ( size_t len_asked )
2017-09-14 14:19:49 +00:00
{
// 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 ;
}
2017-09-21 15:50:39 +00:00
if ( bufindr = = bufindw & & buflen > 0 )
// Full buffer.
2017-09-14 14:19:49 +00:00
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 ;
}
2017-09-21 15:50:39 +00:00
// Could one enqueue a command of length len_asked into the buffer,
2017-09-14 14:19:49 +00:00
// 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.
2017-09-21 15:50:39 +00:00
// This function may update bufindw, therefore for the power panic to work, this function must be called
// with the interrupts disabled!
2018-07-25 19:18:29 +00:00
static bool cmdqueue_could_enqueue_back ( size_t len_asked , bool atomic_update = false )
2017-09-14 14:19:49 +00:00
{
// MAX_CMD_SIZE has to accommodate the zero terminator.
if ( len_asked > = MAX_CMD_SIZE )
return false ;
2017-09-21 15:50:39 +00:00
if ( bufindr = = bufindw & & buflen > 0 )
// Full buffer.
return false ;
2017-09-14 14:19:49 +00:00
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.
2018-07-25 19:18:29 +00:00
size_t endw = bufindw + len_asked + ( 1 + CMDHDRSIZE ) ;
2017-09-14 14:19:49 +00:00
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.
2017-09-21 15:50:39 +00:00
// Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.
if ( atomic_update )
cli ( ) ;
2017-09-14 14:19:49 +00:00
bufindw = 0 ;
2017-09-21 15:50:39 +00:00
if ( atomic_update )
sei ( ) ;
2017-09-14 14:19:49 +00:00
return true ;
}
} else {
// How much memory to reserve for the commands pushed to the front?
// End of the queue, when pushing to the end.
2018-07-25 19:18:29 +00:00
size_t endw = bufindw + len_asked + ( 1 + CMDHDRSIZE ) ;
2017-09-14 14:19:49 +00:00
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.
2017-09-21 15:50:39 +00:00
// Be careful! The bufindw needs to be changed atomically for the power panic & filament panic to work.
if ( atomic_update )
cli ( ) ;
2017-09-14 14:19:49 +00:00
bufindw = 0 ;
2017-09-21 15:50:39 +00:00
if ( atomic_update )
sei ( ) ;
2017-09-14 14:19:49 +00:00
return true ;
}
}
return false ;
}
# ifdef CMDBUFFER_DEBUG
void cmdqueue_dump_to_serial_single_line ( int nr , const char * p )
{
SERIAL_ECHOPGM ( " Entry nr: " ) ;
SERIAL_ECHO ( nr ) ;
SERIAL_ECHOPGM ( " , type: " ) ;
2019-08-23 14:48:30 +00:00
int type = * p ;
SERIAL_ECHO ( type ) ;
SERIAL_ECHOPGM ( " , size: " ) ;
unsigned int size = * ( unsigned int * ) ( p + 1 ) ;
SERIAL_ECHO ( size ) ;
2017-09-14 14:19:49 +00:00
SERIAL_ECHOPGM ( " , cmd: " ) ;
2019-08-23 14:48:30 +00:00
SERIAL_ECHO ( p + CMDHDRSIZE ) ;
2017-09-14 14:19:49 +00:00
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.
2019-08-23 14:48:30 +00:00
for ( p + = CMDHDRSIZE ; * p ! = 0 ; + + p ) ;
2017-09-14 14:19:49 +00:00
// 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.
2019-08-23 14:48:30 +00:00
for ( p + = CMDHDRSIZE ; * p ! = 0 ; + + p ) ;
2017-09-14 14:19:49 +00:00
// 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.
2019-08-23 14:48:30 +00:00
for ( p + = CMDHDRSIZE ; * p ! = 0 ; + + p ) ;
2017-09-14 14:19:49 +00:00
// 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 characters
void enquecommand ( const char * cmd , bool from_progmem )
{
2018-07-25 19:18:29 +00:00
size_t len = from_progmem ? strlen_P ( cmd ) : strlen ( cmd ) ;
2017-09-14 14:19:49 +00:00
// 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 ;
2018-11-22 16:38:52 +00:00
SERIAL_ECHORPGM ( MSG_Enqueing ) ;
2017-09-14 14:19:49 +00:00
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 ;
2018-11-22 16:38:52 +00:00
SERIAL_ECHORPGM ( MSG_Enqueing ) ;
2017-09-14 14:19:49 +00:00
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 */
}
}
2017-11-07 15:49:04 +00:00
bool cmd_buffer_empty ( )
{
return ( buflen = = 0 ) ;
}
2017-09-14 14:19:49 +00:00
void enquecommand_front ( const char * cmd , bool from_progmem )
{
2018-07-25 19:18:29 +00:00
size_t len = from_progmem ? strlen_P ( cmd ) : strlen ( cmd ) ;
2017-09-14 14:19:49 +00:00
// 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 ;
}
2018-03-14 14:35:39 +00:00
void proc_commands ( ) {
if ( buflen )
{
process_commands ( ) ;
if ( ! cmdbuffer_front_already_processed )
cmdqueue_pop_front ( ) ;
cmdbuffer_front_already_processed = false ;
}
}
2017-09-14 14:19:49 +00:00
void get_command ( )
{
// Test and reserve space for the new command string.
2017-09-21 15:50:39 +00:00
if ( ! cmdqueue_could_enqueue_back ( MAX_CMD_SIZE - 1 , true ) )
return ;
2017-09-14 14:19:49 +00:00
2018-01-15 11:18:21 +00:00
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
}
2018-06-19 14:51:22 +00:00
// start of serial line processing loop
2020-10-26 07:42:26 +00:00
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
2018-06-05 18:28:41 +00:00
2017-09-14 14:19:49 +00:00
char serial_char = MYSERIAL . read ( ) ;
2021-05-30 10:01:11 +00:00
2022-07-04 14:14:23 +00:00
serialTimeoutTimer . start ( ) ;
2017-09-14 14:19:49 +00:00
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 ) {
2018-06-05 18:28:41 +00:00
gcode_N = 0 ;
2017-12-22 21:34:57 +00:00
// Line numbers must be first in buffer
2017-09-14 14:19:49 +00:00
2017-12-22 21:34:57 +00:00
if ( ( strstr ( cmdbuffer + bufindw + CMDHDRSIZE , " PRUSA " ) = = NULL ) & &
( cmdbuffer [ bufindw + CMDHDRSIZE ] = = ' N ' ) ) {
2017-09-14 14:19:49 +00:00
2017-12-22 21:34:57 +00:00
// 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 ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORRPGM ( _n ( " Line Number is not Last Line Number+1, Last Line: " ) ) ; ////MSG_ERR_LINE_NO
2017-12-22 21:34:57 +00:00
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 + + ) ;
2021-09-12 16:42:31 +00:00
if ( code_value_short ( ) ! = ( int16_t ) checksum ) {
2017-12-22 21:34:57 +00:00
SERIAL_ERROR_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORRPGM ( _n ( " checksum mismatch, Last Line: " ) ) ; ////MSG_ERR_CHECKSUM_MISMATCH
2017-12-22 21:34:57 +00:00
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 ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORRPGM ( _n ( " No Checksum with line number, Last Line: " ) ) ; ////MSG_ERR_NO_CHECKSUM
2017-12-22 21:34:57 +00:00
SERIAL_ERRORLN ( gcode_LastN ) ;
FlushSerialRequestResend ( ) ;
serial_count = 0 ;
return ;
}
// Don't parse N again with code_seen('N')
cmdbuffer [ bufindw + CMDHDRSIZE ] = ' $ ' ;
}
// if we don't receive 'N' but still see '*'
if ( ( cmdbuffer [ bufindw + CMDHDRSIZE ] ! = ' N ' ) & & ( cmdbuffer [ bufindw + CMDHDRSIZE ] ! = ' $ ' ) & & ( strchr ( cmdbuffer + bufindw + CMDHDRSIZE , ' * ' ) ! = NULL ) )
2017-09-14 14:19:49 +00:00
{
2017-12-22 21:34:57 +00:00
2017-09-14 14:19:49 +00:00
SERIAL_ERROR_START ;
2019-05-07 10:23:09 +00:00
SERIAL_ERRORRPGM ( _n ( " No Line Number with checksum, Last Line: " ) ) ; ////MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM
2017-09-14 14:19:49 +00:00
SERIAL_ERRORLN ( gcode_LastN ) ;
2018-07-16 17:20:39 +00:00
FlushSerialRequestResend ( ) ;
2017-09-14 14:19:49 +00:00
serial_count = 0 ;
return ;
2017-12-22 21:34:57 +00:00
}
2022-06-29 11:51:57 +00:00
// Handle KILL early, even when Stopped
if ( strcmp ( cmdbuffer + bufindw + CMDHDRSIZE , " M112 " ) = = 0 )
kill ( MSG_M112_KILL , 2 ) ;
// Handle the USB timer
2017-09-14 14:19:49 +00:00
if ( ( strchr_pointer = strchr ( cmdbuffer + bufindw + CMDHDRSIZE , ' G ' ) ) ! = NULL ) {
2022-02-13 21:52:49 +00:00
if ( ! IS_SD_PRINTING ) {
usb_timer . start ( ) ;
}
2022-06-29 11:51:57 +00:00
}
if ( Stopped = = true ) {
// Stopped can be set either during error states (thermal error: cannot continue), or
// when a printer-initiated action is processed. In such case the printer will send to
// the host an action, but cannot know if the action has been processed while new
// commands are being sent. In this situation we just drop the command while issuing
// periodic "busy" messages in the main loop. Since we're not incrementing the received
// line number, a request for resend will happen (if necessary), ensuring we don't skip
// commands whenever Stopped is cleared and processing resumes.
serial_count = 0 ;
return ;
}
// Command is complete: store the current line into buffer, move to the next line.
2017-09-14 14:19:49 +00:00
2018-06-05 18:28:41 +00:00
// Store type of entry
cmdbuffer [ bufindw ] = gcode_N ? CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR : CMDBUFFER_CURRENT_TYPE_USB ;
2022-06-29 11:51:57 +00:00
2017-09-14 14:19:49 +00:00
# ifdef CMDBUFFER_DEBUG
SERIAL_ECHO_START ;
SERIAL_ECHOPGM ( " Storing a command line to buffer: " ) ;
SERIAL_ECHO ( cmdbuffer + bufindw + CMDHDRSIZE ) ;
SERIAL_ECHOLNPGM ( " " ) ;
# endif /* CMDBUFFER_DEBUG */
2022-06-29 11:51:57 +00:00
// Store command itself
2017-09-14 14:19:49 +00:00
bufindw + = strlen ( cmdbuffer + bufindw + CMDHDRSIZE ) + ( 1 + CMDHDRSIZE ) ;
if ( bufindw = = sizeof ( cmdbuffer ) )
bufindw = 0 ;
+ + buflen ;
2022-06-29 11:51:57 +00:00
// Update the processed gcode line
gcode_LastN = gcode_N ;
2017-09-14 14:19:49 +00:00
# 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.
2017-09-21 15:50:39 +00:00
if ( MYSERIAL . available ( ) = = 0 | | ! cmdqueue_could_enqueue_back ( MAX_CMD_SIZE - 1 , true ) )
2017-09-14 14:19:49 +00:00
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
2022-01-18 17:06:35 +00:00
if ( serial_count > 0 & & serialTimeoutTimer . expired ( farm_mode ? 800 : 2000 ) ) {
comment_mode = false ;
serial_count = 0 ;
SERIAL_ECHOLNPGM ( " RX timeout " ) ;
return ;
2017-09-14 14:19:49 +00:00
}
# ifdef SDSUPPORT
2022-07-27 22:40:18 +00:00
if ( ! card . sdprinting | | ! card . isFileOpen ( ) | | serial_count ! = 0 ) {
2017-09-14 14:19:49 +00:00
// 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 ;
2017-09-21 15:50:39 +00:00
union {
struct {
char lo ;
char hi ;
} lohi ;
uint16_t value ;
} sd_count ;
sd_count . value = 0 ;
2017-09-14 14:19:49 +00:00
// Reads whole lines from the SD card. Never leaves a half-filled line in the cmdbuffer.
while ( ! card . eof ( ) & & ! stop_buffering ) {
2021-01-14 12:21:58 +00:00
int16_t n = card . getFilteredGcodeChar ( ) ;
2017-09-14 14:19:49 +00:00
char serial_char = ( char ) n ;
2021-01-11 10:13:40 +00:00
if ( serial_char = = ' \n '
| | serial_char = = ' \r '
2021-02-10 10:18:59 +00:00
| | ( ( serial_char = = ' # ' | | serial_char = = ' : ' ) )
2021-01-11 10:13:40 +00:00
| | serial_count > = ( MAX_CMD_SIZE - 1 )
| | n = = - 1
) {
2017-09-14 14:19:49 +00:00
if ( serial_char = = ' # ' )
stop_buffering = true ;
if ( ! serial_count )
{
2017-09-21 15:50:39 +00:00
// 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.
2021-01-29 07:29:51 +00:00
return ; // prevent cycling indefinitely - let manage_heaters do their job
2017-09-14 14:19:49 +00:00
}
2017-09-21 15:50:39 +00:00
// The new command buffer could be updated non-atomically, because it is not yet considered
// to be inside the active queue.
2021-04-17 22:06:56 +00:00
sd_count . value = card . get_sdpos ( ) - sdpos_atomic ;
2017-09-21 15:50:39 +00:00
cmdbuffer [ bufindw ] = CMDBUFFER_CURRENT_TYPE_SDCARD ;
cmdbuffer [ bufindw + 1 ] = sd_count . lohi . lo ;
cmdbuffer [ bufindw + 2 ] = sd_count . lohi . hi ;
2017-09-14 14:19:49 +00:00
cmdbuffer [ bufindw + serial_count + CMDHDRSIZE ] = 0 ; //terminate string
2017-09-21 15:50:39 +00:00
// Calculate the length before disabling the interrupts.
uint8_t len = strlen ( cmdbuffer + bufindw + CMDHDRSIZE ) + ( 1 + CMDHDRSIZE ) ;
2017-09-22 11:44:10 +00:00
// SERIAL_ECHOPGM("SD cmd(");
// MYSERIAL.print(sd_count.value, DEC);
// SERIAL_ECHOPGM(") ");
// SERIAL_ECHOLN(cmdbuffer+bufindw+CMDHDRSIZE);
2021-02-10 10:18:59 +00:00
// SERIAL_ECHOPGM("cmdbuffer:");
// MYSERIAL.print(cmdbuffer);
// SERIAL_ECHOPGM("buflen:");
// MYSERIAL.print(buflen+1);
2017-09-22 11:44:10 +00:00
sd_count . value = 0 ;
2017-09-21 15:50:39 +00:00
cli ( ) ;
2018-01-20 13:58:30 +00:00
// 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.
2017-09-14 14:19:49 +00:00
+ + buflen ;
2017-09-20 14:04:02 +00:00
bufindw + = len ;
2021-04-17 22:06:56 +00:00
sdpos_atomic = card . get_sdpos ( ) ;
2017-09-14 14:19:49 +00:00
if ( bufindw = = sizeof ( cmdbuffer ) )
bufindw = 0 ;
2017-09-21 15:50:39 +00:00
sei ( ) ;
2017-09-20 14:04:02 +00:00
2017-09-21 15:50:39 +00:00
comment_mode = false ; //for new command
2017-09-14 14:19:49 +00:00
serial_count = 0 ; //clear buffer
2020-10-25 18:12:01 +00:00
if ( card . eof ( ) ) break ;
2021-01-11 10:13:40 +00:00
2017-09-14 14:19:49 +00:00
// The following line will reserve buffer space if available.
2017-09-21 15:50:39 +00:00
if ( ! cmdqueue_could_enqueue_back ( MAX_CMD_SIZE - 1 , true ) )
2017-09-14 14:19:49 +00:00
return ;
}
else
{
2021-01-14 12:21:58 +00:00
// there are no comments coming from the filtered file
cmdbuffer [ bufindw + CMDHDRSIZE + serial_count + + ] = serial_char ;
2017-09-14 14:19:49 +00:00
}
}
2019-10-13 16:49:32 +00:00
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 ( ) )
{
2022-07-27 22:40:18 +00:00
// queue is complete, but before we process EOF commands prevent
// re-entry by disabling SD processing from any st_synchronize call
card . closefile ( ) ;
2019-10-13 16:49:32 +00:00
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 ;
}
}
}
2017-09-14 14:19:49 +00:00
# endif //SDSUPPORT
}
2017-09-18 17:36:18 +00:00
uint16_t cmdqueue_calc_sd_length ( )
{
2017-09-22 11:44:10 +00:00
if ( buflen = = 0 )
return 0 ;
2017-09-21 15:50:39 +00:00
union {
struct {
char lo ;
char hi ;
} lohi ;
uint16_t value ;
2017-09-22 11:44:10 +00:00
} sdlen_single ;
uint16_t sdlen = 0 ;
2018-07-25 19:18:29 +00:00
for ( size_t _buflen = buflen , _bufindr = bufindr ; ; ) {
2017-09-21 15:50:39 +00:00
if ( cmdbuffer [ _bufindr ] = = CMDBUFFER_CURRENT_TYPE_SDCARD ) {
2017-09-22 11:44:10 +00:00
sdlen_single . lohi . lo = cmdbuffer [ _bufindr + 1 ] ;
sdlen_single . lohi . hi = cmdbuffer [ _bufindr + 2 ] ;
sdlen + = sdlen_single . value ;
2017-09-21 15:50:39 +00:00
}
2017-09-22 11:44:10 +00:00
if ( - - _buflen = = 0 )
break ;
// First skip the current command ID and iterate up to the end of the string.
2017-09-21 15:50:39 +00:00
for ( _bufindr + = CMDHDRSIZE ; cmdbuffer [ _bufindr ] ! = 0 ; + + _bufindr ) ;
2017-09-22 11:44:10 +00:00
// Second, skip the end of string null character and iterate until a nonzero command ID is found.
2017-09-21 15:50:39 +00:00
for ( + + _bufindr ; _bufindr < sizeof ( cmdbuffer ) & & cmdbuffer [ _bufindr ] = = 0 ; + + _bufindr ) ;
2017-09-22 11:44:10 +00:00
// 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 ) ;
}
2017-09-21 15:50:39 +00:00
}
2017-09-22 11:44:10 +00:00
return sdlen ;
}