Initial commit. Unusable Marlin 2.0.5.3 core without any custimization.
This commit is contained in:
645
Marlin/src/gcode/queue.cpp
Executable file
645
Marlin/src/gcode/queue.cpp
Executable file
@@ -0,0 +1,645 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* queue.cpp - The G-code command queue
|
||||
*/
|
||||
|
||||
#include "queue.h"
|
||||
GCodeQueue queue;
|
||||
|
||||
#include "gcode.h"
|
||||
|
||||
#include "../lcd/ultralcd.h"
|
||||
#include "../sd/cardreader.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/temperature.h"
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
#if ENABLED(PRINTER_EVENT_LEDS)
|
||||
#include "../feature/leds/printer_event_leds.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(BINARY_FILE_TRANSFER)
|
||||
#include "../feature/binary_protocol.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
#include "../feature/powerloss.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* GCode line number handling. Hosts may opt to include line numbers when
|
||||
* sending commands to Marlin, and lines will be checked for sequentiality.
|
||||
* M110 N<int> sets the current line number.
|
||||
*/
|
||||
long gcode_N, GCodeQueue::last_N;
|
||||
|
||||
/**
|
||||
* GCode Command Queue
|
||||
* 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 NUM_SERIAL > 1
|
||||
int16_t GCodeQueue::port[BUFSIZE];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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 Command pointer. nullptr if no commands are being injected.
|
||||
* Used by Marlin internally to ensure that commands initiated from within
|
||||
* are enqueued ahead of any pending serial or sd card commands.
|
||||
*/
|
||||
static PGM_P injected_commands_P = nullptr;
|
||||
|
||||
GCodeQueue::GCodeQueue() {
|
||||
// Send "ok" after commands by default
|
||||
LOOP_L_N(i, COUNT(send_ok)) send_ok[i] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are any commands yet to be executed
|
||||
*/
|
||||
bool GCodeQueue::has_commands_queued() {
|
||||
return queue.length || injected_commands_P;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 NUM_SERIAL > 1
|
||||
, int16_t p/*=-1*/
|
||||
#endif
|
||||
) {
|
||||
send_ok[index_w] = say_ok;
|
||||
#if NUM_SERIAL > 1
|
||||
port[index_w] = p;
|
||||
#endif
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
recovery.commit_sdpos(index_w);
|
||||
#endif
|
||||
if (++index_w >= BUFSIZE) index_w = 0;
|
||||
length++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a command from RAM into the main command buffer.
|
||||
* Return true if the command was successfully added.
|
||||
* Return false for a full buffer, or if the 'command' is a comment.
|
||||
*/
|
||||
bool GCodeQueue::_enqueue(const char* cmd, bool say_ok/*=false*/
|
||||
#if NUM_SERIAL > 1
|
||||
, int16_t pn/*=-1*/
|
||||
#endif
|
||||
) {
|
||||
if (*cmd == ';' || length >= BUFSIZE) return false;
|
||||
strcpy(command_buffer[index_w], cmd);
|
||||
_commit_command(say_ok
|
||||
#if NUM_SERIAL > 1
|
||||
, pn
|
||||
#endif
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define ISEOL(C) ((C) == '\n' || (C) == '\r')
|
||||
|
||||
/**
|
||||
* Enqueue with Serial Echo
|
||||
* Return true if the command was consumed
|
||||
*/
|
||||
bool GCodeQueue::enqueue_one(const char* cmd) {
|
||||
|
||||
//SERIAL_ECHOPGM("enqueue_one(\"");
|
||||
//SERIAL_ECHO(cmd);
|
||||
//SERIAL_ECHOPGM("\") \n");
|
||||
|
||||
if (*cmd == 0 || ISEOL(*cmd)) return true;
|
||||
|
||||
if (_enqueue(cmd)) {
|
||||
SERIAL_ECHO_MSG(STR_ENQUEUEING, cmd, "\"");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the next "immediate" command.
|
||||
* Return 'true' if any commands were processed,
|
||||
* or remain to process.
|
||||
*/
|
||||
bool GCodeQueue::process_injected_command() {
|
||||
if (injected_commands_P == nullptr) return false;
|
||||
|
||||
char c;
|
||||
size_t i = 0;
|
||||
while ((c = pgm_read_byte(&injected_commands_P[i])) && c != '\n') i++;
|
||||
|
||||
// Extract current command and move pointer to next command
|
||||
char cmd[i + 1];
|
||||
memcpy_P(cmd, injected_commands_P, i);
|
||||
cmd[i] = '\0';
|
||||
injected_commands_P = c ? injected_commands_P + i + 1 : nullptr;
|
||||
|
||||
// Execute command if non-blank
|
||||
if (i) {
|
||||
parser.parse(cmd);
|
||||
gcode.process_parsed_command();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue one or many commands to run from program memory.
|
||||
* Do not inject a comment or use leading spaces!
|
||||
* Aborts the current queue, if any.
|
||||
* Note: process_injected_command() will be called to drain any commands afterwards
|
||||
*/
|
||||
void GCodeQueue::inject_P(PGM_P const pgcode) { injected_commands_P = pgcode; }
|
||||
|
||||
/**
|
||||
* Enqueue and return only when commands are actually enqueued.
|
||||
* Never call this from a G-code handler!
|
||||
*/
|
||||
void GCodeQueue::enqueue_one_now(const char* cmd) { while (!enqueue_one(cmd)) idle(); }
|
||||
|
||||
/**
|
||||
* Attempt to enqueue a single G-code command
|
||||
* and return 'true' if successful.
|
||||
*/
|
||||
bool GCodeQueue::enqueue_one_P(PGM_P const pgcode) {
|
||||
size_t i = 0;
|
||||
PGM_P p = pgcode;
|
||||
char c;
|
||||
while ((c = pgm_read_byte(&p[i])) && c != '\n') i++;
|
||||
char cmd[i + 1];
|
||||
memcpy_P(cmd, p, i);
|
||||
cmd[i] = '\0';
|
||||
return _enqueue(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue from program memory and return only when commands are actually enqueued
|
||||
* Never call this from a G-code handler!
|
||||
*/
|
||||
void GCodeQueue::enqueue_now_P(PGM_P const pgcode) {
|
||||
size_t i = 0;
|
||||
PGM_P p = pgcode;
|
||||
for (;;) {
|
||||
char c;
|
||||
while ((c = pgm_read_byte(&p[i])) && c != '\n') i++;
|
||||
char cmd[i + 1];
|
||||
memcpy_P(cmd, p, i);
|
||||
cmd[i] = '\0';
|
||||
enqueue_one_now(cmd);
|
||||
if (!c) break;
|
||||
p += i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an "ok" message to the host, indicating
|
||||
* that a command was successfully processed.
|
||||
*
|
||||
* If ADVANCED_OK is enabled also include:
|
||||
* N<int> Line number of the command, if any
|
||||
* P<int> Planner space remaining
|
||||
* B<int> Block queue space remaining
|
||||
*/
|
||||
void GCodeQueue::ok_to_send() {
|
||||
#if NUM_SERIAL > 1
|
||||
const int16_t pn = port[index_r];
|
||||
if (pn < 0) return;
|
||||
PORT_REDIRECT(pn); // Reply to the serial port that sent the command
|
||||
#endif
|
||||
if (!send_ok[index_r]) return;
|
||||
SERIAL_ECHOPGM(STR_OK);
|
||||
#if ENABLED(ADVANCED_OK)
|
||||
char* p = command_buffer[index_r];
|
||||
if (*p == 'N') {
|
||||
SERIAL_ECHO(' ');
|
||||
SERIAL_ECHO(*p++);
|
||||
while (NUMERIC_SIGNED(*p))
|
||||
SERIAL_ECHO(*p++);
|
||||
}
|
||||
SERIAL_ECHOPAIR_P(SP_P_STR, int(planner.moves_free()));
|
||||
SERIAL_ECHOPAIR(" B", int(BUFSIZE - length));
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a "Resend: nnn" message to the host to
|
||||
* indicate that a command needs to be re-sent.
|
||||
*/
|
||||
void GCodeQueue::flush_and_request_resend() {
|
||||
#if NUM_SERIAL > 1
|
||||
const int16_t pn = port[index_r];
|
||||
if (pn < 0) return;
|
||||
PORT_REDIRECT(pn); // Reply to the serial port that sent the command
|
||||
#endif
|
||||
SERIAL_FLUSH();
|
||||
SERIAL_ECHOPGM(STR_RESEND);
|
||||
SERIAL_ECHOLN(last_N + 1);
|
||||
ok_to_send();
|
||||
}
|
||||
|
||||
inline bool serial_data_available() {
|
||||
return false
|
||||
|| MYSERIAL0.available()
|
||||
#if NUM_SERIAL > 1
|
||||
|| MYSERIAL1.available()
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
inline int read_serial(const uint8_t index) {
|
||||
switch (index) {
|
||||
case 0: return MYSERIAL0.read();
|
||||
#if NUM_SERIAL > 1
|
||||
case 1: return MYSERIAL1.read();
|
||||
#endif
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeQueue::gcode_line_error(PGM_P const err, const int8_t pn) {
|
||||
PORT_REDIRECT(pn); // Reply to the serial port that sent the command
|
||||
SERIAL_ERROR_START();
|
||||
serialprintPGM(err);
|
||||
SERIAL_ECHOLN(last_N);
|
||||
while (read_serial(pn) != -1); // Clear out the RX buffer
|
||||
flush_and_request_resend();
|
||||
serial_count[pn] = 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool is_M29(const char * const cmd) { // matches "M29" & "M29 ", but not "M290", etc
|
||||
const char * const m29 = strstr_P(cmd, PSTR("M29"));
|
||||
return m29 && !NUMERIC(m29[3]);
|
||||
}
|
||||
|
||||
#define PS_NORMAL 0
|
||||
#define PS_EOL 1
|
||||
#define PS_QUOTED 2
|
||||
#define PS_PAREN 3
|
||||
#define PS_ESC 4
|
||||
|
||||
inline void process_stream_char(const char c, uint8_t &sis, char (&buff)[MAX_CMD_SIZE], int &ind) {
|
||||
|
||||
if (sis == PS_EOL) return; // EOL comment or overflow
|
||||
|
||||
#if ENABLED(PAREN_COMMENTS)
|
||||
else if (sis == PS_PAREN) { // Inline comment
|
||||
if (c == ')') sis = PS_NORMAL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
else if (sis >= PS_ESC) // End escaped char
|
||||
sis -= PS_ESC;
|
||||
|
||||
else if (c == '\\') { // Start escaped char
|
||||
sis += PS_ESC;
|
||||
if (sis == PS_ESC) return; // Keep if quoting
|
||||
}
|
||||
|
||||
#if ENABLED(GCODE_QUOTED_STRINGS)
|
||||
|
||||
else if (sis == PS_QUOTED) {
|
||||
if (c == '"') sis = PS_NORMAL; // End quoted string
|
||||
}
|
||||
else if (c == '"') // Start quoted string
|
||||
sis = PS_QUOTED;
|
||||
|
||||
#endif
|
||||
|
||||
else if (c == ';') { // Start end-of-line comment
|
||||
sis = PS_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLED(PAREN_COMMENTS)
|
||||
else if (c == '(') { // Start inline comment
|
||||
sis = PS_PAREN;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
buff[ind++] = c;
|
||||
if (ind >= MAX_CMD_SIZE - 1)
|
||||
sis = PS_EOL; // Skip the rest on overflow
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a line being completed. For an empty line
|
||||
* keep sensor readings going and watchdog alive.
|
||||
*/
|
||||
inline bool process_line_done(uint8_t &sis, char (&buff)[MAX_CMD_SIZE], int &ind) {
|
||||
sis = PS_NORMAL;
|
||||
buff[ind] = 0;
|
||||
if (ind) { ind = 0; return false; }
|
||||
thermalManager.manage_heater();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all commands waiting on the serial port and queue them.
|
||||
* Exit when the buffer is full or when no more characters are
|
||||
* left on the serial port.
|
||||
*/
|
||||
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 (card.flag.binary_mode) {
|
||||
/**
|
||||
* For binary stream file transfer, use serial_line_buffer as the working
|
||||
* receive buffer (which limits the packet size to MAX_CMD_SIZE).
|
||||
* The receive buffer also limits the packet size for reliable transmission.
|
||||
*/
|
||||
binaryStream[card.transfer_port_index].receive(serial_line_buffer[card.transfer_port_index]);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the command buffer is empty for too long,
|
||||
// send "wait" to indicate Marlin is still waiting.
|
||||
#if NO_TIMEOUTS > 0
|
||||
static millis_t last_command_time = 0;
|
||||
const millis_t ms = millis();
|
||||
if (length == 0 && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
|
||||
SERIAL_ECHOLNPGM(STR_WAIT);
|
||||
last_command_time = ms;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Loop while serial characters are incoming and the queue is not full
|
||||
*/
|
||||
while (length < BUFSIZE && serial_data_available()) {
|
||||
LOOP_L_N(i, NUM_SERIAL) {
|
||||
|
||||
const int c = read_serial(i);
|
||||
if (c < 0) continue;
|
||||
|
||||
const char serial_char = c;
|
||||
|
||||
if (ISEOL(serial_char)) {
|
||||
|
||||
// Reset our state, continue if the line was empty
|
||||
if (process_line_done(serial_input_state[i], serial_line_buffer[i], serial_count[i]))
|
||||
continue;
|
||||
|
||||
char* command = serial_line_buffer[i];
|
||||
|
||||
while (*command == ' ') command++; // Skip leading spaces
|
||||
char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line
|
||||
|
||||
if (npos) {
|
||||
|
||||
bool M110 = strstr_P(command, PSTR("M110")) != nullptr;
|
||||
|
||||
if (M110) {
|
||||
char* n2pos = strchr(command + 4, 'N');
|
||||
if (n2pos) npos = n2pos;
|
||||
}
|
||||
|
||||
gcode_N = strtol(npos + 1, nullptr, 10);
|
||||
|
||||
if (gcode_N != last_N + 1 && !M110)
|
||||
return gcode_line_error(PSTR(STR_ERR_LINE_NO), i);
|
||||
|
||||
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), i);
|
||||
}
|
||||
else
|
||||
return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i);
|
||||
|
||||
last_N = 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), i);
|
||||
#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(i); // 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 (strcmp(command, "M108") == 0) {
|
||||
wait_for_heatup = false;
|
||||
#if HAS_LCD_MENU
|
||||
wait_for_user = false;
|
||||
#endif
|
||||
}
|
||||
if (strcmp(command, "M112") == 0) kill(M112_KILL_STR, nullptr, true);
|
||||
if (strcmp(command, "M410") == 0) quickstop_stepper();
|
||||
#endif
|
||||
|
||||
#if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
|
||||
last_command_time = ms;
|
||||
#endif
|
||||
|
||||
// Add the command to the queue
|
||||
_enqueue(serial_line_buffer[i], true
|
||||
#if NUM_SERIAL > 1
|
||||
, i
|
||||
#endif
|
||||
);
|
||||
}
|
||||
else
|
||||
process_stream_char(serial_char, serial_input_state[i], serial_line_buffer[i], serial_count[i]);
|
||||
|
||||
} // for NUM_SERIAL
|
||||
} // queue has space, serial has data
|
||||
}
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
|
||||
/**
|
||||
* Get lines from the SD Card until the command buffer is full
|
||||
* or until the end of the file is reached. Because this method
|
||||
* always receives complete command-lines, they can go directly
|
||||
* into the main command queue.
|
||||
*/
|
||||
inline void GCodeQueue::get_sdcard_commands() {
|
||||
static uint8_t sd_input_state = PS_NORMAL;
|
||||
|
||||
if (!IS_SD_PRINTING()) return;
|
||||
|
||||
int sd_count = 0;
|
||||
bool card_eof = card.eof();
|
||||
while (length < BUFSIZE && !card_eof) {
|
||||
const int16_t n = card.get();
|
||||
card_eof = card.eof();
|
||||
if (n < 0 && !card_eof) { SERIAL_ERROR_MSG(STR_SD_ERR_READ); continue; }
|
||||
|
||||
const char sd_char = (char)n;
|
||||
const bool is_eol = ISEOL(sd_char);
|
||||
if (is_eol || card_eof) {
|
||||
|
||||
// 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 (!process_line_done(sd_input_state, command_buffer[index_w], sd_count)) {
|
||||
_commit_command(false);
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
recovery.cmd_sdpos = card.getIndex(); // Prime for the NEXT _commit_command
|
||||
#endif
|
||||
}
|
||||
|
||||
if (card_eof) card.fileHasFinished(); // Handle end of file reached
|
||||
}
|
||||
else
|
||||
process_stream_char(sd_char, sd_input_state, command_buffer[index_w], sd_count);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDSUPPORT
|
||||
|
||||
/**
|
||||
* Add to the circular command queue the next command from:
|
||||
* - The command-injection queue (injected_commands_P)
|
||||
* - The active serial input (usually USB)
|
||||
* - The SD card file being actively printed
|
||||
*/
|
||||
void GCodeQueue::get_available_commands() {
|
||||
|
||||
get_serial_commands();
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
get_sdcard_commands();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next command in the queue, optionally log it to SD, then dispatch it
|
||||
*/
|
||||
void GCodeQueue::advance() {
|
||||
|
||||
// Process immediate commands
|
||||
if (process_injected_command()) return;
|
||||
|
||||
// Return if the G-code buffer is empty
|
||||
if (!length) return;
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
|
||||
if (card.flag.saving) {
|
||||
char* command = command_buffer[index_r];
|
||||
if (is_M29(command)) {
|
||||
// M29 closes the file
|
||||
card.closefile();
|
||||
SERIAL_ECHOLNPGM(STR_FILE_SAVED);
|
||||
|
||||
#if !defined(__AVR__) || !defined(USBCON)
|
||||
#if ENABLED(SERIAL_STATS_DROPPED_RX)
|
||||
SERIAL_ECHOLNPAIR("Dropped bytes: ", MYSERIAL0.dropped());
|
||||
#endif
|
||||
|
||||
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
|
||||
SERIAL_ECHOLNPAIR("Max RX Queue Size: ", MYSERIAL0.rxMaxEnqueued());
|
||||
#endif
|
||||
#endif // !defined(__AVR__) || !defined(USBCON)
|
||||
|
||||
ok_to_send();
|
||||
}
|
||||
else {
|
||||
// Write the string from the read buffer to SD
|
||||
card.write_command(command);
|
||||
if (card.flag.logging)
|
||||
gcode.process_next_command(); // The card is saving because it's logging
|
||||
else
|
||||
ok_to_send();
|
||||
}
|
||||
}
|
||||
else
|
||||
gcode.process_next_command();
|
||||
|
||||
#else
|
||||
|
||||
gcode.process_next_command();
|
||||
|
||||
#endif // SDSUPPORT
|
||||
|
||||
// The queue may be reset by a command handler or by code invoked by idle() within a handler
|
||||
--length;
|
||||
if (++index_r >= BUFSIZE) index_r = 0;
|
||||
|
||||
}
|
Reference in New Issue
Block a user