Subversion Repositories Tronxy-X3A-Marlin

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 ron 1
/**
2
 * Marlin 3D Printer Firmware
3
 * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
 *
5
 * Based on Sprinter and grbl.
6
 * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22
 
23
/**
24
 * parser.cpp - Parser for a GCode line, providing a parameter interface.
25
 */
26
 
27
#include "parser.h"
28
 
29
#include "Marlin.h"
30
#include "language.h"
31
 
32
// Must be declared for allocation and to satisfy the linker
33
// Zero values need no initialization.
34
 
35
bool GCodeParser::volumetric_enabled;
36
 
37
#if ENABLED(INCH_MODE_SUPPORT)
38
  float GCodeParser::linear_unit_factor, GCodeParser::volumetric_unit_factor;
39
#endif
40
 
41
#if ENABLED(TEMPERATURE_UNITS_SUPPORT)
42
  TempUnit GCodeParser::input_temp_units;
43
#endif
44
 
45
char *GCodeParser::command_ptr,
46
     *GCodeParser::string_arg,
47
     *GCodeParser::value_ptr;
48
char GCodeParser::command_letter;
49
int GCodeParser::codenum;
50
#if USE_GCODE_SUBCODES
51
  uint8_t GCodeParser::subcode;
52
#endif
53
 
54
#if ENABLED(FASTER_GCODE_PARSER)
55
  // Optimized Parameters
56
  uint32_t GCodeParser::codebits;  // found bits
57
  uint8_t GCodeParser::param[26];  // parameter offsets from command_ptr
58
#else
59
  char *GCodeParser::command_args; // start of parameters
60
#endif
61
 
62
// Create a global instance of the GCode parser singleton
63
GCodeParser parser;
64
 
65
/**
66
 * Clear all code-seen (and value pointers)
67
 *
68
 * Since each param is set/cleared on seen codes,
69
 * this may be optimized by commenting out ZERO(param)
70
 */
71
void GCodeParser::reset() {
72
  string_arg = NULL;                    // No whole line argument
73
  command_letter = '?';                 // No command letter
74
  codenum = 0;                          // No command code
75
  #if USE_GCODE_SUBCODES
76
    subcode = 0;                        // No command sub-code
77
  #endif
78
  #if ENABLED(FASTER_GCODE_PARSER)
79
    codebits = 0;                       // No codes yet
80
    //ZERO(param);                      // No parameters (should be safe to comment out this line)
81
  #endif
82
}
83
 
84
// Populate all fields by parsing a single line of GCode
85
// 58 bytes of SRAM are used to speed up seen/value
86
void GCodeParser::parse(char *p) {
87
 
88
  reset(); // No codes to report
89
 
90
  // Skip spaces
91
  while (*p == ' ') ++p;
92
 
93
  // Skip N[-0-9] if included in the command line
94
  if (*p == 'N' && NUMERIC_SIGNED(p[1])) {
95
    #if ENABLED(FASTER_GCODE_PARSER)
96
      //set('N', p + 1);     // (optional) Set the 'N' parameter value
97
    #endif
98
    p += 2;                  // skip N[-0-9]
99
    while (NUMERIC(*p)) ++p; // skip [0-9]*
100
    while (*p == ' ')   ++p; // skip [ ]*
101
  }
102
 
103
  // *p now points to the current command, which should be G, M, or T
104
  command_ptr = p;
105
 
106
  // Get the command letter, which must be G, M, or T
107
  const char letter = *p++;
108
 
109
  // Nullify asterisk and trailing whitespace
110
  char *starpos = strchr(p, '*');
111
  if (starpos) {
112
    --starpos;                          // *
113
    while (*starpos == ' ') --starpos;  // spaces...
114
    starpos[1] = '\0';
115
  }
116
 
117
  // Bail if the letter is not G, M, or T
118
  switch (letter) { case 'G': case 'M': case 'T': break; default: return; }
119
 
120
  // Skip spaces to get the numeric part
121
  while (*p == ' ') p++;
122
 
123
  // Bail if there's no command code number
124
  if (!NUMERIC(*p)) return;
125
 
126
  // Save the command letter at this point
127
  // A '?' signifies an unknown command
128
  command_letter = letter;
129
 
130
  // Get the code number - integer digits only
131
  codenum = 0;
132
  do {
133
    codenum *= 10, codenum += *p++ - '0';
134
  } while (NUMERIC(*p));
135
 
136
  // Allow for decimal point in command
137
  #if USE_GCODE_SUBCODES
138
    if (*p == '.') {
139
      p++;
140
      while (NUMERIC(*p))
141
        subcode *= 10, subcode += *p++ - '0';
142
    }
143
  #endif
144
 
145
  // Skip all spaces to get to the first argument, or nul
146
  while (*p == ' ') p++;
147
 
148
  // The command parameters (if any) start here, for sure!
149
 
150
  #if DISABLED(FASTER_GCODE_PARSER)
151
    command_args = p; // Scan for parameters in seen()
152
  #endif
153
 
154
  // Only use string_arg for these M codes
155
  if (letter == 'M') switch (codenum) { case 23: case 28: case 30: case 117: case 118: case 928: string_arg = p; return; default: break; }
156
 
157
  #if ENABLED(DEBUG_GCODE_PARSER)
158
    const bool debug = codenum == 800;
159
  #endif
160
 
161
  /**
162
   * Find all parameters, set flags and pointers for fast parsing
163
   *
164
   * Most codes ignore 'string_arg', but those that want a string will get the right pointer.
165
   * The following loop assigns the first "parameter" having no numeric value to 'string_arg'.
166
   * This allows M0/M1 with expire time to work: "M0 S5 You Win!"
167
   * For 'M118' you must use 'E1' and 'A1' rather than just 'E' or 'A'
168
   */
169
  string_arg = NULL;
170
  while (const char code = *p++) {                    // Get the next parameter. A NUL ends the loop
171
 
172
    // Special handling for M32 [P] !/path/to/file.g#
173
    // The path must be the last parameter
174
    if (code == '!' && letter == 'M' && codenum == 32) {
175
      string_arg = p;                           // Name starts after '!'
176
      char * const lb = strchr(p, '#');         // Already seen '#' as SD char (to pause buffering)
177
      if (lb) *lb = '\0';                       // Safe to mark the end of the filename
178
      return;
179
    }
180
 
181
    // Arguments MUST be uppercase for fast GCode parsing
182
    #if ENABLED(FASTER_GCODE_PARSER)
183
      #define PARAM_TEST WITHIN(code, 'A', 'Z')
184
    #else
185
      #define PARAM_TEST true
186
    #endif
187
 
188
    if (PARAM_TEST) {
189
 
190
      while (*p == ' ') p++;                    // Skip spaces between parameters & values
191
 
192
      const bool has_num = valid_float(p);
193
 
194
      #if ENABLED(DEBUG_GCODE_PARSER)
195
        if (debug) {
196
          SERIAL_ECHOPAIR("Got letter ", code);
197
          SERIAL_ECHOPAIR(" at index ", (int)(p - command_ptr - 1));
198
          if (has_num) SERIAL_ECHOPGM(" (has_num)");
199
        }
200
      #endif
201
 
202
      if (!has_num && !string_arg) {            // No value? First time, keep as string_arg
203
        string_arg = p - 1;
204
        #if ENABLED(DEBUG_GCODE_PARSER)
205
          if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG
206
        #endif
207
      }
208
 
209
      #if ENABLED(DEBUG_GCODE_PARSER)
210
        if (debug) SERIAL_EOL();
211
      #endif
212
 
213
      #if ENABLED(FASTER_GCODE_PARSER)
214
        set(code, has_num ? p : NULL);          // Set parameter exists and pointer (NULL for no number)
215
      #endif
216
    }
217
    else if (!string_arg) {                     // Not A-Z? First time, keep as the string_arg
218
      string_arg = p - 1;
219
      #if ENABLED(DEBUG_GCODE_PARSER)
220
        if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG
221
      #endif
222
    }
223
 
224
    if (!WITHIN(*p, 'A', 'Z')) {                // Another parameter right away?
225
      while (*p && DECIMAL_SIGNED(*p)) p++;     // Skip over the value section of a parameter
226
      while (*p == ' ') p++;                    // Skip over all spaces
227
    }
228
  }
229
}
230
 
231
#if ENABLED(CNC_COORDINATE_SYSTEMS)
232
 
233
  // Parse the next parameter as a new command
234
  bool GCodeParser::chain() {
235
    #if ENABLED(FASTER_GCODE_PARSER)
236
      char *next_command = command_ptr;
237
      if (next_command) {
238
        while (*next_command && *next_command != ' ') ++next_command;
239
        while (*next_command == ' ') ++next_command;
240
        if (!*next_command) next_command = NULL;
241
      }
242
    #else
243
      const char *next_command = command_args;
244
    #endif
245
    if (next_command) parse(next_command);
246
    return !!next_command;
247
  }
248
 
249
#endif // CNC_COORDINATE_SYSTEMS
250
 
251
void GCodeParser::unknown_command_error() {
252
  SERIAL_ECHO_START();
253
  SERIAL_ECHOPAIR(MSG_UNKNOWN_COMMAND, command_ptr);
254
  SERIAL_CHAR('"');
255
  SERIAL_EOL();
256
}
257
 
258
#if ENABLED(DEBUG_GCODE_PARSER)
259
 
260
  void GCodeParser::debug() {
261
    SERIAL_ECHOPAIR("Command: ", command_ptr);
262
    SERIAL_ECHOPAIR(" (", command_letter);
263
    SERIAL_ECHO(codenum);
264
    SERIAL_ECHOLNPGM(")");
265
    #if ENABLED(FASTER_GCODE_PARSER)
266
      SERIAL_ECHOPGM(" args: \"");
267
      for (char c = 'A'; c <= 'Z'; ++c)
268
        if (seen(c)) { SERIAL_CHAR(c); SERIAL_CHAR(' '); }
269
    #else
270
      SERIAL_ECHOPAIR(" args: \"", command_args);
271
    #endif
272
    SERIAL_CHAR('"');
273
    if (string_arg) {
274
      SERIAL_ECHOPGM(" string: \"");
275
      SERIAL_ECHO(string_arg);
276
      SERIAL_CHAR('"');
277
    }
278
    SERIAL_ECHOPGM("\n\n");
279
    for (char c = 'A'; c <= 'Z'; ++c) {
280
      if (seen(c)) {
281
        SERIAL_ECHOPAIR("Code '", c); SERIAL_ECHOPGM("':");
282
        if (has_value()) {
283
          SERIAL_ECHOPAIR("\n    float: ", value_float());
284
          SERIAL_ECHOPAIR("\n     long: ", value_long());
285
          SERIAL_ECHOPAIR("\n    ulong: ", value_ulong());
286
          SERIAL_ECHOPAIR("\n   millis: ", value_millis());
287
          SERIAL_ECHOPAIR("\n   sec-ms: ", value_millis_from_seconds());
288
          SERIAL_ECHOPAIR("\n      int: ", value_int());
289
          SERIAL_ECHOPAIR("\n   ushort: ", value_ushort());
290
          SERIAL_ECHOPAIR("\n     byte: ", (int)value_byte());
291
          SERIAL_ECHOPAIR("\n     bool: ", (int)value_bool());
292
          SERIAL_ECHOPAIR("\n   linear: ", value_linear_units());
293
          SERIAL_ECHOPAIR("\n  celsius: ", value_celsius());
294
        }
295
        else
296
          SERIAL_ECHOPGM(" (no value)");
297
        SERIAL_ECHOPGM("\n\n");
298
      }
299
    }
300
  }
301
 
302
#endif // DEBUG_GCODE_PARSER