Michael Spencer / Smoothie

Dependencies:   mbed

Fork of Smoothie by Stéphane Cachat

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GcodeDispatch.cpp Source File

GcodeDispatch.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 #include <string>
00009 using std::string;
00010 #include "libs/Module.h"
00011 #include "libs/Kernel.h"
00012 #include "utils/Gcode.h"
00013 #include "libs/nuts_bolts.h"
00014 #include "GcodeDispatch.h"
00015 #include "modules/robot/Conveyor.h"
00016 #include "libs/SerialMessage.h"
00017 #include "libs/StreamOutput.h"
00018 #include "libs/FileStream.h"
00019 
00020 GcodeDispatch::GcodeDispatch() {}
00021 
00022 // Called when the module has just been loaded
00023 void GcodeDispatch::on_module_loaded()
00024 {
00025     return_error_on_unhandled_gcode = THEKERNEL->config->value( return_error_on_unhandled_gcode_checksum )->by_default(false)->as_bool();
00026     this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
00027     currentline = -1;
00028     uploading = false;
00029     last_g= 255;
00030 }
00031 
00032 // When a command is received, if it is a Gcode, dispatch it as an object via an event
00033 void GcodeDispatch::on_console_line_received(void *line)
00034 {
00035     SerialMessage new_message = *static_cast<SerialMessage *>(line);
00036     string possible_command = new_message.message;
00037 
00038     int ln = 0;
00039     int cs = 0;
00040 
00041 try_again:
00042 
00043     char first_char = possible_command[0];
00044     unsigned int n;
00045     if ( first_char == 'G' || first_char == 'M' || first_char == 'T' || first_char == 'N' ) {
00046 
00047         //Get linenumber
00048         if ( first_char == 'N' ) {
00049             Gcode full_line = Gcode(possible_command, new_message.stream);
00050             ln = (int) full_line.get_value('N');
00051             int chksum = (int) full_line.get_value('*');
00052 
00053             //Catch message if it is M110: Set Current Line Number
00054             if ( full_line.has_letter('M') ) {
00055                 if ( ((int) full_line.get_value('M')) == 110 ) {
00056                     currentline = ln;
00057                     new_message.stream->printf("ok\r\n");
00058                     return;
00059                 }
00060             }
00061 
00062             //Strip checksum value from possible_command
00063             size_t chkpos = possible_command.find_first_of("*");
00064             possible_command = possible_command.substr(0, chkpos);
00065             //Calculate checksum
00066             if ( chkpos != string::npos ) {
00067                 for (auto c = possible_command.cbegin(); *c != '*' && c != possible_command.cend(); c++)
00068                     cs = cs ^ *c;
00069                 cs &= 0xff;  // Defensive programming...
00070                 cs -= chksum;
00071             }
00072             //Strip line number value from possible_command
00073             size_t lnsize = possible_command.find_first_not_of("N0123456789.,- ");
00074             possible_command = possible_command.substr(lnsize);
00075 
00076         } else {
00077             //Assume checks succeeded
00078             cs = 0x00;
00079             ln = currentline + 1;
00080         }
00081 
00082         //Remove comments
00083         size_t comment = possible_command.find_first_of(";(");
00084         if( comment != string::npos ) {
00085             possible_command = possible_command.substr(0, comment);
00086         }
00087 
00088         //If checksum passes then process message, else request resend
00089         int nextline = currentline + 1;
00090         if( cs == 0x00 && ln == nextline ) {
00091             if( first_char == 'N' ) {
00092                 currentline = nextline;
00093             }
00094 
00095             while(possible_command.size() > 0) {
00096                 size_t nextcmd = possible_command.find_first_of("GMT", possible_command.find_first_of("GMT") + 1);
00097                 string single_command;
00098                 if(nextcmd == string::npos) {
00099                     single_command = possible_command;
00100                     possible_command = "";
00101                 } else {
00102                     single_command = possible_command.substr(0, nextcmd);
00103                     possible_command = possible_command.substr(nextcmd);
00104                 }
00105 
00106                 if(!uploading) {
00107                     //Prepare gcode for dispatch
00108                     Gcode *gcode = new Gcode(single_command, new_message.stream);
00109 
00110                     if(gcode->has_g) {
00111                         last_g= gcode->g;
00112                     }
00113                     if(gcode->has_m) {
00114                         switch (gcode->m) {
00115                             case 28: // start upload command
00116                                 delete gcode;
00117 
00118                                 this->upload_filename = "/sd/" + single_command.substr(4); // rest of line is filename
00119                                 // open file
00120                                 upload_fd = fopen(this->upload_filename.c_str(), "w");
00121                                 if(upload_fd != NULL) {
00122                                     this->uploading = true;
00123                                     new_message.stream->printf("Writing to file: %s\r\n", this->upload_filename.c_str());
00124                                 } else {
00125                                     new_message.stream->printf("open failed, File: %s.\r\n", this->upload_filename.c_str());
00126                                 }
00127                                 //printf("Start Uploading file: %s, %p\n", upload_filename.c_str(), upload_fd);
00128                                 continue;
00129 
00130                             case 500: // M500 save volatile settings to config-override
00131                                 // replace stream with one that writes to config-override file
00132                                 gcode->stream = new FileStream(THEKERNEL->config_override_filename());
00133                                 // dispatch the M500 here so we can free up the stream when done
00134                                 THEKERNEL->call_event(ON_GCODE_RECEIVED, gcode );
00135                                 delete gcode->stream;
00136                                 delete gcode;
00137                                 new_message.stream->printf("Settings Stored to %s\r\nok\r\n", THEKERNEL->config_override_filename());
00138                                 continue;
00139 
00140                             case 502: // M502 deletes config-override so everything defaults to what is in config
00141                                 remove(THEKERNEL->config_override_filename());
00142                                 new_message.stream->printf("config override file deleted %s, reboot needed\r\nok\r\n", THEKERNEL->config_override_filename());
00143                                 delete gcode;
00144                                 continue;
00145 
00146                             case 503: { // M503 display live settings and indicates if there is an override file
00147                                 FILE *fd = fopen(THEKERNEL->config_override_filename(), "r");
00148                                 if(fd != NULL) {
00149                                     fclose(fd);
00150                                     new_message.stream->printf("; config override present: %s\n",  THEKERNEL->config_override_filename());
00151 
00152                                 } else {
00153                                     new_message.stream->printf("; No config override\n");
00154                                 }
00155                                 break; // fall through to process by modules
00156                             }
00157                         }
00158                     }
00159 
00160                     //printf("dispatch %p: '%s' G%d M%d...", gcode, gcode->command.c_str(), gcode->g, gcode->m);
00161                     //Dispatch message!
00162                     THEKERNEL->call_event(ON_GCODE_RECEIVED, gcode );
00163                     if(gcode->add_nl)
00164                         new_message.stream->printf("\r\n");
00165 
00166                     if( return_error_on_unhandled_gcode == true && gcode->accepted_by_module == false)
00167                         new_message.stream->printf("ok (command unclaimed)\r\n");
00168                     else if(!gcode->txt_after_ok.empty()) {
00169                         new_message.stream->printf("ok %s\r\n", gcode->txt_after_ok.c_str());
00170                         gcode->txt_after_ok.clear();
00171                     } else
00172                         new_message.stream->printf("ok\r\n");
00173 
00174                     delete gcode;
00175 
00176                 } else {
00177                     // we are uploading a file so save it
00178                     if(single_command.substr(0, 3) == "M29") {
00179                         // done uploading, close file
00180                         fclose(upload_fd);
00181                         upload_fd = NULL;
00182                         uploading = false;
00183                         upload_filename.clear();
00184                         new_message.stream->printf("Done saving file.\r\n");
00185                         continue;
00186                     }
00187 
00188                     if(upload_fd == NULL) {
00189                         // error detected writing to file so discard everything until it stops
00190                         new_message.stream->printf("ok\r\n");
00191                         continue;
00192                     }
00193 
00194                     single_command.append("\n");
00195                     static int cnt = 0;
00196                     if(fwrite(single_command.c_str(), 1, single_command.size(), upload_fd) != single_command.size()) {
00197                         // error writing to file
00198                         new_message.stream->printf("Error:error writing to file.\r\n");
00199                         fclose(upload_fd);
00200                         upload_fd = NULL;
00201                         continue;
00202 
00203                     } else {
00204                         cnt += single_command.size();
00205                         if (cnt > 400) {
00206                             // HACK ALERT to get around fwrite corruption close and re open for append
00207                             fclose(upload_fd);
00208                             upload_fd = fopen(upload_filename.c_str(), "a");
00209                             cnt = 0;
00210                         }
00211                         new_message.stream->printf("ok\r\n");
00212                         //printf("uploading file write ok\n");
00213                     }
00214                 }
00215             }
00216 
00217         } else {
00218             //Request resend
00219             new_message.stream->printf("rs N%d\r\n", nextline);
00220         }
00221 
00222     } else if( (n=possible_command.find_first_of("XYZF")) == 0 || (first_char == ' ' && n != string::npos) ) {
00223         // handle pycam syntax, use last G0 or G1 and resubmit if an X Y Z or F is found on its own line
00224         if(last_g != 0 && last_g != 1) {
00225             //if no last G1 or G0 ignore
00226             //THEKERNEL->streams->printf("ignored: %s\r\n", possible_command.c_str());
00227             return;
00228         }
00229         char buf[6];
00230         snprintf(buf, sizeof(buf), "G%d ", last_g);
00231         possible_command.insert(0, buf);
00232         goto try_again;
00233 
00234         // Ignore comments and blank lines
00235     } else if ( first_char == ';' || first_char == '(' || first_char == ' ' || first_char == '\n' || first_char == '\r' ) {
00236         new_message.stream->printf("ok\r\n");
00237     }
00238 }
00239