don;t cal lprintf in play unless -v is specified
[clinton/Smoothieware.git] / src / modules / utils / player / Player.cpp
CommitLineData
663d7943
L
1/*
2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 Smoothie 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.
4 Smoothie 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.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
6*/
7
9ef9c12a 8#include "Player.h"
663d7943
L
9
10#include "libs/Kernel.h"
728477c4 11#include "Robot.h"
663d7943
L
12#include "libs/nuts_bolts.h"
13#include "libs/utils.h"
61134a65 14#include "SerialConsole.h"
663d7943 15#include "libs/SerialMessage.h"
61134a65 16#include "libs/StreamOutputPool.h"
663d7943 17#include "libs/StreamOutput.h"
61134a65
JM
18#include "Gcode.h"
19#include "checksumm.h"
61134a65 20#include "Config.h"
8d54c34c 21#include "ConfigValue.h"
4fba6676 22#include "SDFAT.h"
61134a65 23
663d7943 24#include "modules/robot/Conveyor.h"
172d42d9 25#include "DirHandle.h"
35089dc7 26#include "PublicDataRequest.h"
02e4b295 27#include "PublicData.h"
35089dc7 28#include "PlayerPublicAccess.h"
02e4b295
JM
29#include "TemperatureControlPublicAccess.h"
30#include "TemperatureControlPool.h"
928467c0 31#include "ExtruderPublicAccess.h"
02e4b295
JM
32
33#include <cstddef>
34#include <cmath>
d467fcad 35#include <algorithm>
02e4b295
JM
36
37#include "mbed.h"
663d7943 38
f6fc8c0d
JM
39#define on_boot_gcode_checksum CHECKSUM("on_boot_gcode")
40#define on_boot_gcode_enable_checksum CHECKSUM("on_boot_gcode_enable")
41#define after_suspend_gcode_checksum CHECKSUM("after_suspend_gcode")
42#define before_resume_gcode_checksum CHECKSUM("before_resume_gcode")
43#define leave_heaters_on_suspend_checksum CHECKSUM("leave_heaters_on_suspend")
44
4fba6676 45extern SDFAT mounter;
46
02e4b295 47Player::Player()
36aa8b0b 48{
663d7943 49 this->playing_file = false;
02e4b295 50 this->current_file_handler = nullptr;
addfb453 51 this->booted = false;
02e4b295
JM
52 this->elapsed_secs = 0;
53 this->reply_stream = nullptr;
02e4b295 54 this->suspended= false;
0f11f650 55 this->suspend_loops= 0;
02e4b295
JM
56}
57
58void Player::on_module_loaded()
59{
663d7943
L
60 this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
61 this->register_for_event(ON_MAIN_LOOP);
63888663 62 this->register_for_event(ON_SECOND_TICK);
58d6d841
JM
63 this->register_for_event(ON_GET_PUBLIC_DATA);
64 this->register_for_event(ON_SET_PUBLIC_DATA);
c4e56997 65 this->register_for_event(ON_GCODE_RECEIVED);
18fd33ee 66 this->register_for_event(ON_HALT);
8fef244a 67
314ab8f7
MM
68 this->on_boot_gcode = THEKERNEL->config->value(on_boot_gcode_checksum)->by_default("/sd/on_boot.gcode")->as_string();
69 this->on_boot_gcode_enable = THEKERNEL->config->value(on_boot_gcode_enable_checksum)->by_default(true)->as_bool();
d467fcad
JM
70
71 this->after_suspend_gcode = THEKERNEL->config->value(after_suspend_gcode_checksum)->by_default("")->as_string();
72 this->before_resume_gcode = THEKERNEL->config->value(before_resume_gcode_checksum)->by_default("")->as_string();
73 std::replace( this->after_suspend_gcode.begin(), this->after_suspend_gcode.end(), '_', ' '); // replace _ with space
74 std::replace( this->before_resume_gcode.begin(), this->before_resume_gcode.end(), '_', ' '); // replace _ with space
f6fc8c0d 75 this->leave_heaters_on = THEKERNEL->config->value(leave_heaters_on_suspend_checksum)->by_default(false)->as_bool();
63888663
JM
76}
77
18fd33ee
JM
78void Player::on_halt(void* argument)
79{
80 if(argument == nullptr && this->playing_file ) {
81 abort_command("1", &(StreamOutput::NullStream));
82 }
83}
84
36aa8b0b
JM
85void Player::on_second_tick(void *)
86{
d467fcad 87 if(this->playing_file) this->elapsed_secs++;
663d7943
L
88}
89
428d1181
JM
90// extract any options found on line, terminates args at the space before the first option (-v)
91// eg this is a file.gcode -v
92// will return -v and set args to this is a file.gcode
93string Player::extract_options(string& args)
94{
95 string opts;
96 size_t pos= args.find(" -");
97 if(pos != string::npos) {
98 opts= args.substr(pos);
99 args= args.substr(0, pos);
100 }
101
102 return opts;
103}
104
36aa8b0b
JM
105void Player::on_gcode_received(void *argument)
106{
107 Gcode *gcode = static_cast<Gcode *>(argument);
8090190d 108 string args = get_arguments(gcode->get_command());
c4e56997 109 if (gcode->has_m) {
6d4d88bc 110 if (gcode->m == 21) { // Dummy code; makes Octoprint happy -- supposed to initialize SD card
ca8a9d29 111 mounter.remount();
6d4d88bc 112 gcode->stream->printf("SD card ok\r\n");
4c8afa75 113
36aa8b0b 114 } else if (gcode->m == 23) { // select file
428d1181 115 this->filename = "/sd/" + args; // filename is whatever is in args
36697974 116 this->current_stream = nullptr;
c4e56997
JM
117
118 if(this->current_file_handler != NULL) {
119 this->playing_file = false;
120 fclose(this->current_file_handler);
121 }
e2cd3423 122 this->current_file_handler = fopen( this->filename.c_str(), "r");
fbd92b6a 123
36aa8b0b 124 if(this->current_file_handler == NULL) {
e2cd3423 125 gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str());
1e6e89ee
JM
126 return;
127
36aa8b0b 128 } else {
1e6e89ee
JM
129 // get size of file
130 int result = fseek(this->current_file_handler, 0, SEEK_END);
36aa8b0b
JM
131 if (0 != result) {
132 this->file_size = 0;
133 } else {
134 this->file_size = ftell(this->current_file_handler);
135 fseek(this->current_file_handler, 0, SEEK_SET);
1e6e89ee
JM
136 }
137 gcode->stream->printf("File opened:%s Size:%ld\r\n", this->filename.c_str(), this->file_size);
fbd92b6a 138 gcode->stream->printf("File selected\r\n");
c4e56997
JM
139 }
140
1e6e89ee 141
36aa8b0b
JM
142 this->played_cnt = 0;
143 this->elapsed_secs = 0;
fbd92b6a 144
36aa8b0b 145 } else if (gcode->m == 24) { // start print
c4e56997
JM
146 if (this->current_file_handler != NULL) {
147 this->playing_file = true;
d4ee6ee2
JM
148 // this would be a problem if the stream goes away before the file has finished,
149 // so we attach it to the kernel stream, however network connections from pronterface
150 // do not connect to the kernel streams so won't see this FIXME
36aa8b0b 151 this->reply_stream = THEKERNEL->streams;
c4e56997
JM
152 }
153
36aa8b0b 154 } else if (gcode->m == 25) { // pause print
c4e56997
JM
155 this->playing_file = false;
156
36aa8b0b 157 } else if (gcode->m == 26) { // Reset print. Slightly different than M26 in Marlin and the rest
36aa8b0b
JM
158 if(this->current_file_handler != NULL) {
159 string currentfn = this->filename.c_str();
160 unsigned long old_size = this->file_size;
1e6e89ee 161
4c60a46b
CG
162 // abort the print
163 abort_command("", gcode->stream);
4c8afa75 164
1e6e89ee
JM
165 if(!currentfn.empty()) {
166 // reload the last file opened
167 this->current_file_handler = fopen(currentfn.c_str() , "r");
4c8afa75 168
36aa8b0b 169 if(this->current_file_handler == NULL) {
1e6e89ee 170 gcode->stream->printf("file.open failed: %s\r\n", currentfn.c_str());
36aa8b0b
JM
171 } else {
172 this->filename = currentfn;
173 this->file_size = old_size;
36697974 174 this->current_stream = nullptr;
4c60a46b
CG
175 }
176 }
36aa8b0b 177 } else {
4c60a46b
CG
178 gcode->stream->printf("No file loaded\r\n");
179 }
4c8afa75 180
36aa8b0b 181 } else if (gcode->m == 27) { // report print progress, in format used by Marlin
43d26cb1 182 progress_command("-b", gcode->stream);
6d4d88bc 183
36aa8b0b 184 } else if (gcode->m == 32) { // select file and start print
6d4d88bc 185 // Get filename
428d1181 186 this->filename = "/sd/" + args; // filename is whatever is in args including spaces
36697974 187 this->current_stream = nullptr;
6d4d88bc
CG
188
189 if(this->current_file_handler != NULL) {
190 this->playing_file = false;
191 fclose(this->current_file_handler);
192 }
193
194 this->current_file_handler = fopen( this->filename.c_str(), "r");
36aa8b0b 195 if(this->current_file_handler == NULL) {
6d4d88bc 196 gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str());
36aa8b0b 197 } else {
6d4d88bc 198 this->playing_file = true;
5550a9c9
DM
199
200 // get size of file
201 int result = fseek(this->current_file_handler, 0, SEEK_END);
202 if (0 != result) {
203 file_size = 0;
204 } else {
205 file_size = ftell(this->current_file_handler);
206 fseek(this->current_file_handler, 0, SEEK_SET);
207 }
6d4d88bc 208 }
b34495c6 209
eaf20558
DM
210 this->played_cnt = 0;
211 this->elapsed_secs = 0;
afc5b690 212
16d1a7b7
JM
213 } else if (gcode->m == 600) { // suspend print, Not entirely Marlin compliant, M600.1 will leave the heaters on
214 this->suspend_command((gcode->subcode == 1)?"h":"", gcode->stream);
afc5b690
JM
215
216 } else if (gcode->m == 601) { // resume print
edcfc706 217 this->resume_command("", gcode->stream);
c4e56997 218 }
2a4d8706
JM
219
220 }else if(gcode->has_g) {
221 if(gcode->g == 28) { // homing cancels suspend
222 if(this->suspended) {
223 // clean up
224 this->suspended= false;
c8bac202 225 THEROBOT->pop_state();
2a4d8706
JM
226 this->saved_temperatures.clear();
227 this->was_playing_file= false;
228 this->suspend_loops= 0;
229 }
230 }
c4e56997
JM
231 }
232}
233
663d7943 234// When a new line is received, check if it is a command, and if it is, act upon it
36aa8b0b
JM
235void Player::on_console_line_received( void *argument )
236{
73706276 237 if(THEKERNEL->is_halted()) return; // if in halted state ignore any commands
b34495c6 238
36aa8b0b 239 SerialMessage new_message = *static_cast<SerialMessage *>(argument);
7f613782 240
7e81f138
JM
241 // ignore comments and blank lines and if this is a G code then also ignore it
242 char first_char = new_message.message[0];
243 if(strchr(";( \n\rGMTN", first_char) != NULL) return;
7f613782 244
663d7943 245 string possible_command = new_message.message;
7e81f138 246 string cmd = shift_parameter(possible_command);
663d7943
L
247
248 //new_message.stream->printf("Received %s\r\n", possible_command.c_str());
249
663d7943 250 // Act depending on command
7e81f138
JM
251 if (cmd == "play"){
252 this->play_command( possible_command, new_message.stream );
253 }else if (cmd == "progress"){
254 this->progress_command( possible_command, new_message.stream );
02e4b295 255 }else if (cmd == "abort") {
7e81f138 256 this->abort_command( possible_command, new_message.stream );
02e4b295
JM
257 }else if (cmd == "suspend") {
258 this->suspend_command( possible_command, new_message.stream );
259 }else if (cmd == "resume") {
260 this->resume_command( possible_command, new_message.stream );
261 }
663d7943
L
262}
263
264// Play a gcode file by considering each line as if it was received on the serial console
36aa8b0b
JM
265void Player::play_command( string parameters, StreamOutput *stream )
266{
428d1181
JM
267 // extract any options from the line and terminate the line there
268 string options= extract_options(parameters);
269 // Get filename which is the entire parameter line upto any options found or entire line
270 this->filename = absolute_from_relative(parameters);
663d7943 271
02e4b295 272 if(this->playing_file || this->suspended) {
36aa8b0b
JM
273 stream->printf("Currently printing, abort print first\r\n");
274 return;
275 }
276
277 if(this->current_file_handler != NULL) { // must have been a paused print
278 fclose(this->current_file_handler);
279 }
280
e2cd3423 281 this->current_file_handler = fopen( this->filename.c_str(), "r");
36aa8b0b 282 if(this->current_file_handler == NULL) {
e2cd3423 283 stream->printf("File not found: %s\r\n", this->filename.c_str());
663d7943
L
284 return;
285 }
fdbea18b 286
e2cd3423 287 stream->printf("Playing %s\r\n", this->filename.c_str());
8fef244a 288
663d7943 289 this->playing_file = true;
fdbea18b 290
4eb0e279 291 // Output to the current stream if we were passed the -v ( verbose ) option
36aa8b0b 292 if( options.find_first_of("Vv") == string::npos ) {
36697974 293 this->current_stream = nullptr;
36aa8b0b 294 } else {
d4ee6ee2
JM
295 // we send to the kernels stream as it cannot go away
296 this->current_stream = THEKERNEL->streams;
fdbea18b
AW
297 }
298
299 // get size of file
300 int result = fseek(this->current_file_handler, 0, SEEK_END);
36aa8b0b
JM
301 if (0 != result) {
302 stream->printf("WARNING - Could not get file size\r\n");
303 file_size = 0;
304 } else {
305 file_size = ftell(this->current_file_handler);
306 fseek(this->current_file_handler, 0, SEEK_SET);
307 stream->printf(" File size %ld\r\n", file_size);
fdbea18b 308 }
36aa8b0b
JM
309 this->played_cnt = 0;
310 this->elapsed_secs = 0;
addfb453
L
311}
312
36aa8b0b
JM
313void Player::progress_command( string parameters, StreamOutput *stream )
314{
43d26cb1
CG
315
316 // get options
36aa8b0b 317 string options = shift_parameter( parameters );
09351052 318 bool sdprinting= options.find_first_of("Bb") != string::npos;
43d26cb1 319
36aa8b0b 320 if(!playing_file && current_file_handler != NULL) {
09351052
JM
321 if(sdprinting)
322 stream->printf("SD printing byte %lu/%lu\r\n", played_cnt, file_size);
323 else
324 stream->printf("SD print is paused at %lu/%lu\r\n", played_cnt, file_size);
36aa8b0b
JM
325 return;
326
327 } else if(!playing_file) {
b41aedba
JM
328 stream->printf("Not currently playing\r\n");
329 return;
330 }
331
332 if(file_size > 0) {
36aa8b0b 333 unsigned long est = 0;
0a3f2832 334 if(this->elapsed_secs > 10) {
36aa8b0b 335 unsigned long bytespersec = played_cnt / this->elapsed_secs;
b41aedba 336 if(bytespersec > 0)
36aa8b0b 337 est = (file_size - played_cnt) / bytespersec;
b41aedba 338 }
8fef244a 339
36aa8b0b 340 unsigned int pcnt = (file_size - (file_size - played_cnt)) * 100 / file_size;
43d26cb1 341 // If -b or -B is passed, report in the format used by Marlin and the others.
09351052 342 if (!sdprinting) {
60ecb88e 343 stream->printf("file: %s, %u %% complete, elapsed time: %lu s", this->filename.c_str(), pcnt, this->elapsed_secs);
36aa8b0b 344 if(est > 0) {
104293d2 345 stream->printf(", est time: %lu s", est);
43d26cb1
CG
346 }
347 stream->printf("\r\n");
36aa8b0b 348 } else {
104293d2 349 stream->printf("SD printing byte %lu/%lu\r\n", played_cnt, file_size);
0a3f2832 350 }
8fef244a 351
36aa8b0b 352 } else {
b41aedba
JM
353 stream->printf("File size is unknown\r\n");
354 }
addfb453
L
355}
356
36aa8b0b
JM
357void Player::abort_command( string parameters, StreamOutput *stream )
358{
359 if(!playing_file && current_file_handler == NULL) {
b41aedba
JM
360 stream->printf("Not currently playing\r\n");
361 return;
362 }
02e4b295 363 suspended= false;
b41aedba 364 playing_file = false;
36aa8b0b
JM
365 played_cnt = 0;
366 file_size = 0;
367 this->filename = "";
368 this->current_stream = NULL;
b41aedba 369 fclose(current_file_handler);
36aa8b0b 370 current_file_handler = NULL;
5aa974f7 371 if(parameters.empty()) {
08f89868
JM
372 // clear out the block queue, will wait until queue is empty
373 // MUST be called in on_main_loop to make sure there are no blocked main loops waiting to put something on the queue
5aa974f7 374 THEKERNEL->conveyor->flush_queue();
728477c4
JM
375
376 // now the position will think it is at the last received pos, so we need to do FK to get the actuator position and reset the current position
c8bac202 377 THEROBOT->reset_position_from_current_actuator_position();
18fd33ee 378 stream->printf("Aborted playing or paused file. Please turn any heaters off manually\r\n");
5aa974f7 379 }
addfb453
L
380}
381
36aa8b0b
JM
382void Player::on_main_loop(void *argument)
383{
0f11f650 384 if(suspended && suspend_loops > 0) {
08f89868 385 // if we are suspended we need to allow main loop to cycle a few times then finish off the suspend processing
0f11f650
JM
386 if(--suspend_loops == 0) {
387 suspend_part2();
388 return;
389 }
390 }
391
38776a0b 392 if( !this->booted ) {
33e4cc02 393 this->booted = true;
36aa8b0b 394 if( this->on_boot_gcode_enable ) {
314ab8f7 395 this->play_command(this->on_boot_gcode, THEKERNEL->serial);
36aa8b0b 396 } else {
314ab8f7 397 //THEKERNEL->serial->printf("On boot gcode disabled! skipping...\n");
38776a0b 398 }
addfb453
L
399 }
400
36aa8b0b 401 if( this->playing_file ) {
73706276 402 if(THEKERNEL->is_halted()) {
4c09283b
JM
403 return;
404 }
405
9ef9c12a 406 char buf[130]; // lines upto 128 characters are allowed, anything longer is discarded
36aa8b0b 407 bool discard = false;
9ef9c12a
JM
408
409 while(fgets(buf, sizeof(buf), this->current_file_handler) != NULL) {
36aa8b0b 410 int len = strlen(buf);
9ef9c12a 411 if(len == 0) continue; // empty line? should not be possible
7a1b71b5 412 if(buf[len - 1] == '\n' || feof(this->current_file_handler)) {
9ef9c12a 413 if(discard) { // we are discarding a long line
36aa8b0b 414 discard = false;
9ef9c12a 415 continue;
63ada22a 416 }
9ef9c12a
JM
417 if(len == 1) continue; // empty line
418
36697974
JM
419 if(this->current_stream != nullptr) {
420 this->current_stream->printf("%s", buf);
421 }
422
addfb453 423 struct SerialMessage message;
9ef9c12a 424 message.message = buf;
36697974 425 message.stream = this->current_stream == nullptr ? &(StreamOutput::NullStream) : this->current_stream;
36aa8b0b
JM
426
427 // waits for the queue to have enough room
314ab8f7 428 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message);
9ef9c12a
JM
429 played_cnt += len;
430 return; // we feed one line per main loop
63ada22a 431
36aa8b0b 432 } else {
9ef9c12a 433 // discard long line
36697974 434 if(this->current_stream != nullptr) { this->current_stream->printf("Warning: Discarded long line\n"); }
36aa8b0b 435 discard = true;
addfb453 436 }
c4e56997
JM
437 }
438
b41aedba 439 this->playing_file = false;
36aa8b0b
JM
440 this->filename = "";
441 played_cnt = 0;
442 file_size = 0;
addfb453 443 fclose(this->current_file_handler);
36aa8b0b
JM
444 current_file_handler = NULL;
445 this->current_stream = NULL;
c4e56997
JM
446
447 if(this->reply_stream != NULL) {
448 // if we were printing from an M command from pronterface we need to send this back
449 this->reply_stream->printf("Done printing file\r\n");
36aa8b0b 450 this->reply_stream = NULL;
c4e56997 451 }
663d7943
L
452 }
453}
454
36aa8b0b
JM
455void Player::on_get_public_data(void *argument)
456{
457 PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
58d6d841
JM
458
459 if(!pdr->starts_with(player_checksum)) return;
460
5f1a896b 461 if(pdr->second_element_is(is_playing_checksum) || pdr->second_element_is(is_suspended_checksum)) {
58d6d841 462 static bool bool_data;
5f1a896b 463 bool_data = pdr->second_element_is(is_playing_checksum) ? this->playing_file : this->suspended;
58d6d841
JM
464 pdr->set_data_ptr(&bool_data);
465 pdr->set_taken();
c4e56997 466
36aa8b0b 467 } else if(pdr->second_element_is(get_progress_checksum)) {
58d6d841
JM
468 static struct pad_progress p;
469 if(file_size > 0 && playing_file) {
36aa8b0b
JM
470 p.elapsed_secs = this->elapsed_secs;
471 p.percent_complete = (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size;
472 p.filename = this->filename;
58d6d841
JM
473 pdr->set_data_ptr(&p);
474 pdr->set_taken();
475 }
476 }
35089dc7
JM
477}
478
36aa8b0b
JM
479void Player::on_set_public_data(void *argument)
480{
481 PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
58d6d841
JM
482
483 if(!pdr->starts_with(player_checksum)) return;
484
485 if(pdr->second_element_is(abort_play_checksum)) {
4c8afa75
JM
486 abort_command("", &(StreamOutput::NullStream));
487 pdr->set_taken();
58d6d841 488 }
35089dc7 489}
02e4b295
JM
490
491/**
492Suspend a print in progress
4931. send pause to upstream host, or pause if printing from sd
0f11f650 4941a. loop on_main_loop several times to clear any buffered commmands
02e4b295
JM
4952. wait for empty queue
4963. save the current position, extruder position, temperatures - any state that would need to be restored
4974. retract by specifed amount either on command line or in config
4985. turn off heaters.
d467fcad 4996. optionally run after_suspend gcode (either in config or on command line)
02e4b295
JM
500
501User may jog or remove and insert filament at this point, extruding or retracting as needed
502
503*/
504void Player::suspend_command(string parameters, StreamOutput *stream )
505{
506 if(suspended) {
507 stream->printf("Already suspended\n");
508 return;
509 }
510
edcfc706 511 stream->printf("Suspending print, waiting for queue to empty...\n");
02e4b295 512
16d1a7b7
JM
513 // override the leave_heaters_on setting
514 this->override_leave_heaters_on= (parameters == "h");
515
02e4b295
JM
516 suspended= true;
517 if( this->playing_file ) {
518 // pause an sd print
519 this->playing_file = false;
520 this->was_playing_file= true;
521 }else{
522 // send pause to upstream host, we send it on all ports as we don't know which it is on
523 THEKERNEL->streams->printf("// action:pause\r\n");
524 this->was_playing_file= false;
525 }
526
0f11f650
JM
527 // we need to allow main loop to cycle a few times to clear any buffered commands in the serial streams etc
528 suspend_loops= 10;
0f11f650
JM
529}
530
531// this completes the suspend
532void Player::suspend_part2()
533{
212caccd
JM
534 // need to use streams here as the original stream may have changed
535 THEKERNEL->streams->printf("// Waiting for queue to empty (Host must stop sending)...\n");
02e4b295 536 // wait for queue to empty
04782655 537 THEKERNEL->conveyor->wait_for_idle();
02e4b295 538
212caccd 539 THEKERNEL->streams->printf("// Saving current state...\n");
02e4b295
JM
540
541 // save current XYZ position
c8bac202 542 THEROBOT->get_axis_position(this->saved_position);
02e4b295 543
d467fcad
JM
544 // save current extruder state
545 PublicData::set_value( extruder_checksum, save_state_checksum, nullptr );
02e4b295 546
212caccd 547 // save state use M120
c8bac202 548 THEROBOT->push_state();
02e4b295 549
f6fc8c0d
JM
550 // TODO retract by optional amount...
551
552 this->saved_temperatures.clear();
16d1a7b7 553 if(!this->leave_heaters_on && !this->override_leave_heaters_on) {
bab4e1bd 554 // save current temperatures, get a vector of all the controllers data
56a6c8c1
JM
555 std::vector<struct pad_temperature> controllers;
556 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
bab4e1bd 557 if (ok) {
f6fc8c0d 558 // query each heater and save the target temperature if on
56a6c8c1 559 for (auto &c : controllers) {
f6fc8c0d 560 // TODO see if in exclude list
56a6c8c1
JM
561 if(c.target_temperature > 0) {
562 this->saved_temperatures[c.id]= c.target_temperature;
f6fc8c0d 563 }
02e4b295
JM
564 }
565 }
02e4b295 566
f6fc8c0d
JM
567 // turn off heaters that were on
568 for(auto& h : this->saved_temperatures) {
569 float t= 0;
570 PublicData::set_value( temperature_control_checksum, h.first, &t );
571 }
02e4b295
JM
572 }
573
d467fcad
JM
574 // execute optional gcode if defined
575 if(!after_suspend_gcode.empty()) {
576 struct SerialMessage message;
577 message.message = after_suspend_gcode;
578 message.stream = &(StreamOutput::NullStream);
579 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
580 }
02e4b295 581
212caccd 582 THEKERNEL->streams->printf("// Print Suspended, enter resume to continue printing\n");
02e4b295
JM
583}
584
585/**
586resume the suspended print
5871. restore the temperatures and wait for them to get up to temp
d467fcad
JM
5882. optionally run before_resume gcode if specified
5893. restore the position it was at and E and any other saved state
5904. resume sd print or send resume upstream
02e4b295
JM
591*/
592void Player::resume_command(string parameters, StreamOutput *stream )
593{
594 if(!suspended) {
595 stream->printf("Not suspended\n");
596 return;
597 }
598
edcfc706 599 stream->printf("resuming print...\n");
02e4b295 600
02e4b295
JM
601 // wait for them to reach temp
602 if(!this->saved_temperatures.empty()) {
212caccd
JM
603 // set heaters to saved temps
604 for(auto& h : this->saved_temperatures) {
605 float t= h.second;
606 PublicData::set_value( temperature_control_checksum, h.first, &t );
607 }
02e4b295
JM
608 stream->printf("Waiting for heaters...\n");
609 bool wait= true;
610 uint32_t tus= us_ticker_read(); // mbed call
611 while(wait) {
612 wait= false;
613
614 bool timeup= false;
615 if((us_ticker_read() - tus) >= 1000000) { // print every 1 second
616 timeup= true;
617 tus= us_ticker_read(); // mbed call
618 }
619
620 for(auto& h : this->saved_temperatures) {
3bfb2639 621 struct pad_temperature temp;
56a6c8c1 622 if(PublicData::get_value( temperature_control_checksum, current_temperature_checksum, h.first, &temp )) {
3bfb2639
JM
623 if(timeup)
624 stream->printf("%s:%3.1f /%3.1f @%d ", temp.designator.c_str(), temp.current_temperature, ((temp.target_temperature == -1) ? 0.0 : temp.target_temperature), temp.pwm);
625 wait= wait || (temp.current_temperature < h.second);
02e4b295
JM
626 }
627 }
628 if(timeup) stream->printf("\n");
629
630 if(wait)
631 THEKERNEL->call_event(ON_IDLE, this);
212caccd
JM
632
633 if(THEKERNEL->is_halted()) {
634 // abort temp wait and rest of resume
635 THEKERNEL->streams->printf("Resume aborted by kill\n");
c8bac202 636 THEROBOT->pop_state();
212caccd
JM
637 this->saved_temperatures.clear();
638 suspended= false;
639 return;
640 }
02e4b295
JM
641 }
642 }
643
d467fcad
JM
644 // execute optional gcode if defined
645 if(!before_resume_gcode.empty()) {
646 stream->printf("Executing before resume gcode...\n");
647 struct SerialMessage message;
648 message.message = before_resume_gcode;
649 message.stream = &(StreamOutput::NullStream);
650 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
651 }
652
02e4b295
JM
653 // Restore position
654 stream->printf("Restoring saved XYZ positions and state...\n");
c8bac202
JM
655 THEROBOT->pop_state();
656 bool abs_mode= THEROBOT->absolute_mode; // what mode we were in
212caccd 657 // force absolute mode for restoring position, then set to the saved relative/absolute mode
c8bac202 658 THEROBOT->absolute_mode= true;
02e4b295 659 {
3702f300 660 // NOTE position was saved in MCS so must use G53 to restore position
212caccd 661 char buf[128];
3702f300
JM
662 snprintf(buf, sizeof(buf), "G53 G0 X%f Y%f Z%f", saved_position[0], saved_position[1], saved_position[2]);
663 struct SerialMessage message;
664 message.message = buf;
665 message.stream = &(StreamOutput::NullStream);
666 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
02e4b295 667 }
c8bac202 668 THEROBOT->absolute_mode= abs_mode;
02e4b295 669
d467fcad
JM
670 // restore extruder state
671 PublicData::set_value( extruder_checksum, restore_state_checksum, nullptr );
02e4b295
JM
672
673 stream->printf("Resuming print\n");
674
675 if(this->was_playing_file) {
676 this->playing_file = true;
212caccd 677 this->was_playing_file= false;
02e4b295 678 }else{
d467fcad 679 // Send resume to host
02e4b295
JM
680 THEKERNEL->streams->printf("// action:resume\r\n");
681 }
682
683 // clean up
684 this->saved_temperatures.clear();
685 suspended= false;
686}