Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Smoothie by
Player.cpp
00001 /* 00002 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 00003 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. 00004 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. 00005 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. 00006 */ 00007 00008 00009 #include "libs/Kernel.h" 00010 #include "Player.h" 00011 #include "libs/nuts_bolts.h" 00012 #include "libs/utils.h" 00013 #include "libs/SerialMessage.h" 00014 #include "libs/StreamOutput.h" 00015 #include "modules/robot/Conveyor.h" 00016 #include "DirHandle.h" 00017 #include "PublicDataRequest.h" 00018 #include "PlayerPublicAccess.h" 00019 00020 #define play_command_checksum CHECKSUM("play") 00021 #define progress_command_checksum CHECKSUM("progress") 00022 #define abort_command_checksum CHECKSUM("abort") 00023 #define on_boot_gcode_checksum CHECKSUM("on_boot_gcode") 00024 #define on_boot_gcode_enable_checksum CHECKSUM("on_boot_gcode_enable") 00025 00026 void Player::on_module_loaded(){ 00027 this->playing_file = false; 00028 this->booted = false; 00029 this->register_for_event(ON_CONSOLE_LINE_RECEIVED); 00030 this->register_for_event(ON_MAIN_LOOP); 00031 this->register_for_event(ON_SECOND_TICK); 00032 this->register_for_event(ON_GET_PUBLIC_DATA); 00033 this->register_for_event(ON_SET_PUBLIC_DATA); 00034 this->register_for_event(ON_GCODE_RECEIVED); 00035 00036 this->on_boot_gcode = THEKERNEL->config->value(on_boot_gcode_checksum)->by_default("/sd/on_boot.gcode")->as_string(); 00037 this->on_boot_gcode_enable = THEKERNEL->config->value(on_boot_gcode_enable_checksum)->by_default(true)->as_bool(); 00038 this->elapsed_secs= 0; 00039 this->reply_stream= NULL; 00040 } 00041 00042 void Player::on_second_tick(void*) { 00043 if (!THEKERNEL->pauser->paused()) this->elapsed_secs++; 00044 } 00045 00046 void Player::on_gcode_received(void *argument) { 00047 Gcode *gcode = static_cast<Gcode*>(argument); 00048 string args= get_arguments(gcode->command); 00049 if (gcode->has_m) { 00050 if (gcode->m == 21) { // Dummy code; makes Octoprint happy -- supposed to initialize SD card 00051 gcode->mark_as_taken(); 00052 gcode->stream->printf("SD card ok\r\n"); 00053 00054 }else if (gcode->m == 23) { // select file 00055 gcode->mark_as_taken(); 00056 // Get filename 00057 this->filename= "/sd/" + shift_parameter( args ); 00058 this->current_stream = &(StreamOutput::NullStream); 00059 00060 if(this->current_file_handler != NULL) { 00061 this->playing_file = false; 00062 fclose(this->current_file_handler); 00063 } 00064 this->current_file_handler = fopen( this->filename.c_str(), "r"); 00065 00066 if(this->current_file_handler == NULL){ 00067 gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str()); 00068 return; 00069 00070 }else{ 00071 // get size of file 00072 int result = fseek(this->current_file_handler, 0, SEEK_END); 00073 if (0 != result){ 00074 this->file_size= 0; 00075 }else{ 00076 this->file_size= ftell(this->current_file_handler); 00077 fseek(this->current_file_handler, 0, SEEK_SET); 00078 } 00079 gcode->stream->printf("File opened:%s Size:%ld\r\n", this->filename.c_str(), this->file_size); 00080 gcode->stream->printf("File selected\r\n"); 00081 } 00082 00083 00084 this->played_cnt= 0; 00085 this->elapsed_secs= 0; 00086 00087 }else if (gcode->m == 24) { // start print 00088 gcode->mark_as_taken(); 00089 if (this->current_file_handler != NULL) { 00090 this->playing_file = true; 00091 // this would be a problem if the stream goes away before the file has finished, 00092 // so we attach it to the kernel stream, however network connections from pronterface 00093 // do not connect to the kernel streams so won't see this FIXME 00094 this->reply_stream= THEKERNEL->streams; 00095 } 00096 00097 }else if (gcode->m == 25) { // pause print 00098 gcode->mark_as_taken(); 00099 this->playing_file = false; 00100 00101 }else if (gcode->m == 26) { // Reset print. Slightly different than M26 in Marlin and the rest 00102 gcode->mark_as_taken(); 00103 if(this->current_file_handler != NULL){ 00104 string currentfn= this->filename.c_str(); 00105 unsigned long old_size= this->file_size; 00106 00107 // abort the print 00108 abort_command("", gcode->stream); 00109 00110 if(!currentfn.empty()) { 00111 // reload the last file opened 00112 this->current_file_handler = fopen(currentfn.c_str() , "r"); 00113 00114 if(this->current_file_handler == NULL){ 00115 gcode->stream->printf("file.open failed: %s\r\n", currentfn.c_str()); 00116 }else{ 00117 this->filename= currentfn; 00118 this->file_size= old_size; 00119 this->current_stream = &(StreamOutput::NullStream); 00120 } 00121 } 00122 00123 }else{ 00124 gcode->stream->printf("No file loaded\r\n"); 00125 } 00126 00127 }else if (gcode->m == 27) { // report print progress, in format used by Marlin 00128 gcode->mark_as_taken(); 00129 progress_command("-b", gcode->stream); 00130 00131 }else if (gcode->m == 32) { // select file and start print 00132 gcode->mark_as_taken(); 00133 // Get filename 00134 this->filename= "/sd/" + shift_parameter( args ); 00135 this->current_stream = &(StreamOutput::NullStream); 00136 00137 if(this->current_file_handler != NULL) { 00138 this->playing_file = false; 00139 fclose(this->current_file_handler); 00140 } 00141 00142 this->current_file_handler = fopen( this->filename.c_str(), "r"); 00143 if(this->current_file_handler == NULL){ 00144 gcode->stream->printf("file.open failed: %s\r\n", this->filename.c_str()); 00145 }else{ 00146 this->playing_file = true; 00147 } 00148 } 00149 } 00150 } 00151 00152 00153 // When a new line is received, check if it is a command, and if it is, act upon it 00154 void Player::on_console_line_received( void* argument ){ 00155 SerialMessage new_message = *static_cast<SerialMessage*>(argument); 00156 00157 // ignore comments 00158 if(new_message.message[0] == ';') return; 00159 00160 string possible_command = new_message.message; 00161 00162 //new_message.stream->printf("Received %s\r\n", possible_command.c_str()); 00163 00164 // We don't compare to a string but to a checksum of that string, this saves some space in flash memory 00165 unsigned short check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo: put this method somewhere more convenient 00166 00167 // Act depending on command 00168 if (check_sum == play_command_checksum) 00169 this->play_command( get_arguments(possible_command), new_message.stream ); 00170 else if (check_sum == progress_command_checksum) 00171 this->progress_command(get_arguments(possible_command),new_message.stream ); 00172 else if (check_sum == abort_command_checksum) 00173 this->abort_command(get_arguments(possible_command),new_message.stream ); 00174 } 00175 00176 // Play a gcode file by considering each line as if it was received on the serial console 00177 void Player::play_command( string parameters, StreamOutput* stream ){ 00178 00179 // Get filename 00180 this->filename = absolute_from_relative(shift_parameter( parameters )); 00181 string options = shift_parameter( parameters ); 00182 00183 this->current_file_handler = fopen( this->filename.c_str(), "r"); 00184 if(this->current_file_handler == NULL){ 00185 stream->printf("File not found: %s\r\n", this->filename.c_str()); 00186 return; 00187 } 00188 00189 stream->printf("Playing %s\r\n", this->filename.c_str()); 00190 00191 this->playing_file = true; 00192 00193 // Output to the current stream if we were passed the -v ( verbose ) option 00194 if( options.find_first_of("Vv") == string::npos ){ 00195 this->current_stream = &(StreamOutput::NullStream); 00196 }else{ 00197 // we send to the kernels stream as it cannot go away 00198 this->current_stream = THEKERNEL->streams; 00199 } 00200 00201 // get size of file 00202 int result = fseek(this->current_file_handler, 0, SEEK_END); 00203 if (0 != result){ 00204 stream->printf("WARNING - Could not get file size\r\n"); 00205 file_size= 0; 00206 }else{ 00207 file_size= ftell(this->current_file_handler); 00208 fseek(this->current_file_handler, 0, SEEK_SET); 00209 stream->printf(" File size %ld\r\n", file_size); 00210 } 00211 this->played_cnt= 0; 00212 this->elapsed_secs= 0; 00213 } 00214 00215 void Player::progress_command( string parameters, StreamOutput* stream ){ 00216 00217 // get options 00218 string options = shift_parameter( parameters ); 00219 00220 if(!playing_file) { 00221 stream->printf("Not currently playing\r\n"); 00222 return; 00223 } 00224 00225 if(file_size > 0) { 00226 unsigned long est= 0; 00227 if(this->elapsed_secs > 10) { 00228 unsigned long bytespersec= played_cnt / this->elapsed_secs; 00229 if(bytespersec > 0) 00230 est= (file_size - played_cnt) / bytespersec; 00231 } 00232 00233 unsigned int pcnt= (file_size - (file_size - played_cnt)) * 100 / file_size; 00234 // If -b or -B is passed, report in the format used by Marlin and the others. 00235 if ( options.find_first_of("Bb") == string::npos ){ 00236 stream->printf("%u %% complete, elapsed time: %lu s", pcnt, this->elapsed_secs); 00237 if(est > 0){ 00238 stream->printf(", est time: %lu s", est); 00239 } 00240 stream->printf("\r\n"); 00241 }else{ 00242 stream->printf("SD printing byte %lu/%lu\r\n", played_cnt, file_size); 00243 } 00244 00245 }else{ 00246 stream->printf("File size is unknown\r\n"); 00247 } 00248 } 00249 00250 void Player::abort_command( string parameters, StreamOutput* stream ){ 00251 if(!playing_file) { 00252 stream->printf("Not currently playing\r\n"); 00253 return; 00254 } 00255 playing_file = false; 00256 played_cnt= 0; 00257 file_size= 0; 00258 this->filename= ""; 00259 this->current_stream= NULL; 00260 fclose(current_file_handler); 00261 stream->printf("Aborted playing file\r\n"); 00262 } 00263 00264 void Player::on_main_loop(void* argument){ 00265 if( !this->booted ) { 00266 this->booted = true; 00267 if( this->on_boot_gcode_enable ){ 00268 this->play_command(this->on_boot_gcode, THEKERNEL->serial); 00269 }else{ 00270 //THEKERNEL->serial->printf("On boot gcode disabled! skipping...\n"); 00271 } 00272 } 00273 00274 if( this->playing_file ){ 00275 string buffer; 00276 bool discard= false; 00277 int c; 00278 buffer.reserve(20); 00279 // Print each line of the file 00280 while ((c = fgetc(this->current_file_handler)) != EOF){ 00281 if (c == '\n'){ 00282 if(discard) { 00283 // we hit a long line and discarded it 00284 discard= false; 00285 buffer.clear(); 00286 this->current_stream->printf("Warning: Discarded long line\n"); 00287 return; 00288 } 00289 this->current_stream->printf("%s\n", buffer.c_str()); 00290 struct SerialMessage message; 00291 message.message = buffer; 00292 message.stream = &(StreamOutput::NullStream); // we don't really need to see the ok 00293 // wait for the queue to have enough room that a serial message could still be received before sending 00294 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message); 00295 played_cnt += buffer.size(); 00296 buffer.clear(); 00297 return; 00298 00299 }else if(buffer.size() > 128) { 00300 // discard rest of line 00301 discard= true; 00302 00303 }else{ 00304 buffer += c; 00305 } 00306 } 00307 00308 this->playing_file = false; 00309 this->filename= ""; 00310 played_cnt= 0; 00311 file_size= 0; 00312 fclose(this->current_file_handler); 00313 this->current_stream= NULL; 00314 00315 if(this->reply_stream != NULL) { 00316 // if we were printing from an M command from pronterface we need to send this back 00317 this->reply_stream->printf("Done printing file\r\n"); 00318 this->reply_stream= NULL; 00319 } 00320 } 00321 } 00322 00323 void Player::on_get_public_data(void* argument) { 00324 PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument); 00325 00326 if(!pdr->starts_with(player_checksum)) return; 00327 00328 if(pdr->second_element_is(is_playing_checksum)) { 00329 static bool bool_data; 00330 bool_data= this->playing_file; 00331 pdr->set_data_ptr(&bool_data); 00332 pdr->set_taken(); 00333 00334 }else if(pdr->second_element_is(get_progress_checksum)) { 00335 static struct pad_progress p; 00336 if(file_size > 0 && playing_file) { 00337 p.elapsed_secs= this->elapsed_secs; 00338 p.percent_complete= (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size; 00339 p.filename= this->filename; 00340 pdr->set_data_ptr(&p); 00341 pdr->set_taken(); 00342 } 00343 } 00344 } 00345 00346 void Player::on_set_public_data(void* argument) { 00347 PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument); 00348 00349 if(!pdr->starts_with(player_checksum)) return; 00350 00351 if(pdr->second_element_is(abort_play_checksum)) { 00352 abort_command("", &(StreamOutput::NullStream)); 00353 pdr->set_taken(); 00354 } 00355 }
Generated on Tue Jul 12 2022 20:09:02 by
1.7.2
