Fork of Smoothie to port to mbed non-LPC targets.
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