remove debug printout
[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
c4e56997
JM
116 this->current_stream = &(StreamOutput::NullStream);
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;
1e6e89ee 174 this->current_stream = &(StreamOutput::NullStream);
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
6d4d88bc
CG
187 this->current_stream = &(StreamOutput::NullStream);
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 ) {
d0ef6382 293 this->current_stream = &(StreamOutput::NullStream);
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
419 this->current_stream->printf("%s", buf);
addfb453 420 struct SerialMessage message;
9ef9c12a 421 message.message = buf;
681a62d7 422 message.stream = this->current_stream;
36aa8b0b
JM
423
424 // waits for the queue to have enough room
314ab8f7 425 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message);
9ef9c12a
JM
426 played_cnt += len;
427 return; // we feed one line per main loop
63ada22a 428
36aa8b0b 429 } else {
9ef9c12a
JM
430 // discard long line
431 this->current_stream->printf("Warning: Discarded long line\n");
36aa8b0b 432 discard = true;
addfb453 433 }
c4e56997
JM
434 }
435
b41aedba 436 this->playing_file = false;
36aa8b0b
JM
437 this->filename = "";
438 played_cnt = 0;
439 file_size = 0;
addfb453 440 fclose(this->current_file_handler);
36aa8b0b
JM
441 current_file_handler = NULL;
442 this->current_stream = NULL;
c4e56997
JM
443
444 if(this->reply_stream != NULL) {
445 // if we were printing from an M command from pronterface we need to send this back
446 this->reply_stream->printf("Done printing file\r\n");
36aa8b0b 447 this->reply_stream = NULL;
c4e56997 448 }
663d7943
L
449 }
450}
451
36aa8b0b
JM
452void Player::on_get_public_data(void *argument)
453{
454 PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
58d6d841
JM
455
456 if(!pdr->starts_with(player_checksum)) return;
457
5f1a896b 458 if(pdr->second_element_is(is_playing_checksum) || pdr->second_element_is(is_suspended_checksum)) {
58d6d841 459 static bool bool_data;
5f1a896b 460 bool_data = pdr->second_element_is(is_playing_checksum) ? this->playing_file : this->suspended;
58d6d841
JM
461 pdr->set_data_ptr(&bool_data);
462 pdr->set_taken();
c4e56997 463
36aa8b0b 464 } else if(pdr->second_element_is(get_progress_checksum)) {
58d6d841
JM
465 static struct pad_progress p;
466 if(file_size > 0 && playing_file) {
36aa8b0b
JM
467 p.elapsed_secs = this->elapsed_secs;
468 p.percent_complete = (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size;
469 p.filename = this->filename;
58d6d841
JM
470 pdr->set_data_ptr(&p);
471 pdr->set_taken();
472 }
473 }
35089dc7
JM
474}
475
36aa8b0b
JM
476void Player::on_set_public_data(void *argument)
477{
478 PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
58d6d841
JM
479
480 if(!pdr->starts_with(player_checksum)) return;
481
482 if(pdr->second_element_is(abort_play_checksum)) {
4c8afa75
JM
483 abort_command("", &(StreamOutput::NullStream));
484 pdr->set_taken();
58d6d841 485 }
35089dc7 486}
02e4b295
JM
487
488/**
489Suspend a print in progress
4901. send pause to upstream host, or pause if printing from sd
0f11f650 4911a. loop on_main_loop several times to clear any buffered commmands
02e4b295
JM
4922. wait for empty queue
4933. save the current position, extruder position, temperatures - any state that would need to be restored
4944. retract by specifed amount either on command line or in config
4955. turn off heaters.
d467fcad 4966. optionally run after_suspend gcode (either in config or on command line)
02e4b295
JM
497
498User may jog or remove and insert filament at this point, extruding or retracting as needed
499
500*/
501void Player::suspend_command(string parameters, StreamOutput *stream )
502{
503 if(suspended) {
504 stream->printf("Already suspended\n");
505 return;
506 }
507
edcfc706 508 stream->printf("Suspending print, waiting for queue to empty...\n");
02e4b295 509
16d1a7b7
JM
510 // override the leave_heaters_on setting
511 this->override_leave_heaters_on= (parameters == "h");
512
02e4b295
JM
513 suspended= true;
514 if( this->playing_file ) {
515 // pause an sd print
516 this->playing_file = false;
517 this->was_playing_file= true;
518 }else{
519 // send pause to upstream host, we send it on all ports as we don't know which it is on
520 THEKERNEL->streams->printf("// action:pause\r\n");
521 this->was_playing_file= false;
522 }
523
0f11f650
JM
524 // we need to allow main loop to cycle a few times to clear any buffered commands in the serial streams etc
525 suspend_loops= 10;
0f11f650
JM
526}
527
528// this completes the suspend
529void Player::suspend_part2()
530{
212caccd
JM
531 // need to use streams here as the original stream may have changed
532 THEKERNEL->streams->printf("// Waiting for queue to empty (Host must stop sending)...\n");
02e4b295 533 // wait for queue to empty
04782655 534 THEKERNEL->conveyor->wait_for_idle();
02e4b295 535
212caccd 536 THEKERNEL->streams->printf("// Saving current state...\n");
02e4b295
JM
537
538 // save current XYZ position
c8bac202 539 THEROBOT->get_axis_position(this->saved_position);
02e4b295 540
d467fcad
JM
541 // save current extruder state
542 PublicData::set_value( extruder_checksum, save_state_checksum, nullptr );
02e4b295 543
212caccd 544 // save state use M120
c8bac202 545 THEROBOT->push_state();
02e4b295 546
f6fc8c0d
JM
547 // TODO retract by optional amount...
548
549 this->saved_temperatures.clear();
16d1a7b7 550 if(!this->leave_heaters_on && !this->override_leave_heaters_on) {
bab4e1bd 551 // save current temperatures, get a vector of all the controllers data
56a6c8c1
JM
552 std::vector<struct pad_temperature> controllers;
553 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
bab4e1bd 554 if (ok) {
f6fc8c0d 555 // query each heater and save the target temperature if on
56a6c8c1 556 for (auto &c : controllers) {
f6fc8c0d 557 // TODO see if in exclude list
56a6c8c1
JM
558 if(c.target_temperature > 0) {
559 this->saved_temperatures[c.id]= c.target_temperature;
f6fc8c0d 560 }
02e4b295
JM
561 }
562 }
02e4b295 563
f6fc8c0d
JM
564 // turn off heaters that were on
565 for(auto& h : this->saved_temperatures) {
566 float t= 0;
567 PublicData::set_value( temperature_control_checksum, h.first, &t );
568 }
02e4b295
JM
569 }
570
d467fcad
JM
571 // execute optional gcode if defined
572 if(!after_suspend_gcode.empty()) {
573 struct SerialMessage message;
574 message.message = after_suspend_gcode;
575 message.stream = &(StreamOutput::NullStream);
576 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
577 }
02e4b295 578
212caccd 579 THEKERNEL->streams->printf("// Print Suspended, enter resume to continue printing\n");
02e4b295
JM
580}
581
582/**
583resume the suspended print
5841. restore the temperatures and wait for them to get up to temp
d467fcad
JM
5852. optionally run before_resume gcode if specified
5863. restore the position it was at and E and any other saved state
5874. resume sd print or send resume upstream
02e4b295
JM
588*/
589void Player::resume_command(string parameters, StreamOutput *stream )
590{
591 if(!suspended) {
592 stream->printf("Not suspended\n");
593 return;
594 }
595
edcfc706 596 stream->printf("resuming print...\n");
02e4b295 597
02e4b295
JM
598 // wait for them to reach temp
599 if(!this->saved_temperatures.empty()) {
212caccd
JM
600 // set heaters to saved temps
601 for(auto& h : this->saved_temperatures) {
602 float t= h.second;
603 PublicData::set_value( temperature_control_checksum, h.first, &t );
604 }
02e4b295
JM
605 stream->printf("Waiting for heaters...\n");
606 bool wait= true;
607 uint32_t tus= us_ticker_read(); // mbed call
608 while(wait) {
609 wait= false;
610
611 bool timeup= false;
612 if((us_ticker_read() - tus) >= 1000000) { // print every 1 second
613 timeup= true;
614 tus= us_ticker_read(); // mbed call
615 }
616
617 for(auto& h : this->saved_temperatures) {
3bfb2639 618 struct pad_temperature temp;
56a6c8c1 619 if(PublicData::get_value( temperature_control_checksum, current_temperature_checksum, h.first, &temp )) {
3bfb2639
JM
620 if(timeup)
621 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);
622 wait= wait || (temp.current_temperature < h.second);
02e4b295
JM
623 }
624 }
625 if(timeup) stream->printf("\n");
626
627 if(wait)
628 THEKERNEL->call_event(ON_IDLE, this);
212caccd
JM
629
630 if(THEKERNEL->is_halted()) {
631 // abort temp wait and rest of resume
632 THEKERNEL->streams->printf("Resume aborted by kill\n");
c8bac202 633 THEROBOT->pop_state();
212caccd
JM
634 this->saved_temperatures.clear();
635 suspended= false;
636 return;
637 }
02e4b295
JM
638 }
639 }
640
d467fcad
JM
641 // execute optional gcode if defined
642 if(!before_resume_gcode.empty()) {
643 stream->printf("Executing before resume gcode...\n");
644 struct SerialMessage message;
645 message.message = before_resume_gcode;
646 message.stream = &(StreamOutput::NullStream);
647 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
648 }
649
02e4b295
JM
650 // Restore position
651 stream->printf("Restoring saved XYZ positions and state...\n");
c8bac202
JM
652 THEROBOT->pop_state();
653 bool abs_mode= THEROBOT->absolute_mode; // what mode we were in
212caccd 654 // force absolute mode for restoring position, then set to the saved relative/absolute mode
c8bac202 655 THEROBOT->absolute_mode= true;
02e4b295 656 {
3702f300 657 // NOTE position was saved in MCS so must use G53 to restore position
212caccd 658 char buf[128];
3702f300
JM
659 snprintf(buf, sizeof(buf), "G53 G0 X%f Y%f Z%f", saved_position[0], saved_position[1], saved_position[2]);
660 struct SerialMessage message;
661 message.message = buf;
662 message.stream = &(StreamOutput::NullStream);
663 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
02e4b295 664 }
c8bac202 665 THEROBOT->absolute_mode= abs_mode;
02e4b295 666
d467fcad
JM
667 // restore extruder state
668 PublicData::set_value( extruder_checksum, restore_state_checksum, nullptr );
02e4b295
JM
669
670 stream->printf("Resuming print\n");
671
672 if(this->was_playing_file) {
673 this->playing_file = true;
212caccd 674 this->was_playing_file= false;
02e4b295 675 }else{
d467fcad 676 // Send resume to host
02e4b295
JM
677 THEKERNEL->streams->printf("// action:resume\r\n");
678 }
679
680 // clean up
681 this->saved_temperatures.clear();
682 suspended= false;
683}