Fix bug in Line number handling
[clinton/Smoothieware.git] / src / modules / communication / utils / Gcode.cpp
1 /*
2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 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.
4 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.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
6 */
7
8
9 #include "Gcode.h"
10 #include "libs/StreamOutput.h"
11 #include "utils.h"
12 #include <stdlib.h>
13 #include <algorithm>
14
15 // This is a gcode object. It reprensents a GCode string/command, an caches some important values about that command for the sake of performance.
16 // It gets passed around in events, and attached to the queue ( that'll change )
17 Gcode::Gcode(const string &command, StreamOutput *stream, bool strip)
18 {
19 this->command= strdup(command.c_str());
20 this->m= 0;
21 this->g= 0;
22 this->add_nl= false;
23 this->stream= stream;
24 this->millimeters_of_travel = 0.0F;
25 this->accepted_by_module = false;
26 prepare_cached_values(strip);
27 }
28
29 Gcode::~Gcode()
30 {
31 if(command != nullptr) {
32 // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store
33 free(command);
34 }
35 }
36
37 Gcode::Gcode(const Gcode &to_copy)
38 {
39 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
40 this->millimeters_of_travel = to_copy.millimeters_of_travel;
41 this->has_m = to_copy.has_m;
42 this->has_g = to_copy.has_g;
43 this->m = to_copy.m;
44 this->g = to_copy.g;
45 this->add_nl = to_copy.add_nl;
46 this->stream = to_copy.stream;
47 this->accepted_by_module = false;
48 this->txt_after_ok.assign( to_copy.txt_after_ok );
49 }
50
51 Gcode &Gcode::operator= (const Gcode &to_copy)
52 {
53 if( this != &to_copy ) {
54 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
55 this->millimeters_of_travel = to_copy.millimeters_of_travel;
56 this->has_m = to_copy.has_m;
57 this->has_g = to_copy.has_g;
58 this->m = to_copy.m;
59 this->g = to_copy.g;
60 this->add_nl = to_copy.add_nl;
61 this->stream = to_copy.stream;
62 this->txt_after_ok.assign( to_copy.txt_after_ok );
63 }
64 this->accepted_by_module = false;
65 return *this;
66 }
67
68
69 // Whether or not a Gcode has a letter
70 bool Gcode::has_letter( char letter ) const
71 {
72 for (size_t i = 0; i < strlen(this->command); ++i) {
73 if( command[i] == letter ) {
74 return true;
75 }
76 }
77 return false;
78 }
79
80 // Retrieve the value for a given letter
81 float Gcode::get_value( char letter, char **ptr ) const
82 {
83 const char *cs = command;
84 char *cn = NULL;
85 for (; *cs; cs++) {
86 if( letter == *cs ) {
87 cs++;
88 float r = strtof(cs, &cn);
89 if(ptr != nullptr) *ptr= cn;
90 if (cn > cs)
91 return r;
92 }
93 }
94 if(ptr != nullptr) *ptr= nullptr;
95 return 0;
96 }
97
98 int Gcode::get_int( char letter, char **ptr ) const
99 {
100 const char *cs = command;
101 char *cn = NULL;
102 for (; *cs; cs++) {
103 if( letter == *cs ) {
104 cs++;
105 int r = strtol(cs, &cn, 10);
106 if(ptr != nullptr) *ptr= cn;
107 if (cn > cs)
108 return r;
109 }
110 }
111 if(ptr != nullptr) *ptr= nullptr;
112 return 0;
113 }
114
115 int Gcode::get_num_args() const
116 {
117 int count = 0;
118 for(size_t i = 1; i < strlen(command); i++) {
119 if( this->command[i] >= 'A' && this->command[i] <= 'Z' ) {
120 count++;
121 }
122 }
123 return count;
124 }
125
126 // Cache some of this command's properties, so we don't have to parse the string every time we want to look at them
127 void Gcode::prepare_cached_values(bool strip)
128 {
129 char *p= nullptr;
130 if( this->has_letter('G') ) {
131 this->has_g = true;
132 this->g = this->get_int('G', &p);
133 } else {
134 this->has_g = false;
135 }
136 if( this->has_letter('M') ) {
137 this->has_m = true;
138 this->m = this->get_int('M', &p);
139 } else {
140 this->has_m = false;
141 }
142
143 if(!strip) return;
144
145 // remove the Gxxx or Mxxx from string
146 if (p != nullptr) {
147 char *n= strdup(p); // create new string starting at end of the numeric value
148 free(command);
149 command= n;
150 }
151 }
152
153 void Gcode::mark_as_taken()
154 {
155 this->accepted_by_module = true;
156 }
157
158 // strip off X Y Z I J parameters if G0/1/2/3
159 void Gcode::strip_parameters()
160 {
161 if(has_g && g <= 4){
162 // strip the command of the XYZIJK parameters
163 string newcmd;
164 char *cn= command;
165 // find the start of each parameter
166 char *pch= strpbrk(cn, "XYZIJK");
167 while (pch != nullptr) {
168 if(pch > cn) {
169 // copy non parameters to new string
170 newcmd.append(cn, pch-cn);
171 }
172 // find the end of the parameter and its value
173 char *eos;
174 strtof(pch+1, &eos);
175 cn= eos; // point to end of last parameter
176 pch= strpbrk(cn, "XYZIJ"); // find next parameter
177 }
178 // append anything left on the line
179 newcmd.append(cn);
180 // strip whitespace to save even more
181 newcmd.erase(std::remove_if(newcmd.begin(), newcmd.end(), ::isspace), newcmd.end());
182
183 // release the old one
184 free(command);
185 // copy the new shortened one
186 command= strdup(newcmd.c_str());
187 }
188 }