Commit | Line | Data |
---|---|---|
df27a6a3 | 1 | /* |
cd011f58 AW |
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. | |
df27a6a3 | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
cd011f58 AW |
6 | */ |
7 | ||
8 | ||
4cff3ded | 9 | #include "Gcode.h" |
838b33b4 | 10 | #include "libs/StreamOutput.h" |
adc927a8 | 11 | #include "utils.h" |
838b33b4 | 12 | #include <stdlib.h> |
b9b1bb25 | 13 | #include <algorithm> |
4cff3ded | 14 | |
edac9072 AW |
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 ) | |
aae5a789 | 17 | Gcode::Gcode(const string &command, StreamOutput *stream, bool strip) |
8090190d JM |
18 | { |
19 | this->command= strdup(command.c_str()); | |
20 | this->m= 0; | |
21 | this->g= 0; | |
6f648cda | 22 | this->subcode= 0; |
8090190d JM |
23 | this->add_nl= false; |
24 | this->stream= stream; | |
c2885de8 | 25 | this->millimeters_of_travel = 0.0F; |
aae5a789 | 26 | prepare_cached_values(strip); |
7d678d16 | 27 | this->stripped= strip; |
8090190d JM |
28 | } |
29 | ||
30 | Gcode::~Gcode() | |
31 | { | |
32 | if(command != nullptr) { | |
b9b1bb25 JM |
33 | // TODO we can reference count this so we share copies, may save more ram than the extra count we need to store |
34 | free(command); | |
8090190d | 35 | } |
702023f3 | 36 | } |
4cff3ded | 37 | |
8090190d JM |
38 | Gcode::Gcode(const Gcode &to_copy) |
39 | { | |
b9b1bb25 | 40 | 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 |
e55e3062 AW |
41 | this->millimeters_of_travel = to_copy.millimeters_of_travel; |
42 | this->has_m = to_copy.has_m; | |
43 | this->has_g = to_copy.has_g; | |
44 | this->m = to_copy.m; | |
45 | this->g = to_copy.g; | |
6f648cda | 46 | this->subcode = to_copy.subcode; |
e55e3062 AW |
47 | this->add_nl = to_copy.add_nl; |
48 | this->stream = to_copy.stream; | |
93284f6f | 49 | this->txt_after_ok.assign( to_copy.txt_after_ok ); |
e55e3062 AW |
50 | } |
51 | ||
8090190d JM |
52 | Gcode &Gcode::operator= (const Gcode &to_copy) |
53 | { | |
54 | if( this != &to_copy ) { | |
b9b1bb25 | 55 | 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 |
e55e3062 AW |
56 | this->millimeters_of_travel = to_copy.millimeters_of_travel; |
57 | this->has_m = to_copy.has_m; | |
58 | this->has_g = to_copy.has_g; | |
59 | this->m = to_copy.m; | |
60 | this->g = to_copy.g; | |
6f648cda | 61 | this->subcode = to_copy.subcode; |
e55e3062 AW |
62 | this->add_nl = to_copy.add_nl; |
63 | this->stream = to_copy.stream; | |
93284f6f | 64 | this->txt_after_ok.assign( to_copy.txt_after_ok ); |
e55e3062 AW |
65 | } |
66 | return *this; | |
67 | } | |
68 | ||
69 | ||
2bb8b390 | 70 | // Whether or not a Gcode has a letter |
8090190d JM |
71 | bool Gcode::has_letter( char letter ) const |
72 | { | |
73 | for (size_t i = 0; i < strlen(this->command); ++i) { | |
74 | if( command[i] == letter ) { | |
13e4a3f9 AW |
75 | return true; |
76 | } | |
77 | } | |
78 | return false; | |
4cff3ded AW |
79 | } |
80 | ||
13e4a3f9 | 81 | // Retrieve the value for a given letter |
b9b1bb25 | 82 | float Gcode::get_value( char letter, char **ptr ) const |
8090190d | 83 | { |
8090190d JM |
84 | const char *cs = command; |
85 | char *cn = NULL; | |
86 | for (; *cs; cs++) { | |
87 | if( letter == *cs ) { | |
88 | cs++; | |
89 | float r = strtof(cs, &cn); | |
b9b1bb25 | 90 | if(ptr != nullptr) *ptr= cn; |
8090190d JM |
91 | if (cn > cs) |
92 | return r; | |
93 | } | |
13e4a3f9 | 94 | } |
b9b1bb25 | 95 | if(ptr != nullptr) *ptr= nullptr; |
df27a6a3 | 96 | return 0; |
4cff3ded | 97 | } |
e6b5ae25 | 98 | |
b9b1bb25 | 99 | int Gcode::get_int( char letter, char **ptr ) const |
adc927a8 | 100 | { |
8090190d JM |
101 | const char *cs = command; |
102 | char *cn = NULL; | |
103 | for (; *cs; cs++) { | |
104 | if( letter == *cs ) { | |
105d8d3e MM |
105 | cs++; |
106 | int r = strtol(cs, &cn, 10); | |
b9b1bb25 | 107 | if(ptr != nullptr) *ptr= cn; |
105d8d3e MM |
108 | if (cn > cs) |
109 | return r; | |
adc927a8 MM |
110 | } |
111 | } | |
b9b1bb25 | 112 | if(ptr != nullptr) *ptr= nullptr; |
adc927a8 MM |
113 | return 0; |
114 | } | |
115 | ||
230079d4 JM |
116 | uint32_t Gcode::get_uint( char letter, char **ptr ) const |
117 | { | |
118 | const char *cs = command; | |
119 | char *cn = NULL; | |
120 | for (; *cs; cs++) { | |
121 | if( letter == *cs ) { | |
122 | cs++; | |
123 | int r = strtoul(cs, &cn, 10); | |
124 | if(ptr != nullptr) *ptr= cn; | |
125 | if (cn > cs) | |
126 | return r; | |
127 | } | |
128 | } | |
129 | if(ptr != nullptr) *ptr= nullptr; | |
130 | return 0; | |
131 | } | |
132 | ||
8090190d JM |
133 | int Gcode::get_num_args() const |
134 | { | |
d8f8bbf3 | 135 | int count = 0; |
7d678d16 | 136 | for(size_t i = stripped?0:1; i < strlen(command); i++) { |
8090190d | 137 | if( this->command[i] >= 'A' && this->command[i] <= 'Z' ) { |
7d678d16 | 138 | if(this->command[i] == 'T') continue; |
d8f8bbf3 L |
139 | count++; |
140 | } | |
141 | } | |
142 | return count; | |
143 | } | |
144 | ||
7d678d16 JM |
145 | std::map<char,float> Gcode::get_args() const |
146 | { | |
147 | std::map<char,float> m; | |
148 | for(size_t i = stripped?0:1; i < strlen(command); i++) { | |
149 | char c= this->command[i]; | |
150 | if( c >= 'A' && c <= 'Z' ) { | |
151 | if(c == 'T') continue; | |
152 | m[c]= get_value(c); | |
153 | } | |
154 | } | |
155 | return m; | |
df16e9b0 JM |
156 | } |
157 | ||
158 | std::map<char,int> Gcode::get_args_int() const | |
159 | { | |
160 | std::map<char,int> m; | |
161 | for(size_t i = stripped?0:1; i < strlen(command); i++) { | |
162 | char c= this->command[i]; | |
163 | if( c >= 'A' && c <= 'Z' ) { | |
164 | if(c == 'T') continue; | |
165 | m[c]= get_int(c); | |
166 | } | |
167 | } | |
168 | return m; | |
7d678d16 JM |
169 | } |
170 | ||
edac9072 | 171 | // Cache some of this command's properties, so we don't have to parse the string every time we want to look at them |
aae5a789 | 172 | void Gcode::prepare_cached_values(bool strip) |
8090190d | 173 | { |
b9b1bb25 | 174 | char *p= nullptr; |
8090190d | 175 | if( this->has_letter('G') ) { |
e6b5ae25 | 176 | this->has_g = true; |
b9b1bb25 | 177 | this->g = this->get_int('G', &p); |
6f648cda | 178 | |
8090190d | 179 | } else { |
e6b5ae25 AW |
180 | this->has_g = false; |
181 | } | |
6f648cda | 182 | |
8090190d | 183 | if( this->has_letter('M') ) { |
e6b5ae25 | 184 | this->has_m = true; |
b9b1bb25 | 185 | this->m = this->get_int('M', &p); |
6f648cda | 186 | |
8090190d | 187 | } else { |
e6b5ae25 AW |
188 | this->has_m = false; |
189 | } | |
b9b1bb25 | 190 | |
6f648cda JM |
191 | if(has_g || has_m) { |
192 | // look for subcode and extract it | |
193 | if(p != nullptr && *p == '.') { | |
194 | this->subcode = strtoul(p+1, &p, 10); | |
195 | ||
196 | }else{ | |
197 | this->subcode= 0; | |
198 | } | |
199 | } | |
200 | ||
aae5a789 JM |
201 | if(!strip) return; |
202 | ||
b9b1bb25 JM |
203 | // remove the Gxxx or Mxxx from string |
204 | if (p != nullptr) { | |
205 | char *n= strdup(p); // create new string starting at end of the numeric value | |
206 | free(command); | |
207 | command= n; | |
208 | } | |
e6b5ae25 | 209 | } |
74b6303c | 210 | |
fb7956a9 | 211 | // strip off X Y Z I J K parameters if G0/1/2/3 |
b9b1bb25 JM |
212 | void Gcode::strip_parameters() |
213 | { | |
fb7956a9 | 214 | if(has_g && g < 4){ |
b9b1bb25 JM |
215 | // strip the command of the XYZIJK parameters |
216 | string newcmd; | |
217 | char *cn= command; | |
218 | // find the start of each parameter | |
219 | char *pch= strpbrk(cn, "XYZIJK"); | |
220 | while (pch != nullptr) { | |
221 | if(pch > cn) { | |
222 | // copy non parameters to new string | |
223 | newcmd.append(cn, pch-cn); | |
224 | } | |
225 | // find the end of the parameter and its value | |
226 | char *eos; | |
227 | strtof(pch+1, &eos); | |
228 | cn= eos; // point to end of last parameter | |
fb7956a9 | 229 | pch= strpbrk(cn, "XYZIJK"); // find next parameter |
b9b1bb25 JM |
230 | } |
231 | // append anything left on the line | |
232 | newcmd.append(cn); | |
fb7956a9 JM |
233 | |
234 | // strip whitespace to save even more, this causes problems so don't do it | |
235 | //newcmd.erase(std::remove_if(newcmd.begin(), newcmd.end(), ::isspace), newcmd.end()); | |
b9b1bb25 JM |
236 | |
237 | // release the old one | |
238 | free(command); | |
239 | // copy the new shortened one | |
240 | command= strdup(newcmd.c_str()); | |
241 | } | |
242 | } |