Merge remote-tracking branch 'upstream/edge' into upstream-master
[clinton/Smoothieware.git] / src / modules / communication / utils / Gcode.cpp
CommitLineData
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 17Gcode::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
30Gcode::~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
38Gcode::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
52Gcode &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
71bool 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 82float 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 99int 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
116uint32_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
133int 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
145std::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
158std::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 172void 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
212void 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}