update firmware.bin
[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 uint32_t Gcode::get_uint( char letter, char **ptr ) const
116 {
117 const char *cs = command;
118 char *cn = NULL;
119 for (; *cs; cs++) {
120 if( letter == *cs ) {
121 cs++;
122 int r = strtoul(cs, &cn, 10);
123 if(ptr != nullptr) *ptr= cn;
124 if (cn > cs)
125 return r;
126 }
127 }
128 if(ptr != nullptr) *ptr= nullptr;
129 return 0;
130 }
131
132 int Gcode::get_num_args() const
133 {
134 int count = 0;
135 for(size_t i = 1; i < strlen(command); i++) {
136 if( this->command[i] >= 'A' && this->command[i] <= 'Z' ) {
137 count++;
138 }
139 }
140 return count;
141 }
142
143 // Cache some of this command's properties, so we don't have to parse the string every time we want to look at them
144 void Gcode::prepare_cached_values(bool strip)
145 {
146 char *p= nullptr;
147 if( this->has_letter('G') ) {
148 this->has_g = true;
149 this->g = this->get_int('G', &p);
150 } else {
151 this->has_g = false;
152 }
153 if( this->has_letter('M') ) {
154 this->has_m = true;
155 this->m = this->get_int('M', &p);
156 } else {
157 this->has_m = false;
158 }
159
160 if(!strip) return;
161
162 // remove the Gxxx or Mxxx from string
163 if (p != nullptr) {
164 char *n= strdup(p); // create new string starting at end of the numeric value
165 free(command);
166 command= n;
167 }
168 }
169
170 void Gcode::mark_as_taken()
171 {
172 this->accepted_by_module = true;
173 }
174
175 // strip off X Y Z I J K parameters if G0/1/2/3
176 void Gcode::strip_parameters()
177 {
178 if(has_g && g < 4){
179 // strip the command of the XYZIJK parameters
180 string newcmd;
181 char *cn= command;
182 // find the start of each parameter
183 char *pch= strpbrk(cn, "XYZIJK");
184 while (pch != nullptr) {
185 if(pch > cn) {
186 // copy non parameters to new string
187 newcmd.append(cn, pch-cn);
188 }
189 // find the end of the parameter and its value
190 char *eos;
191 strtof(pch+1, &eos);
192 cn= eos; // point to end of last parameter
193 pch= strpbrk(cn, "XYZIJK"); // find next parameter
194 }
195 // append anything left on the line
196 newcmd.append(cn);
197
198 // strip whitespace to save even more, this causes problems so don't do it
199 //newcmd.erase(std::remove_if(newcmd.begin(), newcmd.end(), ::isspace), newcmd.end());
200
201 // release the old one
202 free(command);
203 // copy the new shortened one
204 command= strdup(newcmd.c_str());
205 }
206 }