Skip to content

Commit

Permalink
Adding Line Error Resend Control
Browse files Browse the repository at this point in the history
Initial commit with Line Error Resend control to tackle resend loops
with host >> printer high latency allowing GCode in flight.
eg, Octoprint>[wifi]>ESP3D>>Printer

Existing Resend method, clears RX_Buffer, sends request
    Unable to clear consecutive N lines already release from Octoprint
    host(In-flight). Printer receives expecting requested line,
    triggering additional resend request. Printer eventually receives
    correct line, multiple times, triggering further resend requests
    for next line.
  • Loading branch information
silycr committed Nov 10, 2022
1 parent c209626 commit 4f992eb
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
15 changes: 15 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2405,6 +2405,21 @@
//#define SERIAL_XON_XOFF
#endif

/**
* Line Number Error Handler
*
* Add function to delay resend requests for line number
* errors, for Hosts that send N# line number with GCode.
* Prevents resend cycles causing stuttering and failure
* on systems with latency between Host and Printer.
* eg. Octoprint through ESP8266(WIFI) pass-through
*/
#define RESEND_HANDLER
#if ENABLED(RESEND_HANDLER)
#define RESEND_HANDLER_DROP_GCODE 1 //Number of GCode lines to drop before resend request sent to Host. Octprint>>ESP3D; Min 5
//#define RESEND_HANDLER_NOTICE //Send additional details to host terminal
#endif

#if ENABLED(SDSUPPORT)
// Enable this option to collect and display the maximum
// RX queue usage after transferring a file to SD.
Expand Down
58 changes: 55 additions & 3 deletions Marlin/src/gcode/queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ PGMSTR(G28_STR, "G28");

GCodeQueue::SerialState GCodeQueue::serial_state[NUM_SERIAL] = { 0 };
GCodeQueue::RingBuffer GCodeQueue::ring_buffer = { 0 };
#if ENABLED(RESEND_HANDLER)
GCodeQueue::ResendCtrl GCodeQueue::resend_ctrl = { 0 };
#endif

#if NO_TIMEOUTS > 0
static millis_t last_command_time = 0;
Expand Down Expand Up @@ -280,6 +283,25 @@ void GCodeQueue::flush_and_request_resend(const serial_index_t serial_ind) {
SERIAL_ECHOLNPGM(STR_OK);
}

/**
* Notify host a resend has been skipped, with the offending and expected Line number
* Or
* send "ok" to keep comms alive
*/
void GCodeQueue::ln_num_error_notice(const serial_index_t serial_ind, const long host_gcode_N) { //~8ms to send @500000 through ESP8266(ESP3D WIFI)>>Octoprint
#if HAS_MULTI_SERIAL
if (!serial_ind.valid()) return; // Optimization here, skip if the command came from SD or Flash Drive
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
#endif
#if ENABLED(RESEND_HANDLER_NOTICE)
serial_echo_start(); // { serial_print(F("Echo:")); }
SERIAL_ECHOLNPGM("Host sent incorrect line : ", host_gcode_N);
serial_echo_start(); // { serial_print(F("Echo:")); }
SERIAL_ECHOLNPGM("Line expected : ", serial_state[serial_ind.index].last_N + 1);
#endif
SERIAL_ECHOLNPGM(STR_OK); //send Ok to continue action from Host
}

static bool serial_data_available(serial_index_t index) {
const int a = SERIAL_IMPL.available(index);
#if ENABLED(RX_BUFFER_MONITOR) && RX_BUFFER_SIZE
Expand Down Expand Up @@ -467,11 +489,41 @@ void GCodeQueue::get_serial_commands() {
if (n2pos) npos = n2pos;
}

const long gcode_N = strtol(npos + 1, nullptr, 10);

const long gcode_N = strtol(npos + 1, nullptr, 10); //Host sent GCode Line# from RX_buffer

/**
* Resend Handler - Received line # != expected line number
*
* Standard behaviour - Clear RX_Buffer; resend request to host
* Extended behaviour - Allow ignore_resend_max # of errors to be ignored.
* Required when latency present between Marlin>>Host allowing GCode in-flight to cause comms issue
* on resend requests, such as stuttering and print failure.
* Ignore_resend_max should not exceed expected in-flight GCode + RX_Buffer. RX_Buffer is cleared in
* gcode_line_error, not ln_num_error_notice as next serial in buffer may be the required line.
*/
if (gcode_N != serial.last_N + 1 && !M110) {
// In case of error on a serial port, don't prevent other serial port from making progress
gcode_line_error(F(STR_ERR_LINE_NO), p);
#if ENABLED(RESEND_HANDLER)
const serial_index_t serial_ind = p;

if ((resend_ctrl.ignore_resend_count < resend_ctrl.ignore_resend_max - 1)) { //Threshold eliminated resends
if(serial_state[serial_ind.index].last_N != resend_ctrl.last_error_N){ //Is first error instance
ln_num_error_notice(p, gcode_N);
resend_ctrl.last_error_N = serial_state[serial_ind.index].last_N; //Set last ignored error line
resend_ctrl.ignore_resend_count = 0; //Reset count as first instance
}
else {
ln_num_error_notice(p, gcode_N);
}
resend_ctrl.ignore_resend_count += 1; //Capture anything that doesn't fall in prev if_stmt. Nothing should miss
}
else{ //Exceeded maximum deleted requests or is a new resend request
resend_ctrl.ignore_resend_count = 0; //Reset counter
gcode_line_error(F(STR_ERR_LINE_NO), p); //Send resend request
}
#else
gcode_line_error(F(STR_ERR_LINE_NO), p); //Send resend request
#endif
break;
}

Expand Down
15 changes: 15 additions & 0 deletions Marlin/src/gcode/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ class GCodeQueue {
inline char* peek_next_command_string() { return peek_next_command().buffer; }
};

/**
* Resend handler to deal with Host>>Printer latency
*/
#if ENABLED(RESEND_HANDLER)
struct ResendCtrl {
long last_error_N = 0; //Record the last requested resend line number
uint8_t ignore_resend_count = 0;
const uint8_t ignore_resend_max = RESEND_HANDLER_DROP_GCODE; //Number of resends requests deleted //LH this can be constant
};

static ResendCtrl resend_ctrl; //resend ctrl variables

static void ln_num_error_notice(const serial_index_t serial_ind, const long host_gcode_N);
#endif

/**
* The ring buffer of commands
*/
Expand Down

0 comments on commit 4f992eb

Please sign in to comment.