|
@@ -35,6 +35,7 @@ GCodeQueue queue;
|
35
|
35
|
#include "../module/planner.h"
|
36
|
36
|
#include "../module/temperature.h"
|
37
|
37
|
#include "../MarlinCore.h"
|
|
38
|
+#include "../core/bug_on.h"
|
38
|
39
|
|
39
|
40
|
#if ENABLED(PRINTER_EVENT_LEDS)
|
40
|
41
|
#include "../feature/leds/printer_event_leds.h"
|
|
@@ -48,10 +49,6 @@ GCodeQueue queue;
|
48
|
49
|
#include "../feature/binary_stream.h"
|
49
|
50
|
#endif
|
50
|
51
|
|
51
|
|
-#if ENABLED(MEATPACK)
|
52
|
|
- #include "../feature/meatpack.h"
|
53
|
|
-#endif
|
54
|
|
-
|
55
|
52
|
#if ENABLED(POWER_LOSS_RECOVERY)
|
56
|
53
|
#include "../feature/powerloss.h"
|
57
|
54
|
#endif
|
|
@@ -67,44 +64,17 @@ PGMSTR(G28_STR, "G28");
|
67
|
64
|
static millis_t last_command_time = 0;
|
68
|
65
|
#endif
|
69
|
66
|
|
70
|
|
-/**
|
71
|
|
- * GCode line number handling. Hosts may opt to include line numbers when
|
72
|
|
- * sending commands to Marlin, and lines will be checked for sequentiality.
|
73
|
|
- * M110 N<int> sets the current line number.
|
74
|
|
- */
|
75
|
|
-long GCodeQueue::last_N[NUM_SERIAL];
|
76
|
|
-
|
77
|
|
-/**
|
78
|
|
- * GCode Command Queue
|
79
|
|
- * A simple ring buffer of BUFSIZE command strings.
|
80
|
|
- *
|
81
|
|
- * Commands are copied into this buffer by the command injectors
|
82
|
|
- * (immediate, serial, sd card) and they are processed sequentially by
|
83
|
|
- * the main loop. The gcode.process_next_command method parses the next
|
84
|
|
- * command and hands off execution to individual handler functions.
|
85
|
|
- */
|
86
|
|
-uint8_t GCodeQueue::length = 0, // Count of commands in the queue
|
87
|
|
- GCodeQueue::index_r = 0, // Ring buffer read position
|
88
|
|
- GCodeQueue::index_w = 0; // Ring buffer write position
|
89
|
|
-
|
90
|
|
-char GCodeQueue::command_buffer[BUFSIZE][MAX_CMD_SIZE];
|
|
67
|
+GCodeQueue::SerialState GCodeQueue::serial_state[NUM_SERIAL] = { 0 };
|
|
68
|
+GCodeQueue::RingBuffer GCodeQueue::ring_buffer = { 0 };
|
91
|
69
|
|
92
|
|
-/*
|
93
|
|
- * The port that the command was received on
|
94
|
|
- */
|
95
|
|
-#if HAS_MULTI_SERIAL
|
96
|
|
- serial_index_t GCodeQueue::port[BUFSIZE];
|
|
70
|
+#if NO_TIMEOUTS > 0
|
|
71
|
+ static millis_t last_command_time = 0;
|
97
|
72
|
#endif
|
98
|
73
|
|
99
|
74
|
/**
|
100
|
75
|
* Serial command injection
|
101
|
76
|
*/
|
102
|
77
|
|
103
|
|
-// Number of characters read in the current line of serial input
|
104
|
|
-static int serial_count[NUM_SERIAL] = { 0 };
|
105
|
|
-
|
106
|
|
-bool send_ok[BUFSIZE];
|
107
|
|
-
|
108
|
78
|
/**
|
109
|
79
|
* Next Injected PROGMEM Command pointer. (nullptr == empty)
|
110
|
80
|
* Internal commands are enqueued ahead of serial / SD commands.
|
|
@@ -116,38 +86,16 @@ PGM_P GCodeQueue::injected_commands_P; // = nullptr
|
116
|
86
|
*/
|
117
|
87
|
char GCodeQueue::injected_commands[64]; // = { 0 }
|
118
|
88
|
|
119
|
|
-GCodeQueue::GCodeQueue() {
|
120
|
|
- // Send "ok" after commands by default
|
121
|
|
- LOOP_L_N(i, COUNT(send_ok)) send_ok[i] = true;
|
122
|
|
-}
|
123
|
|
-
|
124
|
|
-/**
|
125
|
|
- * Check whether there are any commands yet to be executed
|
126
|
|
- */
|
127
|
|
-bool GCodeQueue::has_commands_queued() {
|
128
|
|
- return queue.length || injected_commands_P || injected_commands[0];
|
129
|
|
-}
|
130
|
|
-
|
131
|
|
-/**
|
132
|
|
- * Clear the Marlin command queue
|
133
|
|
- */
|
134
|
|
-void GCodeQueue::clear() {
|
135
|
|
- index_r = index_w = length = 0;
|
136
|
|
-}
|
137
|
89
|
|
138
|
|
-/**
|
139
|
|
- * Once a new command is in the ring buffer, call this to commit it
|
140
|
|
- */
|
141
|
|
-void GCodeQueue::_commit_command(bool say_ok
|
|
90
|
+void GCodeQueue::RingBuffer::commit_command(bool skip_ok
|
142
|
91
|
#if HAS_MULTI_SERIAL
|
143
|
92
|
, serial_index_t serial_ind/*=-1*/
|
144
|
93
|
#endif
|
145
|
94
|
) {
|
146
|
|
- send_ok[index_w] = say_ok;
|
147
|
|
- TERN_(HAS_MULTI_SERIAL, port[index_w] = serial_ind);
|
|
95
|
+ commands[index_w].skip_ok = skip_ok;
|
|
96
|
+ TERN_(HAS_MULTI_SERIAL, commands[index_w].port = serial_ind);
|
148
|
97
|
TERN_(POWER_LOSS_RECOVERY, recovery.commit_sdpos(index_w));
|
149
|
|
- if (++index_w >= BUFSIZE) index_w = 0;
|
150
|
|
- length++;
|
|
98
|
+ advance_pos(index_w, 1);
|
151
|
99
|
}
|
152
|
100
|
|
153
|
101
|
/**
|
|
@@ -155,14 +103,14 @@ void GCodeQueue::_commit_command(bool say_ok
|
155
|
103
|
* Return true if the command was successfully added.
|
156
|
104
|
* Return false for a full buffer, or if the 'command' is a comment.
|
157
|
105
|
*/
|
158
|
|
-bool GCodeQueue::_enqueue(const char* cmd, bool say_ok/*=false*/
|
|
106
|
+bool GCodeQueue::RingBuffer::enqueue(const char* cmd, bool skip_ok/*=true*/
|
159
|
107
|
#if HAS_MULTI_SERIAL
|
160
|
108
|
, serial_index_t serial_ind/*=-1*/
|
161
|
109
|
#endif
|
162
|
110
|
) {
|
163
|
111
|
if (*cmd == ';' || length >= BUFSIZE) return false;
|
164
|
|
- strcpy(command_buffer[index_w], cmd);
|
165
|
|
- _commit_command(say_ok
|
|
112
|
+ strcpy(commands[index_w].buffer, cmd);
|
|
113
|
+ commit_command(skip_ok
|
166
|
114
|
#if HAS_MULTI_SERIAL
|
167
|
115
|
, serial_ind
|
168
|
116
|
#endif
|
|
@@ -175,14 +123,11 @@ bool GCodeQueue::_enqueue(const char* cmd, bool say_ok/*=false*/
|
175
|
123
|
* Return true if the command was consumed
|
176
|
124
|
*/
|
177
|
125
|
bool GCodeQueue::enqueue_one(const char* cmd) {
|
178
|
|
-
|
179
|
|
- //SERIAL_ECHOPGM("enqueue_one(\"");
|
180
|
|
- //SERIAL_ECHO(cmd);
|
181
|
|
- //SERIAL_ECHOPGM("\") \n");
|
|
126
|
+ //SERIAL_ECHOLNPAIR("enqueue_one(\"", cmd, "\")");
|
182
|
127
|
|
183
|
128
|
if (*cmd == 0 || ISEOL(*cmd)) return true;
|
184
|
129
|
|
185
|
|
- if (_enqueue(cmd)) {
|
|
130
|
+ if (ring_buffer.enqueue(cmd)) {
|
186
|
131
|
SERIAL_ECHO_MSG(STR_ENQUEUEING, cmd, "\"");
|
187
|
132
|
return true;
|
188
|
133
|
}
|
|
@@ -260,7 +205,7 @@ bool GCodeQueue::enqueue_one_P(PGM_P const pgcode) {
|
260
|
205
|
char cmd[i + 1];
|
261
|
206
|
memcpy_P(cmd, p, i);
|
262
|
207
|
cmd[i] = '\0';
|
263
|
|
- return _enqueue(cmd);
|
|
208
|
+ return ring_buffer.enqueue(cmd);
|
264
|
209
|
}
|
265
|
210
|
|
266
|
211
|
/**
|
|
@@ -291,20 +236,21 @@ void GCodeQueue::enqueue_now_P(PGM_P const pgcode) {
|
291
|
236
|
* P<int> Planner space remaining
|
292
|
237
|
* B<int> Block queue space remaining
|
293
|
238
|
*/
|
294
|
|
-void GCodeQueue::ok_to_send() {
|
|
239
|
+void GCodeQueue::RingBuffer::ok_to_send() {
|
295
|
240
|
#if NO_TIMEOUTS > 0
|
296
|
241
|
// Start counting from the last command's execution
|
297
|
242
|
last_command_time = millis();
|
298
|
243
|
#endif
|
|
244
|
+ CommandLine &command = commands[index_r];
|
299
|
245
|
#if HAS_MULTI_SERIAL
|
300
|
|
- const serial_index_t serial_ind = command_port();
|
|
246
|
+ const serial_index_t serial_ind = command.port;
|
301
|
247
|
if (serial_ind < 0) return;
|
302
|
248
|
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
|
303
|
249
|
#endif
|
304
|
|
- if (!send_ok[index_r]) return;
|
|
250
|
+ if (command.skip_ok) return;
|
305
|
251
|
SERIAL_ECHOPGM(STR_OK);
|
306
|
252
|
#if ENABLED(ADVANCED_OK)
|
307
|
|
- char* p = command_buffer[index_r];
|
|
253
|
+ char* p = command.buffer;
|
308
|
254
|
if (*p == 'N') {
|
309
|
255
|
SERIAL_CHAR(' ', *p++);
|
310
|
256
|
while (NUMERIC_SIGNED(*p))
|
|
@@ -321,27 +267,40 @@ void GCodeQueue::ok_to_send() {
|
321
|
267
|
* indicate that a command needs to be re-sent.
|
322
|
268
|
*/
|
323
|
269
|
void GCodeQueue::flush_and_request_resend() {
|
324
|
|
- const serial_index_t serial_ind = command_port();
|
|
270
|
+ const serial_index_t serial_ind = ring_buffer.command_port();
|
325
|
271
|
#if HAS_MULTI_SERIAL
|
326
|
272
|
if (serial_ind < 0) return; // Never mind. Command came from SD or Flash Drive
|
327
|
273
|
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
|
328
|
274
|
#endif
|
329
|
275
|
SERIAL_FLUSH();
|
330
|
276
|
SERIAL_ECHOPGM(STR_RESEND);
|
331
|
|
- SERIAL_ECHOLN(last_N[serial_ind] + 1);
|
332
|
|
- ok_to_send();
|
|
277
|
+ SERIAL_ECHOLN(serial_state[serial_ind].last_N + 1);
|
333
|
278
|
}
|
334
|
279
|
|
335
|
|
-
|
336
|
280
|
// Multiserial already handle the dispatch to/from multiple port by itself
|
337
|
281
|
inline bool serial_data_available(uint8_t index = SERIAL_ALL) {
|
338
|
282
|
if (index == SERIAL_ALL) {
|
339
|
283
|
for (index = 0; index < NUM_SERIAL; index++) {
|
340
|
|
- if (SERIAL_IMPL.available(index) > 0) return true;
|
|
284
|
+ const int a = SERIAL_IMPL.available(index);
|
|
285
|
+ #if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
|
|
286
|
+ if (a > RX_BUFFER_SIZE - 2) {
|
|
287
|
+ PORT_REDIRECT(SERIAL_PORTMASK(index));
|
|
288
|
+ SERIAL_ERROR_MSG("RX BUF overflow, increase RX_BUFFER_SIZE: ", a);
|
|
289
|
+ }
|
|
290
|
+ #endif
|
|
291
|
+ if (a > 0) return true;
|
341
|
292
|
}
|
342
|
293
|
return false;
|
343
|
294
|
}
|
344
|
|
- return SERIAL_IMPL.available(index) > 0;
|
|
295
|
+ const int a = SERIAL_IMPL.available(index);
|
|
296
|
+ #if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
|
|
297
|
+ if (a > RX_BUFFER_SIZE - 2) {
|
|
298
|
+ PORT_REDIRECT(SERIAL_PORTMASK(index));
|
|
299
|
+ SERIAL_ERROR_MSG("RX BUF overflow, increase RX_BUFFER_SIZE: ", a);
|
|
300
|
+ }
|
|
301
|
+ #endif
|
|
302
|
+
|
|
303
|
+ return a > 0;
|
345
|
304
|
}
|
346
|
305
|
|
347
|
306
|
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); }
|
349
|
308
|
void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) {
|
350
|
309
|
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
|
351
|
310
|
SERIAL_ERROR_START();
|
352
|
|
- serialprintPGM(err);
|
353
|
|
- SERIAL_ECHOLN(last_N[serial_ind]);
|
354
|
|
- while (read_serial(serial_ind) != -1); // Clear out the RX buffer
|
|
311
|
+ SERIAL_ECHOPGM_P(err);
|
|
312
|
+ SERIAL_ECHOLN(serial_state[serial_ind].last_N);
|
|
313
|
+ while (read_serial(serial_ind) != -1) { /* nada */ } // Clear out the RX buffer. Why don't use flush here ?
|
355
|
314
|
flush_and_request_resend();
|
356
|
|
- serial_count[serial_ind] = 0;
|
|
315
|
+ serial_state[serial_ind].count = 0;
|
357
|
316
|
}
|
358
|
317
|
|
359
|
318
|
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
|
440
|
399
|
* left on the serial port.
|
441
|
400
|
*/
|
442
|
401
|
void GCodeQueue::get_serial_commands() {
|
443
|
|
- static char serial_line_buffer[NUM_SERIAL][MAX_CMD_SIZE];
|
444
|
|
-
|
445
|
|
- static uint8_t serial_input_state[NUM_SERIAL] = { PS_NORMAL };
|
446
|
|
-
|
447
|
402
|
#if ENABLED(BINARY_FILE_TRANSFER)
|
448
|
403
|
if (card.flag.binary_mode) {
|
449
|
404
|
/**
|
|
@@ -451,7 +406,7 @@ void GCodeQueue::get_serial_commands() {
|
451
|
406
|
* receive buffer (which limits the packet size to MAX_CMD_SIZE).
|
452
|
407
|
* The receive buffer also limits the packet size for reliable transmission.
|
453
|
408
|
*/
|
454
|
|
- binaryStream[card.transfer_port_index].receive(serial_line_buffer[card.transfer_port_index]);
|
|
409
|
+ binaryStream[card.transfer_port_index].receive(serial_state[card.transfer_port_index].line_buffer);
|
455
|
410
|
return;
|
456
|
411
|
}
|
457
|
412
|
#endif
|
|
@@ -460,122 +415,140 @@ void GCodeQueue::get_serial_commands() {
|
460
|
415
|
// send "wait" to indicate Marlin is still waiting.
|
461
|
416
|
#if NO_TIMEOUTS > 0
|
462
|
417
|
const millis_t ms = millis();
|
463
|
|
- if (length == 0 && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
|
|
418
|
+ if (ring_buffer.empty() && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
|
464
|
419
|
SERIAL_ECHOLNPGM(STR_WAIT);
|
465
|
420
|
last_command_time = ms;
|
466
|
421
|
}
|
467
|
422
|
#endif
|
468
|
423
|
|
469
|
|
- /**
|
470
|
|
- * Loop while serial characters are incoming and the queue is not full
|
471
|
|
- */
|
472
|
|
- while (length < BUFSIZE && serial_data_available()) {
|
|
424
|
+ // Loop while serial characters are incoming and the queue is not full
|
|
425
|
+ for (bool hadData = true; hadData;) {
|
|
426
|
+ // Unless a serial port has data, this will exit on next iteration
|
|
427
|
+ hadData = false;
|
|
428
|
+
|
473
|
429
|
LOOP_L_N(p, NUM_SERIAL) {
|
|
430
|
+ // Check if the queue is full and exit if it is.
|
|
431
|
+ if (ring_buffer.full()) return;
|
474
|
432
|
|
475
|
|
- const int c = read_serial(p);
|
476
|
|
- if (c < 0) continue;
|
477
|
|
-
|
478
|
|
- #if ENABLED(MEATPACK)
|
479
|
|
- meatpack.handle_rx_char(uint8_t(c), p);
|
480
|
|
- char c_res[2] = { 0, 0 };
|
481
|
|
- const uint8_t char_count = meatpack.get_result_char(c_res);
|
482
|
|
- #else
|
483
|
|
- constexpr uint8_t char_count = 1;
|
484
|
|
- #endif
|
|
433
|
+ // No data for this port ? Skip it
|
|
434
|
+ if (!serial_data_available(p)) continue;
|
485
|
435
|
|
486
|
|
- LOOP_L_N(char_index, char_count) {
|
487
|
|
- const char serial_char = TERN(MEATPACK, c_res[char_index], c);
|
|
436
|
+ // Ok, we have some data to process, let's make progress here
|
|
437
|
+ hadData = true;
|
488
|
438
|
|
489
|
|
- if (ISEOL(serial_char)) {
|
|
439
|
+ const int c = read_serial(p);
|
|
440
|
+ if (c < 0) {
|
|
441
|
+ // This should never happen, let's log it
|
|
442
|
+ PORT_REDIRECT(SERIAL_PORTMASK(p)); // Reply to the serial port that sent the command
|
|
443
|
+ // Crash here to get more information why it failed
|
|
444
|
+ BUG_ON("SP available but read -1");
|
|
445
|
+ SERIAL_ERROR_MSG(STR_ERR_SERIAL_MISMATCH);
|
|
446
|
+ SERIAL_FLUSH();
|
|
447
|
+ continue;
|
|
448
|
+ }
|
490
|
449
|
|
491
|
|
- // Reset our state, continue if the line was empty
|
492
|
|
- if (process_line_done(serial_input_state[p], serial_line_buffer[p], serial_count[p]))
|
493
|
|
- continue;
|
|
450
|
+ const char serial_char = (char)c;
|
|
451
|
+ SerialState &serial = serial_state[p];
|
494
|
452
|
|
495
|
|
- char* command = serial_line_buffer[p];
|
|
453
|
+ if (ISEOL(serial_char)) {
|
496
|
454
|
|
497
|
|
- while (*command == ' ') command++; // Skip leading spaces
|
498
|
|
- char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line
|
|
455
|
+ // Reset our state, continue if the line was empty
|
|
456
|
+ if (process_line_done(serial.input_state, serial.line_buffer, serial.count))
|
|
457
|
+ continue;
|
499
|
458
|
|
500
|
|
- if (npos) {
|
|
459
|
+ char* command = serial.line_buffer;
|
501
|
460
|
|
502
|
|
- const bool M110 = !!strstr_P(command, PSTR("M110"));
|
|
461
|
+ while (*command == ' ') command++; // Skip leading spaces
|
|
462
|
+ char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line
|
503
|
463
|
|
504
|
|
- if (M110) {
|
505
|
|
- char* n2pos = strchr(command + 4, 'N');
|
506
|
|
- if (n2pos) npos = n2pos;
|
507
|
|
- }
|
|
464
|
+ if (npos) {
|
508
|
465
|
|
509
|
|
- const long gcode_N = strtol(npos + 1, nullptr, 10);
|
|
466
|
+ const bool M110 = !!strstr_P(command, PSTR("M110"));
|
510
|
467
|
|
511
|
|
- if (gcode_N != last_N[p] + 1 && !M110)
|
512
|
|
- return gcode_line_error(PSTR(STR_ERR_LINE_NO), p);
|
|
468
|
+ if (M110) {
|
|
469
|
+ char* n2pos = strchr(command + 4, 'N');
|
|
470
|
+ if (n2pos) npos = n2pos;
|
|
471
|
+ }
|
513
|
472
|
|
514
|
|
- char *apos = strrchr(command, '*');
|
515
|
|
- if (apos) {
|
516
|
|
- uint8_t checksum = 0, count = uint8_t(apos - command);
|
517
|
|
- while (count) checksum ^= command[--count];
|
518
|
|
- if (strtol(apos + 1, nullptr, 10) != checksum)
|
519
|
|
- return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
|
520
|
|
- }
|
521
|
|
- else
|
522
|
|
- return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
|
|
473
|
+ const long gcode_N = strtol(npos + 1, nullptr, 10);
|
523
|
474
|
|
524
|
|
- last_N[p] = gcode_N;
|
|
475
|
+ if (gcode_N != serial.last_N + 1 && !M110) {
|
|
476
|
+ // In case of error on a serial port, don't prevent other serial port from making progress
|
|
477
|
+ gcode_line_error(PSTR(STR_ERR_LINE_NO), p);
|
|
478
|
+ break;
|
525
|
479
|
}
|
526
|
|
- #if ENABLED(SDSUPPORT)
|
527
|
|
- // Pronterface "M29" and "M29 " has no line number
|
528
|
|
- else if (card.flag.saving && !is_M29(command))
|
529
|
|
- return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
|
530
|
|
- #endif
|
531
|
480
|
|
532
|
|
- //
|
533
|
|
- // Movement commands give an alert when the machine is stopped
|
534
|
|
- //
|
535
|
|
-
|
536
|
|
- if (IsStopped()) {
|
537
|
|
- char* gpos = strchr(command, 'G');
|
538
|
|
- if (gpos) {
|
539
|
|
- switch (strtol(gpos + 1, nullptr, 10)) {
|
540
|
|
- case 0: case 1:
|
541
|
|
- #if ENABLED(ARC_SUPPORT)
|
542
|
|
- case 2: case 3:
|
543
|
|
- #endif
|
544
|
|
- #if ENABLED(BEZIER_CURVE_SUPPORT)
|
545
|
|
- case 5:
|
546
|
|
- #endif
|
547
|
|
- PORT_REDIRECT(SERIAL_PORTMASK(p)); // Reply to the serial port that sent the command
|
548
|
|
- SERIAL_ECHOLNPGM(STR_ERR_STOPPED);
|
549
|
|
- LCD_MESSAGEPGM(MSG_STOPPED);
|
550
|
|
- break;
|
551
|
|
- }
|
|
481
|
+ char *apos = strrchr(command, '*');
|
|
482
|
+ if (apos) {
|
|
483
|
+ uint8_t checksum = 0, count = uint8_t(apos - command);
|
|
484
|
+ while (count) checksum ^= command[--count];
|
|
485
|
+ if (strtol(apos + 1, nullptr, 10) != checksum) {
|
|
486
|
+ // In case of error on a serial port, don't prevent other serial port from making progress
|
|
487
|
+ gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
|
|
488
|
+ break;
|
552
|
489
|
}
|
553
|
490
|
}
|
|
491
|
+ else {
|
|
492
|
+ // In case of error on a serial port, don't prevent other serial port from making progress
|
|
493
|
+ gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
|
|
494
|
+ break;
|
|
495
|
+ }
|
|
496
|
+
|
|
497
|
+ serial.last_N = gcode_N;
|
|
498
|
+ }
|
|
499
|
+ #if ENABLED(SDSUPPORT)
|
|
500
|
+ // Pronterface "M29" and "M29 " has no line number
|
|
501
|
+ else if (card.flag.saving && !is_M29(command)) {
|
|
502
|
+ gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
|
|
503
|
+ break;
|
|
504
|
+ }
|
|
505
|
+ #endif
|
554
|
506
|
|
555
|
|
- #if DISABLED(EMERGENCY_PARSER)
|
556
|
|
- // Process critical commands early
|
557
|
|
- if (command[0] == 'M') switch (command[3]) {
|
558
|
|
- case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break;
|
559
|
|
- case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break;
|
560
|
|
- case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break;
|
|
507
|
+ //
|
|
508
|
+ // Movement commands give an alert when the machine is stopped
|
|
509
|
+ //
|
|
510
|
+
|
|
511
|
+ if (IsStopped()) {
|
|
512
|
+ char* gpos = strchr(command, 'G');
|
|
513
|
+ if (gpos) {
|
|
514
|
+ switch (strtol(gpos + 1, nullptr, 10)) {
|
|
515
|
+ case 0: case 1:
|
|
516
|
+ #if ENABLED(ARC_SUPPORT)
|
|
517
|
+ case 2: case 3:
|
|
518
|
+ #endif
|
|
519
|
+ #if ENABLED(BEZIER_CURVE_SUPPORT)
|
|
520
|
+ case 5:
|
|
521
|
+ #endif
|
|
522
|
+ PORT_REDIRECT(SERIAL_PORTMASK(p)); // Reply to the serial port that sent the command
|
|
523
|
+ SERIAL_ECHOLNPGM(STR_ERR_STOPPED);
|
|
524
|
+ LCD_MESSAGEPGM(MSG_STOPPED);
|
|
525
|
+ break;
|
561
|
526
|
}
|
562
|
|
- #endif
|
|
527
|
+ }
|
|
528
|
+ }
|
563
|
529
|
|
564
|
|
- #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
|
565
|
|
- last_command_time = ms;
|
566
|
|
- #endif
|
|
530
|
+ #if DISABLED(EMERGENCY_PARSER)
|
|
531
|
+ // Process critical commands early
|
|
532
|
+ if (command[0] == 'M') switch (command[3]) {
|
|
533
|
+ case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break;
|
|
534
|
+ case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break;
|
|
535
|
+ case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break;
|
|
536
|
+ }
|
|
537
|
+ #endif
|
567
|
538
|
|
568
|
|
- // Add the command to the queue
|
569
|
|
- _enqueue(serial_line_buffer[p], true
|
570
|
|
- #if HAS_MULTI_SERIAL
|
571
|
|
- , p
|
572
|
|
- #endif
|
573
|
|
- );
|
574
|
|
- }
|
575
|
|
- else
|
576
|
|
- process_stream_char(serial_char, serial_input_state[p], serial_line_buffer[p], serial_count[p]);
|
|
539
|
+ #if NO_TIMEOUTS > 0
|
|
540
|
+ last_command_time = ms;
|
|
541
|
+ #endif
|
577
|
542
|
|
578
|
|
- } // char_count loop
|
|
543
|
+ // Add the command to the queue
|
|
544
|
+ ring_buffer.enqueue(serial.line_buffer, false
|
|
545
|
+ #if HAS_MULTI_SERIAL
|
|
546
|
+ , p
|
|
547
|
+ #endif
|
|
548
|
+ );
|
|
549
|
+ }
|
|
550
|
+ else
|
|
551
|
+ process_stream_char(serial_char, serial.input_state, serial.line_buffer, serial.count);
|
579
|
552
|
|
580
|
553
|
} // NUM_SERIAL loop
|
581
|
554
|
} // queue has space, serial has data
|
|
@@ -595,33 +568,35 @@ void GCodeQueue::get_serial_commands() {
|
595
|
568
|
if (!IS_SD_PRINTING()) return;
|
596
|
569
|
|
597
|
570
|
int sd_count = 0;
|
598
|
|
- while (length < BUFSIZE && !card.eof()) {
|
|
571
|
+ while (!ring_buffer.full() && !card.eof()) {
|
599
|
572
|
const int16_t n = card.get();
|
600
|
573
|
const bool card_eof = card.eof();
|
601
|
574
|
if (n < 0 && !card_eof) { SERIAL_ERROR_MSG(STR_SD_ERR_READ); continue; }
|
602
|
575
|
|
|
576
|
+ CommandLine &command = ring_buffer.commands[ring_buffer.index_w];
|
603
|
577
|
const char sd_char = (char)n;
|
604
|
578
|
const bool is_eol = ISEOL(sd_char);
|
605
|
579
|
if (is_eol || card_eof) {
|
606
|
580
|
|
|
581
|
+
|
607
|
582
|
// Reset stream state, terminate the buffer, and commit a non-empty command
|
608
|
583
|
if (!is_eol && sd_count) ++sd_count; // End of file with no newline
|
609
|
|
- if (!process_line_done(sd_input_state, command_buffer[index_w], sd_count)) {
|
|
584
|
+ if (!process_line_done(sd_input_state, command.buffer, sd_count)) {
|
610
|
585
|
|
611
|
586
|
// M808 L saves the sdpos of the next line. M808 loops to a new sdpos.
|
612
|
|
- TERN_(GCODE_REPEAT_MARKERS, repeat.early_parse_M808(command_buffer[index_w]));
|
|
587
|
+ TERN_(GCODE_REPEAT_MARKERS, repeat.early_parse_M808(command.buffer));
|
613
|
588
|
|
614
|
589
|
// Put the new command into the buffer (no "ok" sent)
|
615
|
|
- _commit_command(false);
|
|
590
|
+ ring_buffer.commit_command(true);
|
616
|
591
|
|
617
|
|
- // Prime Power-Loss Recovery for the NEXT _commit_command
|
|
592
|
+ // Prime Power-Loss Recovery for the NEXT commit_command
|
618
|
593
|
TERN_(POWER_LOSS_RECOVERY, recovery.cmd_sdpos = card.getIndex());
|
619
|
594
|
}
|
620
|
595
|
|
621
|
596
|
if (card.eof()) card.fileHasFinished(); // Handle end of file reached
|
622
|
597
|
}
|
623
|
598
|
else
|
624
|
|
- process_stream_char(sd_char, sd_input_state, command_buffer[index_w], sd_count);
|
|
599
|
+ process_stream_char(sd_char, sd_input_state, command.buffer, sd_count);
|
625
|
600
|
}
|
626
|
601
|
}
|
627
|
602
|
|
|
@@ -634,6 +609,7 @@ void GCodeQueue::get_serial_commands() {
|
634
|
609
|
* - The SD card file being actively printed
|
635
|
610
|
*/
|
636
|
611
|
void GCodeQueue::get_available_commands() {
|
|
612
|
+ if (ring_buffer.full()) return;
|
637
|
613
|
|
638
|
614
|
get_serial_commands();
|
639
|
615
|
|
|
@@ -649,13 +625,13 @@ void GCodeQueue::advance() {
|
649
|
625
|
if (process_injected_command_P() || process_injected_command()) return;
|
650
|
626
|
|
651
|
627
|
// Return if the G-code buffer is empty
|
652
|
|
- if (!length) return;
|
|
628
|
+ if (ring_buffer.empty()) return;
|
653
|
629
|
|
654
|
630
|
#if ENABLED(SDSUPPORT)
|
655
|
631
|
|
656
|
632
|
if (card.flag.saving) {
|
657
|
|
- char* command = command_buffer[index_r];
|
658
|
|
- if (is_M29(command)) {
|
|
633
|
+ char * const cmd = ring_buffer.peek_next_command_string();
|
|
634
|
+ if (is_M29(cmd)) {
|
659
|
635
|
// M29 closes the file
|
660
|
636
|
card.closefile();
|
661
|
637
|
SERIAL_ECHOLNPGM(STR_FILE_SAVED);
|
|
@@ -673,7 +649,7 @@ void GCodeQueue::advance() {
|
673
|
649
|
}
|
674
|
650
|
else {
|
675
|
651
|
// Write the string from the read buffer to SD
|
676
|
|
- card.write_command(command);
|
|
652
|
+ card.write_command(cmd);
|
677
|
653
|
if (card.flag.logging)
|
678
|
654
|
gcode.process_next_command(); // The card is saving because it's logging
|
679
|
655
|
else
|
|
@@ -690,7 +666,5 @@ void GCodeQueue::advance() {
|
690
|
666
|
#endif // SDSUPPORT
|
691
|
667
|
|
692
|
668
|
// The queue may be reset by a command handler or by code invoked by idle() within a handler
|
693
|
|
- --length;
|
694
|
|
- if (++index_r >= BUFSIZE) index_r = 0;
|
695
|
|
-
|
|
669
|
+ ring_buffer.advance_pos(ring_buffer.index_r, -1);
|
696
|
670
|
}
|