Fix bug in Line number handling
[clinton/Smoothieware.git] / src / modules / communication / utils / Gcode.cpp
index 679c693..48e337b 100644 (file)
@@ -6,22 +6,37 @@
 */
 
 
-#include <string>
-using std::string;
 #include "Gcode.h"
 #include "libs/StreamOutput.h"
 #include "utils.h"
-
 #include <stdlib.h>
+#include <algorithm>
 
+// This is a gcode object. It reprensents a GCode string/command, an caches some important values about that command for the sake of performance.
+// It gets passed around in events, and attached to the queue ( that'll change )
+Gcode::Gcode(const string &command, StreamOutput *stream, bool strip)
+{
+    this->command= strdup(command.c_str());
+    this->m= 0;
+    this->g= 0;
+    this->add_nl= false;
+    this->stream= stream;
+    this->millimeters_of_travel = 0.0F;
+    this->accepted_by_module = false;
+    prepare_cached_values(strip);
+}
 
-Gcode::Gcode(const string& command, StreamOutput* stream) : command(command), m(0), g(0), add_nl(false), stream(stream) {
-    prepare_cached_values();
-    this->millimeters_of_travel = 0L;
+Gcode::~Gcode()
+{
+    if(command != nullptr) {
+        // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store
+        free(command);
+    }
 }
 
-Gcode::Gcode(const Gcode& to_copy){
-    this->command.assign( to_copy.command );
+Gcode::Gcode(const Gcode &to_copy)
+{
+    this->command               = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store
     this->millimeters_of_travel = to_copy.millimeters_of_travel;
     this->has_m                 = to_copy.has_m;
     this->has_g                 = to_copy.has_g;
@@ -29,11 +44,14 @@ Gcode::Gcode(const Gcode& to_copy){
     this->g                     = to_copy.g;
     this->add_nl                = to_copy.add_nl;
     this->stream                = to_copy.stream;
+    this->accepted_by_module    = false;
+    this->txt_after_ok.assign( to_copy.txt_after_ok );
 }
 
-Gcode& Gcode::operator= (const Gcode& to_copy){
-    if( this != &to_copy ){
-        this->command.assign( to_copy.command );
+Gcode &Gcode::operator= (const Gcode &to_copy)
+{
+    if( this != &to_copy ) {
+        this->command               = strdup(to_copy.command); // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store
         this->millimeters_of_travel = to_copy.millimeters_of_travel;
         this->has_m                 = to_copy.has_m;
         this->has_g                 = to_copy.has_g;
@@ -41,16 +59,18 @@ Gcode& Gcode::operator= (const Gcode& to_copy){
         this->g                     = to_copy.g;
         this->add_nl                = to_copy.add_nl;
         this->stream                = to_copy.stream;
+        this->txt_after_ok.assign( to_copy.txt_after_ok );
     }
+    this->accepted_by_module = false;
     return *this;
 }
 
 
 // Whether or not a Gcode has a letter
-bool Gcode::has_letter( char letter ){
-    //return ( this->command->find( letter ) != string::npos );
-    for (std::string::const_iterator c = this->command.cbegin(); c != this->command.cend(); c++) {
-        if( *c == letter ){
+bool Gcode::has_letter( char letter ) const
+{
+    for (size_t i = 0; i < strlen(this->command); ++i) {
+        if( command[i] == letter ) {
             return true;
         }
     }
@@ -58,59 +78,111 @@ bool Gcode::has_letter( char letter ){
 }
 
 // Retrieve the value for a given letter
-// We don't use the high-level methods of std::string because they call malloc and it's very bad to do that inside of interrupts
-double Gcode::get_value( char letter ){
-    //__disable_irq();
-    const char* cs = command.c_str();
-    char* cn = NULL;
-    for (; *cs; cs++){
-         if( letter == *cs ){
-             cs++;
-             double r = strtod(cs, &cn);
-             if (cn > cs)
-                 return r;
-         }
+float Gcode::get_value( char letter, char **ptr ) const
+{
+    const char *cs = command;
+    char *cn = NULL;
+    for (; *cs; cs++) {
+        if( letter == *cs ) {
+            cs++;
+            float r = strtof(cs, &cn);
+            if(ptr != nullptr) *ptr= cn;
+            if (cn > cs)
+                return r;
+        }
     }
-    //__enable_irq();
+    if(ptr != nullptr) *ptr= nullptr;
     return 0;
 }
 
-int Gcode::get_int( char letter )
+int Gcode::get_int( char letter, char **ptr ) const
 {
-    const char* cs = command.c_str();
-    charcn = NULL;
-    for (; *cs; cs++){
-        if( letter == *cs ){
+    const char *cs = command;
+    char *cn = NULL;
+    for (; *cs; cs++) {
+        if( letter == *cs ) {
             cs++;
             int r = strtol(cs, &cn, 10);
+            if(ptr != nullptr) *ptr= cn;
             if (cn > cs)
                 return r;
         }
     }
+    if(ptr != nullptr) *ptr= nullptr;
     return 0;
 }
 
-int Gcode::get_num_args(){
+int Gcode::get_num_args() const
+{
     int count = 0;
-    for(size_t i=1; i<this->command.length(); i++){
-        if( this->command.at(i) >= 'A' && this->command.at(i) <= 'Z' ){
+    for(size_t i = 1; i < strlen(command); i++) {
+        if( this->command[i] >= 'A' && this->command[i] <= 'Z' ) {
             count++;
         }
     }
     return count;
 }
 
-void Gcode::prepare_cached_values(){
-    if( this->has_letter('G') ){
+// Cache some of this command's properties, so we don't have to parse the string every time we want to look at them
+void Gcode::prepare_cached_values(bool strip)
+{
+    char *p= nullptr;
+    if( this->has_letter('G') ) {
         this->has_g = true;
-        this->g = this->get_int('G');
-    }else{
+        this->g = this->get_int('G', &p);
+    } else {
         this->has_g = false;
     }
-    if( this->has_letter('M') ){
+    if( this->has_letter('M') ) {
         this->has_m = true;
-        this->m = this->get_int('M');
-    }else{
+        this->m = this->get_int('M', &p);
+    } else {
         this->has_m = false;
     }
+
+    if(!strip) return;
+
+    // remove the Gxxx or Mxxx from string
+    if (p != nullptr) {
+        char *n= strdup(p); // create new string starting at end of the numeric value
+        free(command);
+        command= n;
+    }
+}
+
+void Gcode::mark_as_taken()
+{
+    this->accepted_by_module = true;
+}
+
+// strip off X Y Z I J parameters if G0/1/2/3
+void Gcode::strip_parameters()
+{
+    if(has_g && g <= 4){
+        // strip the command of the XYZIJK parameters
+        string newcmd;
+        char *cn= command;
+        // find the start of each parameter
+        char *pch= strpbrk(cn, "XYZIJK");
+        while (pch != nullptr) {
+            if(pch > cn) {
+                // copy non parameters to new string
+                newcmd.append(cn, pch-cn);
+            }
+            // find the end of the parameter and its value
+            char *eos;
+            strtof(pch+1, &eos);
+            cn= eos; // point to end of last parameter
+            pch= strpbrk(cn, "XYZIJ"); // find next parameter
+        }
+        // append anything left on the line
+        newcmd.append(cn);
+        // strip whitespace to save even more
+        newcmd.erase(std::remove_if(newcmd.begin(), newcmd.end(), ::isspace), newcmd.end());
+
+        // release the old one
+        free(command);
+        // copy the new shortened one
+        command= strdup(newcmd.c_str());
+    }
 }