X-Git-Url: http://git.hcoop.net/clinton/Smoothieware.git/blobdiff_plain/d5c301789d3ac90452bd999ee7e538d7a479400c..50a6ccf694ef5aae54a3c30f03700744f3beffd3:/src/modules/utils/player/Player.cpp diff --git a/src/modules/utils/player/Player.cpp b/src/modules/utils/player/Player.cpp index 1db71227..cb7f319a 100644 --- a/src/modules/utils/player/Player.cpp +++ b/src/modules/utils/player/Player.cpp @@ -5,27 +5,58 @@ You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ +#include "Player.h" #include "libs/Kernel.h" -#include "Player.h" +#include "Robot.h" #include "libs/nuts_bolts.h" #include "libs/utils.h" +#include "SerialConsole.h" #include "libs/SerialMessage.h" +#include "libs/StreamOutputPool.h" #include "libs/StreamOutput.h" +#include "Gcode.h" +#include "checksumm.h" +#include "Config.h" +#include "ConfigValue.h" +#include "SDFAT.h" + #include "modules/robot/Conveyor.h" #include "DirHandle.h" #include "PublicDataRequest.h" +#include "PublicData.h" #include "PlayerPublicAccess.h" +#include "TemperatureControlPublicAccess.h" +#include "TemperatureControlPool.h" +#include "ExtruderPublicAccess.h" + +#include +#include +#include -#define play_command_checksum CHECKSUM("play") -#define progress_command_checksum CHECKSUM("progress") -#define abort_command_checksum CHECKSUM("abort") -#define on_boot_gcode_checksum CHECKSUM("on_boot_gcode") -#define on_boot_gcode_enable_checksum CHECKSUM("on_boot_gcode_enable") +#include "mbed.h" -void Player::on_module_loaded(){ +#define on_boot_gcode_checksum CHECKSUM("on_boot_gcode") +#define on_boot_gcode_enable_checksum CHECKSUM("on_boot_gcode_enable") +#define after_suspend_gcode_checksum CHECKSUM("after_suspend_gcode") +#define before_resume_gcode_checksum CHECKSUM("before_resume_gcode") +#define leave_heaters_on_suspend_checksum CHECKSUM("leave_heaters_on_suspend") + +extern SDFAT mounter; + +Player::Player() +{ this->playing_file = false; + this->current_file_handler = nullptr; this->booted = false; + this->elapsed_secs = 0; + this->reply_stream = nullptr; + this->suspended= false; + this->suspend_loops= 0; +} + +void Player::on_module_loaded() +{ this->register_for_event(ON_CONSOLE_LINE_RECEIVED); this->register_for_event(ON_MAIN_LOOP); this->register_for_event(ON_SECOND_TICK); @@ -35,26 +66,45 @@ void Player::on_module_loaded(){ this->on_boot_gcode = THEKERNEL->config->value(on_boot_gcode_checksum)->by_default("/sd/on_boot.gcode")->as_string(); this->on_boot_gcode_enable = THEKERNEL->config->value(on_boot_gcode_enable_checksum)->by_default(true)->as_bool(); - this->elapsed_secs= 0; - this->reply_stream= NULL; + + this->after_suspend_gcode = THEKERNEL->config->value(after_suspend_gcode_checksum)->by_default("")->as_string(); + this->before_resume_gcode = THEKERNEL->config->value(before_resume_gcode_checksum)->by_default("")->as_string(); + std::replace( this->after_suspend_gcode.begin(), this->after_suspend_gcode.end(), '_', ' '); // replace _ with space + std::replace( this->before_resume_gcode.begin(), this->before_resume_gcode.end(), '_', ' '); // replace _ with space + this->leave_heaters_on = THEKERNEL->config->value(leave_heaters_on_suspend_checksum)->by_default(false)->as_bool(); +} + +void Player::on_second_tick(void *) +{ + if(this->playing_file) this->elapsed_secs++; } -void Player::on_second_tick(void*) { - if (!THEKERNEL->pauser->paused()) this->elapsed_secs++; +// extract any options found on line, terminates args at the space before the first option (-v) +// eg this is a file.gcode -v +// will return -v and set args to this is a file.gcode +string Player::extract_options(string& args) +{ + string opts; + size_t pos= args.find(" -"); + if(pos != string::npos) { + opts= args.substr(pos); + args= args.substr(0, pos); + } + + return opts; } -void Player::on_gcode_received(void *argument) { - Gcode *gcode = static_cast(argument); - string args= get_arguments(gcode->command); +void Player::on_gcode_received(void *argument) +{ + Gcode *gcode = static_cast(argument); + string args = get_arguments(gcode->get_command()); if (gcode->has_m) { if (gcode->m == 21) { // Dummy code; makes Octoprint happy -- supposed to initialize SD card - gcode->mark_as_taken(); + mounter.remount(); gcode->stream->printf("SD card ok\r\n"); - }else if (gcode->m == 23) { // select file - gcode->mark_as_taken(); - // Get filename - this->filename= "/sd/" + shift_parameter( args ); + } else if (gcode->m == 23) { // select file + this->filename = "/sd/" + args; // filename is whatever is in args this->current_stream = &(StreamOutput::NullStream); if(this->current_file_handler != NULL) { @@ -63,46 +113,43 @@ void Player::on_gcode_received(void *argument) { } this->current_file_handler = fopen( this->filename.c_str(), "r"); - if(this->current_file_handler == NULL){ + if(this->current_file_handler == NULL) { gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str()); return; - }else{ + } else { // get size of file int result = fseek(this->current_file_handler, 0, SEEK_END); - if (0 != result){ - this->file_size= 0; - }else{ - this->file_size= ftell(this->current_file_handler); - fseek(this->current_file_handler, 0, SEEK_SET); + if (0 != result) { + this->file_size = 0; + } else { + this->file_size = ftell(this->current_file_handler); + fseek(this->current_file_handler, 0, SEEK_SET); } gcode->stream->printf("File opened:%s Size:%ld\r\n", this->filename.c_str(), this->file_size); gcode->stream->printf("File selected\r\n"); } - this->played_cnt= 0; - this->elapsed_secs= 0; + this->played_cnt = 0; + this->elapsed_secs = 0; - }else if (gcode->m == 24) { // start print - gcode->mark_as_taken(); + } else if (gcode->m == 24) { // start print if (this->current_file_handler != NULL) { this->playing_file = true; // this would be a problem if the stream goes away before the file has finished, // so we attach it to the kernel stream, however network connections from pronterface // do not connect to the kernel streams so won't see this FIXME - this->reply_stream= THEKERNEL->streams; + this->reply_stream = THEKERNEL->streams; } - }else if (gcode->m == 25) { // pause print - gcode->mark_as_taken(); + } else if (gcode->m == 25) { // pause print this->playing_file = false; - }else if (gcode->m == 26) { // Reset print. Slightly different than M26 in Marlin and the rest - gcode->mark_as_taken(); - if(this->current_file_handler != NULL){ - string currentfn= this->filename.c_str(); - unsigned long old_size= this->file_size; + } else if (gcode->m == 26) { // Reset print. Slightly different than M26 in Marlin and the rest + if(this->current_file_handler != NULL) { + string currentfn = this->filename.c_str(); + unsigned long old_size = this->file_size; // abort the print abort_command("", gcode->stream); @@ -111,27 +158,24 @@ void Player::on_gcode_received(void *argument) { // reload the last file opened this->current_file_handler = fopen(currentfn.c_str() , "r"); - if(this->current_file_handler == NULL){ + if(this->current_file_handler == NULL) { gcode->stream->printf("file.open failed: %s\r\n", currentfn.c_str()); - }else{ - this->filename= currentfn; - this->file_size= old_size; + } else { + this->filename = currentfn; + this->file_size = old_size; this->current_stream = &(StreamOutput::NullStream); } } - - }else{ + } else { gcode->stream->printf("No file loaded\r\n"); } - }else if (gcode->m == 27) { // report print progress, in format used by Marlin - gcode->mark_as_taken(); + } else if (gcode->m == 27) { // report print progress, in format used by Marlin progress_command("-b", gcode->stream); - }else if (gcode->m == 32) { // select file and start print - gcode->mark_as_taken(); + } else if (gcode->m == 32) { // select file and start print // Get filename - this->filename= "/sd/" + shift_parameter( args ); + this->filename = "/sd/" + args; // filename is whatever is in args including spaces this->current_stream = &(StreamOutput::NullStream); if(this->current_file_handler != NULL) { @@ -140,48 +184,82 @@ void Player::on_gcode_received(void *argument) { } this->current_file_handler = fopen( this->filename.c_str(), "r"); - if(this->current_file_handler == NULL){ + if(this->current_file_handler == NULL) { gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str()); - }else{ + } else { this->playing_file = true; + + // get size of file + int result = fseek(this->current_file_handler, 0, SEEK_END); + if (0 != result) { + file_size = 0; + } else { + file_size = ftell(this->current_file_handler); + fseek(this->current_file_handler, 0, SEEK_SET); + } } + + this->played_cnt = 0; + this->elapsed_secs = 0; + + } else if (gcode->m == 600) { // suspend print, Not entirely Marlin compliant, M600.1 will leave the heaters on + this->suspend_command((gcode->subcode == 1)?"h":"", gcode->stream); + + } else if (gcode->m == 601) { // resume print + this->resume_command("", gcode->stream); } } } - // When a new line is received, check if it is a command, and if it is, act upon it -void Player::on_console_line_received( void* argument ){ - SerialMessage new_message = *static_cast(argument); +void Player::on_console_line_received( void *argument ) +{ + if(THEKERNEL->is_halted()) return; // if in halted state ignore any commands - // ignore comments - if(new_message.message[0] == ';') return; + SerialMessage new_message = *static_cast(argument); + + // ignore comments and blank lines and if this is a G code then also ignore it + char first_char = new_message.message[0]; + if(strchr(";( \n\rGMTN", first_char) != NULL) return; string possible_command = new_message.message; + string cmd = shift_parameter(possible_command); //new_message.stream->printf("Received %s\r\n", possible_command.c_str()); - // We don't compare to a string but to a checksum of that string, this saves some space in flash memory - unsigned short check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo: put this method somewhere more convenient - // Act depending on command - if (check_sum == play_command_checksum) - this->play_command( get_arguments(possible_command), new_message.stream ); - else if (check_sum == progress_command_checksum) - this->progress_command(get_arguments(possible_command),new_message.stream ); - else if (check_sum == abort_command_checksum) - this->abort_command(get_arguments(possible_command),new_message.stream ); + if (cmd == "play"){ + this->play_command( possible_command, new_message.stream ); + }else if (cmd == "progress"){ + this->progress_command( possible_command, new_message.stream ); + }else if (cmd == "abort") { + this->abort_command( possible_command, new_message.stream ); + }else if (cmd == "suspend") { + this->suspend_command( possible_command, new_message.stream ); + }else if (cmd == "resume") { + this->resume_command( possible_command, new_message.stream ); + } } // Play a gcode file by considering each line as if it was received on the serial console -void Player::play_command( string parameters, StreamOutput* stream ){ +void Player::play_command( string parameters, StreamOutput *stream ) +{ + // extract any options from the line and terminate the line there + string options= extract_options(parameters); + // Get filename which is the entire parameter line upto any options found or entire line + this->filename = absolute_from_relative(parameters); + + if(this->playing_file || this->suspended) { + stream->printf("Currently printing, abort print first\r\n"); + return; + } - // Get filename - this->filename = absolute_from_relative(shift_parameter( parameters )); - string options = shift_parameter( parameters ); + if(this->current_file_handler != NULL) { // must have been a paused print + fclose(this->current_file_handler); + } this->current_file_handler = fopen( this->filename.c_str(), "r"); - if(this->current_file_handler == NULL){ + if(this->current_file_handler == NULL) { stream->printf("File not found: %s\r\n", this->filename.c_str()); return; } @@ -191,160 +269,194 @@ void Player::play_command( string parameters, StreamOutput* stream ){ this->playing_file = true; // Output to the current stream if we were passed the -v ( verbose ) option - if( options.find_first_of("Vv") == string::npos ){ + if( options.find_first_of("Vv") == string::npos ) { this->current_stream = &(StreamOutput::NullStream); - }else{ + } else { // we send to the kernels stream as it cannot go away this->current_stream = THEKERNEL->streams; } // get size of file int result = fseek(this->current_file_handler, 0, SEEK_END); - if (0 != result){ - stream->printf("WARNING - Could not get file size\r\n"); - file_size= 0; - }else{ - file_size= ftell(this->current_file_handler); - fseek(this->current_file_handler, 0, SEEK_SET); - stream->printf(" File size %ld\r\n", file_size); + if (0 != result) { + stream->printf("WARNING - Could not get file size\r\n"); + file_size = 0; + } else { + file_size = ftell(this->current_file_handler); + fseek(this->current_file_handler, 0, SEEK_SET); + stream->printf(" File size %ld\r\n", file_size); } - this->played_cnt= 0; - this->elapsed_secs= 0; + this->played_cnt = 0; + this->elapsed_secs = 0; } -void Player::progress_command( string parameters, StreamOutput* stream ){ +void Player::progress_command( string parameters, StreamOutput *stream ) +{ // get options - string options = shift_parameter( parameters ); + string options = shift_parameter( parameters ); + bool sdprinting= options.find_first_of("Bb") != string::npos; + + if(!playing_file && current_file_handler != NULL) { + if(sdprinting) + stream->printf("SD printing byte %lu/%lu\r\n", played_cnt, file_size); + else + stream->printf("SD print is paused at %lu/%lu\r\n", played_cnt, file_size); + return; - if(!playing_file) { + } else if(!playing_file) { stream->printf("Not currently playing\r\n"); return; } if(file_size > 0) { - unsigned long est= 0; + unsigned long est = 0; if(this->elapsed_secs > 10) { - unsigned long bytespersec= played_cnt / this->elapsed_secs; + unsigned long bytespersec = played_cnt / this->elapsed_secs; if(bytespersec > 0) - est= (file_size - played_cnt) / bytespersec; + est = (file_size - played_cnt) / bytespersec; } - unsigned int pcnt= (file_size - (file_size - played_cnt)) * 100 / file_size; + unsigned int pcnt = (file_size - (file_size - played_cnt)) * 100 / file_size; // If -b or -B is passed, report in the format used by Marlin and the others. - if ( options.find_first_of("Bb") == string::npos ){ - stream->printf("%u %% complete, elapsed time: %lu s", pcnt, this->elapsed_secs); - if(est > 0){ + if (!sdprinting) { + stream->printf("file: %s, %u %% complete, elapsed time: %lu s", this->filename.c_str(), pcnt, this->elapsed_secs); + if(est > 0) { stream->printf(", est time: %lu s", est); } stream->printf("\r\n"); - }else{ + } else { stream->printf("SD printing byte %lu/%lu\r\n", played_cnt, file_size); } - }else{ + } else { stream->printf("File size is unknown\r\n"); } } -void Player::abort_command( string parameters, StreamOutput* stream ){ - if(!playing_file) { +void Player::abort_command( string parameters, StreamOutput *stream ) +{ + if(!playing_file && current_file_handler == NULL) { stream->printf("Not currently playing\r\n"); return; } + suspended= false; playing_file = false; - played_cnt= 0; - file_size= 0; - this->filename= ""; - this->current_stream= NULL; + played_cnt = 0; + file_size = 0; + this->filename = ""; + this->current_stream = NULL; fclose(current_file_handler); - stream->printf("Aborted playing file\r\n"); + current_file_handler = NULL; + if(parameters.empty()) { + // clear out the block queue, will wait until queue is empty + // MUST be called in on_main_loop to make sure there are no blocked main loops waiting to put something on the queue + THEKERNEL->conveyor->flush_queue(); + + // 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 + THEKERNEL->robot->reset_position_from_current_actuator_position(); + } + stream->printf("Aborted playing or paused file. Please turn any heaters off manually\r\n"); } -void Player::on_main_loop(void* argument){ +void Player::on_main_loop(void *argument) +{ + if(suspended && suspend_loops > 0) { + // if we are suspended we need to allow main loop to cycle a few times then finish off the suspend processing + if(--suspend_loops == 0) { + suspend_part2(); + return; + } + } + if( !this->booted ) { this->booted = true; - if( this->on_boot_gcode_enable ){ + if( this->on_boot_gcode_enable ) { this->play_command(this->on_boot_gcode, THEKERNEL->serial); - }else{ + } else { //THEKERNEL->serial->printf("On boot gcode disabled! skipping...\n"); } } - if( this->playing_file ){ - string buffer; - bool discard= false; - int c; - buffer.reserve(20); - // Print each line of the file - while ((c = fgetc(this->current_file_handler)) != EOF){ - if (c == '\n'){ - if(discard) { - // we hit a long line and discarded it - discard= false; - buffer.clear(); - this->current_stream->printf("Warning: Discarded long line\n"); - return; + if( this->playing_file ) { + if(THEKERNEL->is_halted()) { + abort_command("1", &(StreamOutput::NullStream)); + return; + } + + char buf[130]; // lines upto 128 characters are allowed, anything longer is discarded + bool discard = false; + + while(fgets(buf, sizeof(buf), this->current_file_handler) != NULL) { + int len = strlen(buf); + if(len == 0) continue; // empty line? should not be possible + if(buf[len - 1] == '\n' || feof(this->current_file_handler)) { + if(discard) { // we are discarding a long line + discard = false; + continue; } - this->current_stream->printf("%s\n", buffer.c_str()); + if(len == 1) continue; // empty line + + this->current_stream->printf("%s", buf); struct SerialMessage message; - message.message = buffer; - message.stream = &(StreamOutput::NullStream); // we don't really need to see the ok - // wait for the queue to have enough room that a serial message could still be received before sending - THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message); - played_cnt += buffer.size(); - buffer.clear(); - return; + message.message = buf; + message.stream = this->current_stream; - }else if(buffer.size() > 128) { - // discard rest of line - discard= true; + // waits for the queue to have enough room + THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message); + played_cnt += len; + return; // we feed one line per main loop - }else{ - buffer += c; + } else { + // discard long line + this->current_stream->printf("Warning: Discarded long line\n"); + discard = true; } } this->playing_file = false; - this->filename= ""; - played_cnt= 0; - file_size= 0; + this->filename = ""; + played_cnt = 0; + file_size = 0; fclose(this->current_file_handler); - this->current_stream= NULL; + current_file_handler = NULL; + this->current_stream = NULL; if(this->reply_stream != NULL) { // if we were printing from an M command from pronterface we need to send this back this->reply_stream->printf("Done printing file\r\n"); - this->reply_stream= NULL; + this->reply_stream = NULL; } } } -void Player::on_get_public_data(void* argument) { - PublicDataRequest* pdr = static_cast(argument); +void Player::on_get_public_data(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); if(!pdr->starts_with(player_checksum)) return; - if(pdr->second_element_is(is_playing_checksum)) { + if(pdr->second_element_is(is_playing_checksum) || pdr->second_element_is(is_suspended_checksum)) { static bool bool_data; - bool_data= this->playing_file; + bool_data = pdr->second_element_is(is_playing_checksum) ? this->playing_file : this->suspended; pdr->set_data_ptr(&bool_data); pdr->set_taken(); - }else if(pdr->second_element_is(get_progress_checksum)) { + } else if(pdr->second_element_is(get_progress_checksum)) { static struct pad_progress p; if(file_size > 0 && playing_file) { - p.elapsed_secs= this->elapsed_secs; - p.percent_complete= (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size; - p.filename= this->filename; + p.elapsed_secs = this->elapsed_secs; + p.percent_complete = (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size; + p.filename = this->filename; pdr->set_data_ptr(&p); pdr->set_taken(); } } } -void Player::on_set_public_data(void* argument) { - PublicDataRequest* pdr = static_cast(argument); +void Player::on_set_public_data(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); if(!pdr->starts_with(player_checksum)) return; @@ -353,3 +465,198 @@ void Player::on_set_public_data(void* argument) { pdr->set_taken(); } } + +/** +Suspend a print in progress +1. send pause to upstream host, or pause if printing from sd +1a. loop on_main_loop several times to clear any buffered commmands +2. wait for empty queue +3. save the current position, extruder position, temperatures - any state that would need to be restored +4. retract by specifed amount either on command line or in config +5. turn off heaters. +6. optionally run after_suspend gcode (either in config or on command line) + +User may jog or remove and insert filament at this point, extruding or retracting as needed + +*/ +void Player::suspend_command(string parameters, StreamOutput *stream ) +{ + if(suspended) { + stream->printf("Already suspended\n"); + return; + } + + stream->printf("Suspending print, waiting for queue to empty...\n"); + + // override the leave_heaters_on setting + this->override_leave_heaters_on= (parameters == "h"); + + suspended= true; + if( this->playing_file ) { + // pause an sd print + this->playing_file = false; + this->was_playing_file= true; + }else{ + // send pause to upstream host, we send it on all ports as we don't know which it is on + THEKERNEL->streams->printf("// action:pause\r\n"); + this->was_playing_file= false; + } + + // we need to allow main loop to cycle a few times to clear any buffered commands in the serial streams etc + suspend_loops= 10; +} + +// this completes the suspend +void Player::suspend_part2() +{ + // need to use streams here as the original stream may have changed + THEKERNEL->streams->printf("// Waiting for queue to empty (Host must stop sending)...\n"); + // wait for queue to empty + THEKERNEL->conveyor->wait_for_empty_queue(); + + THEKERNEL->streams->printf("// Saving current state...\n"); + + // save current XYZ position + THEKERNEL->robot->get_axis_position(this->saved_position); + + // save current extruder state + PublicData::set_value( extruder_checksum, save_state_checksum, nullptr ); + + // save state use M120 + THEKERNEL->robot->push_state(); + + // TODO retract by optional amount... + + this->saved_temperatures.clear(); + if(!this->leave_heaters_on && !this->override_leave_heaters_on) { + // save current temperatures, get a vector of all the controllers data + std::vector controllers; + bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers); + if (ok) { + // query each heater and save the target temperature if on + for (auto &c : controllers) { + // TODO see if in exclude list + if(c.target_temperature > 0) { + this->saved_temperatures[c.id]= c.target_temperature; + } + } + } + + // turn off heaters that were on + for(auto& h : this->saved_temperatures) { + float t= 0; + PublicData::set_value( temperature_control_checksum, h.first, &t ); + } + } + + // execute optional gcode if defined + if(!after_suspend_gcode.empty()) { + struct SerialMessage message; + message.message = after_suspend_gcode; + message.stream = &(StreamOutput::NullStream); + THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); + } + + THEKERNEL->streams->printf("// Print Suspended, enter resume to continue printing\n"); +} + +/** +resume the suspended print +1. restore the temperatures and wait for them to get up to temp +2. optionally run before_resume gcode if specified +3. restore the position it was at and E and any other saved state +4. resume sd print or send resume upstream +*/ +void Player::resume_command(string parameters, StreamOutput *stream ) +{ + if(!suspended) { + stream->printf("Not suspended\n"); + return; + } + + stream->printf("resuming print...\n"); + + // wait for them to reach temp + if(!this->saved_temperatures.empty()) { + // set heaters to saved temps + for(auto& h : this->saved_temperatures) { + float t= h.second; + PublicData::set_value( temperature_control_checksum, h.first, &t ); + } + stream->printf("Waiting for heaters...\n"); + bool wait= true; + uint32_t tus= us_ticker_read(); // mbed call + while(wait) { + wait= false; + + bool timeup= false; + if((us_ticker_read() - tus) >= 1000000) { // print every 1 second + timeup= true; + tus= us_ticker_read(); // mbed call + } + + for(auto& h : this->saved_temperatures) { + struct pad_temperature temp; + if(PublicData::get_value( temperature_control_checksum, current_temperature_checksum, h.first, &temp )) { + if(timeup) + 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); + wait= wait || (temp.current_temperature < h.second); + } + } + if(timeup) stream->printf("\n"); + + if(wait) + THEKERNEL->call_event(ON_IDLE, this); + + if(THEKERNEL->is_halted()) { + // abort temp wait and rest of resume + THEKERNEL->streams->printf("Resume aborted by kill\n"); + THEKERNEL->robot->pop_state(); + this->saved_temperatures.clear(); + suspended= false; + return; + } + } + } + + // execute optional gcode if defined + if(!before_resume_gcode.empty()) { + stream->printf("Executing before resume gcode...\n"); + struct SerialMessage message; + message.message = before_resume_gcode; + message.stream = &(StreamOutput::NullStream); + THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); + } + + // Restore position + stream->printf("Restoring saved XYZ positions and state...\n"); + THEKERNEL->robot->pop_state(); + bool abs_mode= THEKERNEL->robot->absolute_mode; // what mode we were in + // force absolute mode for restoring position, then set to the saved relative/absolute mode + THEKERNEL->robot->absolute_mode= true; + { + char buf[128]; + int n = snprintf(buf, sizeof(buf), "G1 X%f Y%f Z%f", saved_position[0], saved_position[1], saved_position[2]); + string g(buf, n); + Gcode gcode(g, &(StreamOutput::NullStream)); + THEKERNEL->call_event(ON_GCODE_RECEIVED, &gcode ); + } + THEKERNEL->robot->absolute_mode= abs_mode; + + // restore extruder state + PublicData::set_value( extruder_checksum, restore_state_checksum, nullptr ); + + stream->printf("Resuming print\n"); + + if(this->was_playing_file) { + this->playing_file = true; + this->was_playing_file= false; + }else{ + // Send resume to host + THEKERNEL->streams->printf("// action:resume\r\n"); + } + + // clean up + this->saved_temperatures.clear(); + suspended= false; +}