@ -35,6 +35,7 @@ GCodeQueue queue;
# include "../module/planner.h"
# include "../module/planner.h"
# include "../module/temperature.h"
# include "../module/temperature.h"
# include "../MarlinCore.h"
# include "../MarlinCore.h"
# include "../core/bug_on.h"
# if ENABLED(PRINTER_EVENT_LEDS)
# if ENABLED(PRINTER_EVENT_LEDS)
# include "../feature/leds/printer_event_leds.h"
# include "../feature/leds/printer_event_leds.h"
@ -48,10 +49,6 @@ GCodeQueue queue;
# include "../feature/binary_stream.h"
# include "../feature/binary_stream.h"
# endif
# endif
# if ENABLED(MEATPACK)
# include "../feature/meatpack.h"
# endif
# if ENABLED(POWER_LOSS_RECOVERY)
# if ENABLED(POWER_LOSS_RECOVERY)
# include "../feature/powerloss.h"
# include "../feature/powerloss.h"
# endif
# endif
@ -67,44 +64,17 @@ PGMSTR(G28_STR, "G28");
static millis_t last_command_time = 0 ;
static millis_t last_command_time = 0 ;
# endif
# endif
/**
GCodeQueue : : SerialState GCodeQueue : : serial_state [ NUM_SERIAL ] = { 0 } ;
* GCode line number handling . Hosts may opt to include line numbers when
GCodeQueue : : RingBuffer GCodeQueue : : ring_buffer = { 0 } ;
* sending commands to Marlin , and lines will be checked for sequentiality .
* M110 N < int > sets the current line number .
*/
long GCodeQueue : : last_N [ NUM_SERIAL ] ;
/**
# if NO_TIMEOUTS > 0
* GCode Command Queue
static millis_t last_command_time = 0 ;
* A simple ring buffer of BUFSIZE command strings .
*
* Commands are copied into this buffer by the command injectors
* ( immediate , serial , sd card ) and they are processed sequentially by
* the main loop . The gcode . process_next_command method parses the next
* command and hands off execution to individual handler functions .
*/
uint8_t GCodeQueue : : length = 0 , // Count of commands in the queue
GCodeQueue : : index_r = 0 , // Ring buffer read position
GCodeQueue : : index_w = 0 ; // Ring buffer write position
char GCodeQueue : : command_buffer [ BUFSIZE ] [ MAX_CMD_SIZE ] ;
/*
* The port that the command was received on
*/
# if HAS_MULTI_SERIAL
serial_index_t GCodeQueue : : port [ BUFSIZE ] ;
# endif
# endif
/**
/**
* Serial command injection
* Serial command injection
*/
*/
// Number of characters read in the current line of serial input
static int serial_count [ NUM_SERIAL ] = { 0 } ;
bool send_ok [ BUFSIZE ] ;
/**
/**
* Next Injected PROGMEM Command pointer . ( nullptr = = empty )
* Next Injected PROGMEM Command pointer . ( nullptr = = empty )
* Internal commands are enqueued ahead of serial / SD commands .
* Internal commands are enqueued ahead of serial / SD commands .
@ -116,38 +86,16 @@ PGM_P GCodeQueue::injected_commands_P; // = nullptr
*/
*/
char GCodeQueue : : injected_commands [ 64 ] ; // = { 0 }
char GCodeQueue : : injected_commands [ 64 ] ; // = { 0 }
GCodeQueue : : GCodeQueue ( ) {
// Send "ok" after commands by default
LOOP_L_N ( i , COUNT ( send_ok ) ) send_ok [ i ] = true ;
}
/**
void GCodeQueue : : RingBuffer : : commit_command ( bool skip_ok
* Check whether there are any commands yet to be executed
*/
bool GCodeQueue : : has_commands_queued ( ) {
return queue . length | | injected_commands_P | | injected_commands [ 0 ] ;
}
/**
* Clear the Marlin command queue
*/
void GCodeQueue : : clear ( ) {
index_r = index_w = length = 0 ;
}
/**
* Once a new command is in the ring buffer , call this to commit it
*/
void GCodeQueue : : _commit_command ( bool say_ok
# if HAS_MULTI_SERIAL
# if HAS_MULTI_SERIAL
, serial_index_t serial_ind /*=-1*/
, serial_index_t serial_ind /*=-1*/
# endif
# endif
) {
) {
send_ok[ index_w ] = say _ok;
commands [ index_w ] . skip_ok = skip_ok ;
TERN_ ( HAS_MULTI_SERIAL , port[ index_w ] = serial_ind ) ;
TERN_ ( HAS_MULTI_SERIAL , commands [ index_w ] . port = serial_ind ) ;
TERN_ ( POWER_LOSS_RECOVERY , recovery . commit_sdpos ( index_w ) ) ;
TERN_ ( POWER_LOSS_RECOVERY , recovery . commit_sdpos ( index_w ) ) ;
if ( + + index_w > = BUFSIZE ) index_w = 0 ;
advance_pos ( index_w , 1 ) ;
length + + ;
}
}
/**
/**
@ -155,14 +103,14 @@ void GCodeQueue::_commit_command(bool say_ok
* Return true if the command was successfully added .
* Return true if the command was successfully added .
* Return false for a full buffer , or if the ' command ' is a comment .
* Return false for a full buffer , or if the ' command ' is a comment .
*/
*/
bool GCodeQueue : : _enqueue( const char * cmd , bool say_ok /*=fals e*/
bool GCodeQueue : : RingBuffer: : enqueue ( const char * cmd , bool skip_ok /*=tru e*/
# if HAS_MULTI_SERIAL
# if HAS_MULTI_SERIAL
, serial_index_t serial_ind /*=-1*/
, serial_index_t serial_ind /*=-1*/
# endif
# endif
) {
) {
if ( * cmd = = ' ; ' | | length > = BUFSIZE ) return false ;
if ( * cmd = = ' ; ' | | length > = BUFSIZE ) return false ;
strcpy ( command _buffer[ index_w ] , cmd ) ;
strcpy ( command s[ index_w ] . buffer , cmd ) ;
_commit_command( say _ok
commit_command( skip _ok
# if HAS_MULTI_SERIAL
# if HAS_MULTI_SERIAL
, serial_ind
, serial_ind
# endif
# endif
@ -175,14 +123,11 @@ bool GCodeQueue::_enqueue(const char* cmd, bool say_ok/*=false*/
* Return true if the command was consumed
* Return true if the command was consumed
*/
*/
bool GCodeQueue : : enqueue_one ( const char * cmd ) {
bool GCodeQueue : : enqueue_one ( const char * cmd ) {
//SERIAL_ECHOLNPAIR("enqueue_one(\"", cmd, "\")");
//SERIAL_ECHOPGM("enqueue_one(\"");
//SERIAL_ECHO(cmd);
//SERIAL_ECHOPGM("\") \n");
if ( * cmd = = 0 | | ISEOL ( * cmd ) ) return true ;
if ( * cmd = = 0 | | ISEOL ( * cmd ) ) return true ;
if ( _enqueue( cmd ) ) {
if ( ring _buffer. enqueue( cmd ) ) {
SERIAL_ECHO_MSG ( STR_ENQUEUEING , cmd , " \" " ) ;
SERIAL_ECHO_MSG ( STR_ENQUEUEING , cmd , " \" " ) ;
return true ;
return true ;
}
}
@ -260,7 +205,7 @@ bool GCodeQueue::enqueue_one_P(PGM_P const pgcode) {
char cmd [ i + 1 ] ;
char cmd [ i + 1 ] ;
memcpy_P ( cmd , p , i ) ;
memcpy_P ( cmd , p , i ) ;
cmd [ i ] = ' \0 ' ;
cmd [ i ] = ' \0 ' ;
return _ enqueue( cmd ) ;
return ring_buffer . enqueue( cmd ) ;
}
}
/**
/**
@ -291,20 +236,21 @@ void GCodeQueue::enqueue_now_P(PGM_P const pgcode) {
* P < int > Planner space remaining
* P < int > Planner space remaining
* B < int > Block queue space remaining
* B < int > Block queue space remaining
*/
*/
void GCodeQueue : : ok_to_send( ) {
void GCodeQueue : : RingBuffer: : ok_to_send( ) {
# if NO_TIMEOUTS > 0
# if NO_TIMEOUTS > 0
// Start counting from the last command's execution
// Start counting from the last command's execution
last_command_time = millis ( ) ;
last_command_time = millis ( ) ;
# endif
# endif
CommandLine & command = commands [ index_r ] ;
# if HAS_MULTI_SERIAL
# if HAS_MULTI_SERIAL
const serial_index_t serial_ind = command _port( ) ;
const serial_index_t serial_ind = command . port ;
if ( serial_ind < 0 ) return ;
if ( serial_ind < 0 ) return ;
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
# endif
# endif
if ( ! send_ok [ index_r ] ) return ;
if ( command . skip_ok ) return ;
SERIAL_ECHOPGM ( STR_OK ) ;
SERIAL_ECHOPGM ( STR_OK ) ;
# if ENABLED(ADVANCED_OK)
# if ENABLED(ADVANCED_OK)
char * p = command _buffer[ index_r ] ;
char * p = command . buffer ;
if ( * p = = ' N ' ) {
if ( * p = = ' N ' ) {
SERIAL_CHAR ( ' ' , * p + + ) ;
SERIAL_CHAR ( ' ' , * p + + ) ;
while ( NUMERIC_SIGNED ( * p ) )
while ( NUMERIC_SIGNED ( * p ) )
@ -321,27 +267,40 @@ void GCodeQueue::ok_to_send() {
* indicate that a command needs to be re - sent .
* indicate that a command needs to be re - sent .
*/
*/
void GCodeQueue : : flush_and_request_resend ( ) {
void GCodeQueue : : flush_and_request_resend ( ) {
const serial_index_t serial_ind = command_port( ) ;
const serial_index_t serial_ind = ring_buffer. command_port( ) ;
# if HAS_MULTI_SERIAL
# if HAS_MULTI_SERIAL
if ( serial_ind < 0 ) return ; // Never mind. Command came from SD or Flash Drive
if ( serial_ind < 0 ) return ; // Never mind. Command came from SD or Flash Drive
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
# endif
# endif
SERIAL_FLUSH ( ) ;
SERIAL_FLUSH ( ) ;
SERIAL_ECHOPGM ( STR_RESEND ) ;
SERIAL_ECHOPGM ( STR_RESEND ) ;
SERIAL_ECHOLN ( last_N [ serial_ind ] + 1 ) ;
SERIAL_ECHOLN ( serial_state [ serial_ind ] . last_N + 1 ) ;
ok_to_send ( ) ;
}
}
// Multiserial already handle the dispatch to/from multiple port by itself
// Multiserial already handle the dispatch to/from multiple port by itself
inline bool serial_data_available ( uint8_t index = SERIAL_ALL ) {
inline bool serial_data_available ( uint8_t index = SERIAL_ALL ) {
if ( index = = SERIAL_ALL ) {
if ( index = = SERIAL_ALL ) {
for ( index = 0 ; index < NUM_SERIAL ; index + + ) {
for ( index = 0 ; index < NUM_SERIAL ; index + + ) {
if ( SERIAL_IMPL . available ( index ) > 0 ) return true ;
const int a = SERIAL_IMPL . available ( index ) ;
# if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
if ( a > RX_BUFFER_SIZE - 2 ) {
PORT_REDIRECT ( SERIAL_PORTMASK ( index ) ) ;
SERIAL_ERROR_MSG ( " RX BUF overflow, increase RX_BUFFER_SIZE: " , a ) ;
}
# endif
if ( a > 0 ) return true ;
}
}
return false ;
return false ;
}
}
return SERIAL_IMPL . available ( index ) > 0 ;
const int a = SERIAL_IMPL . available ( index ) ;
# if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
if ( a > RX_BUFFER_SIZE - 2 ) {
PORT_REDIRECT ( SERIAL_PORTMASK ( index ) ) ;
SERIAL_ERROR_MSG ( " RX BUF overflow, increase RX_BUFFER_SIZE: " , a ) ;
}
# endif
return a > 0 ;
}
}
inline int read_serial ( const uint8_t index ) { return SERIAL_IMPL . read ( index ) ; }
inline int read_serial ( const uint8_t index ) { return SERIAL_IMPL . read ( index ) ; }
@ -349,11 +308,11 @@ inline int read_serial(const uint8_t index) { return SERIAL_IMPL.read(index); }
void GCodeQueue : : gcode_line_error ( PGM_P const err , const serial_index_t serial_ind ) {
void GCodeQueue : : gcode_line_error ( PGM_P const err , const serial_index_t serial_ind ) {
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
PORT_REDIRECT ( SERIAL_PORTMASK ( serial_ind ) ) ; // Reply to the serial port that sent the command
SERIAL_ERROR_START ( ) ;
SERIAL_ERROR_START ( ) ;
serialprintPGM ( err ) ;
SERIAL_ECHOPGM_P ( err ) ;
SERIAL_ECHOLN ( last_N[ serial_ind ] ) ;
SERIAL_ECHOLN ( serial_state[ serial_ind ] . last_N ) ;
while ( read_serial ( serial_ind ) ! = - 1 ) ; // Clear out the RX buffer
while ( read_serial ( serial_ind ) ! = - 1 ) { /* nada */ } // Clear out the RX buffer. Why don't use flush here ?
flush_and_request_resend ( ) ;
flush_and_request_resend ( ) ;
serial_ count[ serial_ind ] = 0 ;
serial_ state[ serial_ind ] . count = 0 ;
}
}
FORCE_INLINE bool is_M29 ( const char * const cmd ) { // matches "M29" & "M29 ", but not "M290", etc
FORCE_INLINE bool is_M29 ( const char * const cmd ) { // matches "M29" & "M29 ", but not "M290", etc
@ -440,10 +399,6 @@ inline bool process_line_done(uint8_t &sis, char (&buff)[MAX_CMD_SIZE], int &ind
* left on the serial port .
* left on the serial port .
*/
*/
void GCodeQueue : : get_serial_commands ( ) {
void GCodeQueue : : get_serial_commands ( ) {
static char serial_line_buffer [ NUM_SERIAL ] [ MAX_CMD_SIZE ] ;
static uint8_t serial_input_state [ NUM_SERIAL ] = { PS_NORMAL } ;
# if ENABLED(BINARY_FILE_TRANSFER)
# if ENABLED(BINARY_FILE_TRANSFER)
if ( card . flag . binary_mode ) {
if ( card . flag . binary_mode ) {
/**
/**
@ -451,7 +406,7 @@ void GCodeQueue::get_serial_commands() {
* receive buffer ( which limits the packet size to MAX_CMD_SIZE ) .
* receive buffer ( which limits the packet size to MAX_CMD_SIZE ) .
* The receive buffer also limits the packet size for reliable transmission .
* The receive buffer also limits the packet size for reliable transmission .
*/
*/
binaryStream [ card . transfer_port_index ] . receive ( serial_ line_buffer [ card . transfer_port_index ] ) ;
binaryStream [ card . transfer_port_index ] . receive ( serial_ state [ card . transfer_port_index ] . line_buffer ) ;
return ;
return ;
}
}
# endif
# endif
@ -460,122 +415,140 @@ void GCodeQueue::get_serial_commands() {
// send "wait" to indicate Marlin is still waiting.
// send "wait" to indicate Marlin is still waiting.
# if NO_TIMEOUTS > 0
# if NO_TIMEOUTS > 0
const millis_t ms = millis ( ) ;
const millis_t ms = millis ( ) ;
if ( length = = 0 & & ! serial_data_available ( ) & & ELAPSED ( ms , last_command_time + NO_TIMEOUTS ) ) {
if ( ring_buffer. empty ( ) & & ! serial_data_available ( ) & & ELAPSED ( ms , last_command_time + NO_TIMEOUTS ) ) {
SERIAL_ECHOLNPGM ( STR_WAIT ) ;
SERIAL_ECHOLNPGM ( STR_WAIT ) ;
last_command_time = ms ;
last_command_time = ms ;
}
}
# endif
# endif
/**
// Loop while serial characters are incoming and the queue is not full
* Loop while serial characters are incoming and the queue is not full
for ( bool hadData = true ; hadData ; ) {
*/
// Unless a serial port has data, this will exit on next iteration
while ( length < BUFSIZE & & serial_data_available ( ) ) {
hadData = false ;
LOOP_L_N ( p , NUM_SERIAL ) {
LOOP_L_N ( p , NUM_SERIAL ) {
// Check if the queue is full and exit if it is.
if ( ring_buffer . full ( ) ) return ;
// No data for this port ? Skip it
if ( ! serial_data_available ( p ) ) continue ;
// Ok, we have some data to process, let's make progress here
hadData = true ;
const int c = read_serial ( p ) ;
const int c = read_serial ( p ) ;
if ( c < 0 ) continue ;
if ( c < 0 ) {
// This should never happen, let's log it
PORT_REDIRECT ( SERIAL_PORTMASK ( p ) ) ; // Reply to the serial port that sent the command
// Crash here to get more information why it failed
BUG_ON ( " SP available but read -1 " ) ;
SERIAL_ERROR_MSG ( STR_ERR_SERIAL_MISMATCH ) ;
SERIAL_FLUSH ( ) ;
continue ;
}
# if ENABLED(MEATPACK)
const char serial_char = ( char ) c ;
meatpack . handle_rx_char ( uint8_t ( c ) , p ) ;
SerialState & serial = serial_state [ p ] ;
char c_res [ 2 ] = { 0 , 0 } ;
const uint8_t char_count = meatpack . get_result_char ( c_res ) ;
# else
constexpr uint8_t char_count = 1 ;
# endif
LOOP_L_N ( char_index , char_count ) {
if ( ISEOL ( serial_char ) ) {
const char serial_char = TERN ( MEATPACK , c_res [ char_index ] , c ) ;
if ( ISEOL ( serial_char ) ) {
// Reset our state, continue if the line was empty
if ( process_line_done ( serial . input_state , serial . line_buffer , serial . count ) )
continue ;
// Reset our state, continue if the line was empty
char * command = serial . line_buffer ;
if ( process_line_done ( serial_input_state [ p ] , serial_line_buffer [ p ] , serial_count [ p ] ) )
continue ;
char * command = serial_line_buffer [ p ] ;
while ( * command = = ' ' ) command + + ; // Skip leading spaces
char * npos = ( * command = = ' N ' ) ? command : nullptr ; // Require the N parameter to start the line
while ( * command = = ' ' ) command + + ; // Skip leading spaces
if ( npos ) {
char * npos = ( * command = = ' N ' ) ? command : nullptr ; // Require the N parameter to start the line
if ( npos ) {
const bool M110 = ! ! strstr_P ( command , PSTR ( " M110 " ) ) ;
const bool M110 = ! ! strstr_P ( command , PSTR ( " M110 " ) ) ;
if ( M110 ) {
char * n2pos = strchr ( command + 4 , ' N ' ) ;
if ( M110 ) {
if ( n2pos ) npos = n2pos ;
char * n2pos = strchr ( command + 4 , ' N ' ) ;
if ( n2pos ) npos = n2pos ;
}
const long gcode_N = strtol ( npos + 1 , nullptr , 10 ) ;
if ( gcode_N ! = last_N [ p ] + 1 & & ! M110 )
return gcode_line_error ( PSTR ( STR_ERR_LINE_NO ) , p ) ;
char * apos = strrchr ( command , ' * ' ) ;
if ( apos ) {
uint8_t checksum = 0 , count = uint8_t ( apos - command ) ;
while ( count ) checksum ^ = command [ - - count ] ;
if ( strtol ( apos + 1 , nullptr , 10 ) ! = checksum )
return gcode_line_error ( PSTR ( STR_ERR_CHECKSUM_MISMATCH ) , p ) ;
}
else
return gcode_line_error ( PSTR ( STR_ERR_NO_CHECKSUM ) , p ) ;
last_N [ p ] = gcode_N ;
}
# if ENABLED(SDSUPPORT)
// Pronterface "M29" and "M29 " has no line number
else if ( card . flag . saving & & ! is_M29 ( command ) )
return gcode_line_error ( PSTR ( STR_ERR_NO_CHECKSUM ) , p ) ;
# endif
//
// Movement commands give an alert when the machine is stopped
//
if ( IsStopped ( ) ) {
char * gpos = strchr ( command , ' G ' ) ;
if ( gpos ) {
switch ( strtol ( gpos + 1 , nullptr , 10 ) ) {
case 0 : case 1 :
# if ENABLED(ARC_SUPPORT)
case 2 : case 3 :
# endif
# if ENABLED(BEZIER_CURVE_SUPPORT)
case 5 :
# endif
PORT_REDIRECT ( SERIAL_PORTMASK ( p ) ) ; // Reply to the serial port that sent the command
SERIAL_ECHOLNPGM ( STR_ERR_STOPPED ) ;
LCD_MESSAGEPGM ( MSG_STOPPED ) ;
break ;
}
}
}
}
# if DISABLED(EMERGENCY_PARSER)
const long gcode_N = strtol ( npos + 1 , nullptr , 10 ) ;
// Process critical commands early
if ( command [ 0 ] = = ' M ' ) switch ( command [ 3 ] ) {
if ( gcode_N ! = serial . last_N + 1 & & ! M110 ) {
case ' 8 ' : if ( command [ 2 ] = = ' 0 ' & & command [ 1 ] = = ' 1 ' ) { wait_for_heatup = false ; TERN_ ( HAS_LCD_MENU , wait_for_user = false ) ; } break ;
// In case of error on a serial port, don't prevent other serial port from making progress
case ' 2 ' : if ( command [ 2 ] = = ' 1 ' & & command [ 1 ] = = ' 1 ' ) kill ( M112_KILL_STR , nullptr , true ) ; break ;
gcode_line_error ( PSTR ( STR_ERR_LINE_NO ) , p ) ;
case ' 0 ' : if ( command [ 1 ] = = ' 4 ' & & command [ 2 ] = = ' 1 ' ) quickstop_stepper ( ) ; break ;
break ;
}
char * apos = strrchr ( command , ' * ' ) ;
if ( apos ) {
uint8_t checksum = 0 , count = uint8_t ( apos - command ) ;
while ( count ) checksum ^ = command [ - - count ] ;
if ( strtol ( apos + 1 , nullptr , 10 ) ! = checksum ) {
// In case of error on a serial port, don't prevent other serial port from making progress
gcode_line_error ( PSTR ( STR_ERR_CHECKSUM_MISMATCH ) , p ) ;
break ;
}
}
# endif
}
else {
// In case of error on a serial port, don't prevent other serial port from making progress
gcode_line_error ( PSTR ( STR_ERR_NO_CHECKSUM ) , p ) ;
break ;
}
# if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
serial . last_N = gcode_N ;
last_command_time = ms ;
# endif
// Add the command to the queue
_enqueue ( serial_line_buffer [ p ] , true
# if HAS_MULTI_SERIAL
, p
# endif
) ;
}
}
else
# if ENABLED(SDSUPPORT)
process_stream_char ( serial_char , serial_input_state [ p ] , serial_line_buffer [ p ] , serial_count [ p ] ) ;
// Pronterface "M29" and "M29 " has no line number
else if ( card . flag . saving & & ! is_M29 ( command ) ) {
gcode_line_error ( PSTR ( STR_ERR_NO_CHECKSUM ) , p ) ;
break ;
}
# endif
} // char_count loop
//
// Movement commands give an alert when the machine is stopped
//
if ( IsStopped ( ) ) {
char * gpos = strchr ( command , ' G ' ) ;
if ( gpos ) {
switch ( strtol ( gpos + 1 , nullptr , 10 ) ) {
case 0 : case 1 :
# if ENABLED(ARC_SUPPORT)
case 2 : case 3 :
# endif
# if ENABLED(BEZIER_CURVE_SUPPORT)
case 5 :
# endif
PORT_REDIRECT ( SERIAL_PORTMASK ( p ) ) ; // Reply to the serial port that sent the command
SERIAL_ECHOLNPGM ( STR_ERR_STOPPED ) ;
LCD_MESSAGEPGM ( MSG_STOPPED ) ;
break ;
}
}
}
# if DISABLED(EMERGENCY_PARSER)
// Process critical commands early
if ( command [ 0 ] = = ' M ' ) switch ( command [ 3 ] ) {
case ' 8 ' : if ( command [ 2 ] = = ' 0 ' & & command [ 1 ] = = ' 1 ' ) { wait_for_heatup = false ; TERN_ ( HAS_LCD_MENU , wait_for_user = false ) ; } break ;
case ' 2 ' : if ( command [ 2 ] = = ' 1 ' & & command [ 1 ] = = ' 1 ' ) kill ( M112_KILL_STR , nullptr , true ) ; break ;
case ' 0 ' : if ( command [ 1 ] = = ' 4 ' & & command [ 2 ] = = ' 1 ' ) quickstop_stepper ( ) ; break ;
}
# endif
# if NO_TIMEOUTS > 0
last_command_time = ms ;
# endif
// Add the command to the queue
ring_buffer . enqueue ( serial . line_buffer , false
# if HAS_MULTI_SERIAL
, p
# endif
) ;
}
else
process_stream_char ( serial_char , serial . input_state , serial . line_buffer , serial . count ) ;
} // NUM_SERIAL loop
} // NUM_SERIAL loop
} // queue has space, serial has data
} // queue has space, serial has data
@ -595,33 +568,35 @@ void GCodeQueue::get_serial_commands() {
if ( ! IS_SD_PRINTING ( ) ) return ;
if ( ! IS_SD_PRINTING ( ) ) return ;
int sd_count = 0 ;
int sd_count = 0 ;
while ( length < BUFSIZE & & ! card . eof ( ) ) {
while ( ! ring_buffer . full ( ) & & ! card . eof ( ) ) {
const int16_t n = card . get ( ) ;
const int16_t n = card . get ( ) ;
const bool card_eof = card . eof ( ) ;
const bool card_eof = card . eof ( ) ;
if ( n < 0 & & ! card_eof ) { SERIAL_ERROR_MSG ( STR_SD_ERR_READ ) ; continue ; }
if ( n < 0 & & ! card_eof ) { SERIAL_ERROR_MSG ( STR_SD_ERR_READ ) ; continue ; }
CommandLine & command = ring_buffer . commands [ ring_buffer . index_w ] ;
const char sd_char = ( char ) n ;
const char sd_char = ( char ) n ;
const bool is_eol = ISEOL ( sd_char ) ;
const bool is_eol = ISEOL ( sd_char ) ;
if ( is_eol | | card_eof ) {
if ( is_eol | | card_eof ) {
// Reset stream state, terminate the buffer, and commit a non-empty command
// Reset stream state, terminate the buffer, and commit a non-empty command
if ( ! is_eol & & sd_count ) + + sd_count ; // End of file with no newline
if ( ! is_eol & & sd_count ) + + sd_count ; // End of file with no newline
if ( ! process_line_done ( sd_input_state , command _buffer[ index_w ] , sd_count ) ) {
if ( ! process_line_done ( sd_input_state , command . buffer , sd_count ) ) {
// M808 L saves the sdpos of the next line. M808 loops to a new sdpos.
// M808 L saves the sdpos of the next line. M808 loops to a new sdpos.
TERN_ ( GCODE_REPEAT_MARKERS , repeat . early_parse_M808 ( command _buffer[ index_w ] ) ) ;
TERN_ ( GCODE_REPEAT_MARKERS , repeat . early_parse_M808 ( command . buffer ) ) ;
// Put the new command into the buffer (no "ok" sent)
// Put the new command into the buffer (no "ok" sent)
_commit_command( fals e) ;
ring_buffer. commit_command ( tru e) ;
// Prime Power-Loss Recovery for the NEXT _ commit_command
// Prime Power-Loss Recovery for the NEXT commit_command
TERN_ ( POWER_LOSS_RECOVERY , recovery . cmd_sdpos = card . getIndex ( ) ) ;
TERN_ ( POWER_LOSS_RECOVERY , recovery . cmd_sdpos = card . getIndex ( ) ) ;
}
}
if ( card . eof ( ) ) card . fileHasFinished ( ) ; // Handle end of file reached
if ( card . eof ( ) ) card . fileHasFinished ( ) ; // Handle end of file reached
}
}
else
else
process_stream_char ( sd_char , sd_input_state , command _buffer[ index_w ] , sd_count ) ;
process_stream_char ( sd_char , sd_input_state , command . buffer , sd_count ) ;
}
}
}
}
@ -634,6 +609,7 @@ void GCodeQueue::get_serial_commands() {
* - The SD card file being actively printed
* - The SD card file being actively printed
*/
*/
void GCodeQueue : : get_available_commands ( ) {
void GCodeQueue : : get_available_commands ( ) {
if ( ring_buffer . full ( ) ) return ;
get_serial_commands ( ) ;
get_serial_commands ( ) ;
@ -649,13 +625,13 @@ void GCodeQueue::advance() {
if ( process_injected_command_P ( ) | | process_injected_command ( ) ) return ;
if ( process_injected_command_P ( ) | | process_injected_command ( ) ) return ;
// Return if the G-code buffer is empty
// Return if the G-code buffer is empty
if ( ! length ) return ;
if ( ring_buffer . empty ( ) ) return ;
# if ENABLED(SDSUPPORT)
# if ENABLED(SDSUPPORT)
if ( card . flag . saving ) {
if ( card . flag . saving ) {
char * command = command_buffer [ index_r ] ;
char * const cmd = ring_buffer . peek_next_command_string ( ) ;
if ( is_M29 ( c o mman d) ) {
if ( is_M29 ( c md) ) {
// M29 closes the file
// M29 closes the file
card . closefile ( ) ;
card . closefile ( ) ;
SERIAL_ECHOLNPGM ( STR_FILE_SAVED ) ;
SERIAL_ECHOLNPGM ( STR_FILE_SAVED ) ;
@ -673,7 +649,7 @@ void GCodeQueue::advance() {
}
}
else {
else {
// Write the string from the read buffer to SD
// Write the string from the read buffer to SD
card . write_command ( c o mman d) ;
card . write_command ( c md) ;
if ( card . flag . logging )
if ( card . flag . logging )
gcode . process_next_command ( ) ; // The card is saving because it's logging
gcode . process_next_command ( ) ; // The card is saving because it's logging
else
else
@ -690,7 +666,5 @@ void GCodeQueue::advance() {
# endif // SDSUPPORT
# endif // SDSUPPORT
// The queue may be reset by a command handler or by code invoked by idle() within a handler
// The queue may be reset by a command handler or by code invoked by idle() within a handler
- - length ;
ring_buffer . advance_pos ( ring_buffer . index_r , - 1 ) ;
if ( + + index_r > = BUFSIZE ) index_r = 0 ;
}
}