1 |
|
/******************************************************************** |
2 |
|
* Description: interp_internal.cc |
3 |
|
* |
4 |
|
* Derived from a work by Thomas Kramer |
5 |
|
* |
6 |
|
* Author: |
7 |
|
* License: GPL Version 2 |
8 |
|
* System: Linux |
9 |
|
* |
10 |
|
* Copyright (c) 2004 All rights reserved. |
11 |
|
* |
12 |
|
* Last change: |
13 |
|
********************************************************************/ |
14 |
|
#include <unistd.h> |
15 |
|
#include <stdio.h> |
16 |
|
#include <stdlib.h> |
17 |
|
#include <math.h> |
18 |
|
#include <string.h> |
19 |
|
#include <ctype.h> |
20 |
|
#include <sys/types.h> |
21 |
|
#include <sys/stat.h> |
22 |
|
#include "rs274ngc.hh" |
23 |
|
#include "rs274ngc_return.hh" |
24 |
|
#include "interp_internal.hh" // interpreter private definitions |
25 |
|
#include "rs274ngc_interp.hh" |
26 |
|
|
27 |
|
/****************************************************************************/ |
28 |
|
|
29 |
|
/*! close_and_downcase |
30 |
|
|
31 |
|
Returned Value: int |
32 |
|
If any of the following errors occur, this returns the error code shown. |
33 |
|
Otherwise, it returns INTERP_OK. |
34 |
|
1. A left parenthesis is found inside a comment: |
35 |
|
NCE_NESTED_COMMENT_FOUND |
36 |
|
2. The line ends before an open comment is closed: |
37 |
|
NCE_UNCLOSED_COMMENT_FOUND |
38 |
|
3. A newline character is found that is not followed by null: |
39 |
|
NCE_NULL_MISSING_AFTER_NEWLINE |
40 |
|
|
41 |
|
Side effects: See below |
42 |
|
|
43 |
|
Called by: read_text |
44 |
|
|
45 |
|
To simplify handling upper case letters, spaces, and tabs, this |
46 |
|
function removes spaces and and tabs and downcases everything on a |
47 |
|
line which is not part of a comment. |
48 |
|
|
49 |
|
Comments are left unchanged in place. Comments are anything |
50 |
|
enclosed in parentheses. Nested comments, indicated by a left |
51 |
|
parenthesis inside a comment, are illegal. |
52 |
|
|
53 |
|
The line must have a null character at the end when it comes in. |
54 |
|
The line may have one newline character just before the end. If |
55 |
|
there is a newline, it will be removed. |
56 |
|
|
57 |
|
Although this software system detects and rejects all illegal characters |
58 |
|
and illegal syntax, this particular function does not detect problems |
59 |
|
with anything but comments. |
60 |
|
|
61 |
|
We are treating RS274 code here as case-insensitive and spaces and |
62 |
|
tabs as if they have no meaning. [RS274D, page 6] says spaces and tabs |
63 |
|
are to be ignored by control. |
64 |
|
|
65 |
|
The KT and NGC manuals say nothing about case or spaces and tabs. |
66 |
|
|
67 |
|
*/ |
68 |
|
|
69 |
11161 |
int Interp::close_and_downcase(char *line) //!< string: one line of NC code |
70 |
|
{ |
71 |
|
int m; |
72 |
|
int n; |
73 |
|
int comment, semicomment; |
74 |
|
char item; |
75 |
11161 |
comment = semicomment = 0; |
76 |
333788 |
for (n = 0, m = 0; (item = line[m]) != '\0'; m++) { |
77 |
322627 |
if ((item == ';') && !comment) |
78 |
177 |
semicomment = 1; |
79 |
|
|
80 |
322627 |
if (semicomment) { |
81 |
5337 |
line[n++] = item; // pass literally |
82 |
5337 |
continue; |
83 |
|
} |
84 |
317290 |
if (comment) { |
85 |
36313 |
line[n++] = item; |
86 |
36313 |
if (item == ')') { |
87 |
|
comment = 0; |
88 |
34946 |
} else if (item == '(') |
89 |
|
ERS(NCE_NESTED_COMMENT_FOUND); |
90 |
280977 |
} else if ((item == ' ') || (item == '\t') || (item == '\r')); |
91 |
|
/* don't copy blank or tab or CR */ |
92 |
232335 |
else if (item == '\n') { /* don't copy newline *//* but check null follows */ |
93 |
|
CHKS((line[m + 1] != 0), NCE_NULL_MISSING_AFTER_NEWLINE); |
94 |
232335 |
} else if ((64 < item) && (item < 91)) { /* downcase upper case letters */ |
95 |
22232 |
line[n++] = (32 + item); |
96 |
210103 |
} else if ((item == '(') && !semicomment) { /* (comment is starting */ |
97 |
1367 |
comment = 1; |
98 |
1367 |
line[n++] = item; |
99 |
|
} else { |
100 |
208736 |
line[n++] = item; /* copy anything else */ |
101 |
|
} |
102 |
|
} |
103 |
11161 |
CHKS((comment), NCE_UNCLOSED_COMMENT_FOUND); |
104 |
11161 |
line[n] = 0; |
105 |
11161 |
return INTERP_OK; |
106 |
|
} |
107 |
|
|
108 |
|
|
109 |
|
/****************************************************************************/ |
110 |
|
|
111 |
|
/*! enhance_block |
112 |
|
|
113 |
|
Returned Value: |
114 |
|
If any of the following errors occur, this returns the error shown. |
115 |
|
Otherwise, it returns INTERP_OK. |
116 |
|
1. A g80 is in the block, no modal group 0 code that uses axes |
117 |
|
is in the block, and one or more axis values is given: |
118 |
|
NCE_CANNOT_USE_AXIS_VALUES_WITH_G80 |
119 |
|
2. A g52 g92 is in the block and no axis value is given: |
120 |
|
NCE_ALL_AXES_MISSING_WITH_G52_OR_G92 |
121 |
|
3. One g-code from group 1 and one from group 0, both of which can use |
122 |
|
axis values, are in the block: |
123 |
|
NCE_CANNOT_USE_TWO_G_CODES_THAT_BOTH_USE_AXIS_VALUES |
124 |
|
4. A g-code (other than 0 or 1, for which we are allowing all axes |
125 |
|
missing) from group 1 which can use axis values is in the block, |
126 |
|
but no axis value is given: NCE_ALL_AXES_MISSING_WITH_MOTION_CODE |
127 |
|
5. Axis values are given, but there is neither a g-code in the block |
128 |
|
nor an active previously given modal g-code that uses axis values: |
129 |
|
NCE_CANNOT_USE_AXIS_VALUES_WITHOUT_A_G_CODE_THAT_USES_THEM |
130 |
|
|
131 |
|
Side effects: |
132 |
|
The value of motion_to_be in the block is set. |
133 |
|
|
134 |
|
Called by: parse_line |
135 |
|
|
136 |
|
If there is a g-code for motion in the block (in g_modes[1]), |
137 |
|
set motion_to_be to that. Otherwise, if there is an axis value in the |
138 |
|
block and no g-code to use it (any such would be from group 0 in |
139 |
|
g_modes[0]), set motion_to_be to be the last motion saved (in |
140 |
|
settings->motion mode). |
141 |
|
|
142 |
|
This also make the checks described above. |
143 |
|
|
144 |
|
*/ |
145 |
|
|
146 |
6554 |
int Interp::enhance_block(block_pointer block, //!< pointer to a block to be checked |
147 |
|
setup_pointer settings) //!< pointer to machine settings |
148 |
|
{ |
149 |
|
int axis_flag; |
150 |
|
int ijk_flag; |
151 |
|
int polar_flag; |
152 |
|
int mode_zero_covets_axes; |
153 |
|
int mode0; |
154 |
|
int mode1; |
155 |
|
|
156 |
6554 |
if(block->radius_flag || block->theta_flag) { |
157 |
|
// someday, tediously add polar support for other planes here: |
158 |
|
CHKS((!_readers[(int)'x'] || !_readers[(int)'y']), _("Cannot use polar coordinate on a machine lacking X or Y axes")); |
159 |
|
CHKS(((settings->plane != CANON_PLANE_XY)), _("Cannot use polar coordinate except in G17 plane")); |
160 |
|
CHKS(((block->x_flag)), _("Cannot specify both polar coordinate and X word")); |
161 |
|
CHKS(((block->y_flag)), _("Cannot specify both polar coordinate and Y word")); |
162 |
|
} |
163 |
|
|
164 |
5302 |
axis_flag = ((block->x_flag) || (block->y_flag) || |
165 |
5038 |
(block->z_flag) || (block->a_flag) || |
166 |
5037 |
(block->b_flag) || (block->c_flag) || |
167 |
11591 |
(block->u_flag) || (block->v_flag) || |
168 |
6554 |
(block->w_flag)); |
169 |
6554 |
polar_flag = (block->radius_flag) || (block->theta_flag); |
170 |
6554 |
ijk_flag = ((block->i_flag) || (block->j_flag) || |
171 |
6554 |
(block->k_flag)); |
172 |
6554 |
mode0 = block->g_modes[0]; |
173 |
6554 |
mode1 = block->g_modes[1]; |
174 |
|
mode_zero_covets_axes = |
175 |
13050 |
((mode0 == G_10) || (mode0 == G_28) || (mode0 == G_30) |
176 |
13050 |
|| (mode0 == G_52) || (mode0 == G_92)); |
177 |
|
|
178 |
6554 |
if (mode1 != -1) { |
179 |
1437 |
if (mode1 == G_80) { |
180 |
1 |
CHKS(((polar_flag || axis_flag) && (!mode_zero_covets_axes)), |
181 |
|
NCE_CANNOT_USE_AXIS_VALUES_WITH_G80); |
182 |
1 |
CHKS((polar_flag && mode0 == G_92), _("Polar coordinates can only be used for motion")); |
183 |
1 |
CHKS(((!axis_flag) && (mode0 == G_52 || mode0 == G_92)), |
184 |
|
NCE_ALL_AXES_MISSING_WITH_G52_OR_G92); |
185 |
|
} else { |
186 |
1436 |
CHKS(mode_zero_covets_axes, |
187 |
|
NCE_CANNOT_USE_TWO_G_CODES_THAT_BOTH_USE_AXIS_VALUES); |
188 |
1436 |
CHKS(((!axis_flag && !polar_flag) && |
189 |
|
mode1 != G_0 && mode1 != G_1 && |
190 |
|
mode1 != G_2 && mode1 != G_3 && mode1 != G_5_2 && |
191 |
|
! IS_USER_GCODE(mode1)), |
192 |
|
NCE_ALL_AXES_MISSING_WITH_MOTION_CODE); |
193 |
|
} |
194 |
1436 |
block->motion_to_be = mode1; |
195 |
5117 |
} else if (mode_zero_covets_axes) { /* other 3 can get by without axes but not G92 */ |
196 |
63 |
CHKS((polar_flag && mode0 == G_92), _("Polar coordinates can only be used for motion")); |
197 |
63 |
CHKS(((!axis_flag) && |
198 |
|
(block->g_modes[0] == G_52 || block->g_modes[0] == G_92)), |
199 |
|
NCE_ALL_AXES_MISSING_WITH_G52_OR_G92); |
200 |
5054 |
} else if (axis_flag || polar_flag) { |
201 |
25 |
CHKS(((settings->motion_mode == -1) |
202 |
|
|| (settings->motion_mode == G_80)) && (block->g_modes[8] != G_43_1), |
203 |
|
NCE_CANNOT_USE_AXIS_VALUES_WITHOUT_A_G_CODE_THAT_USES_THEM); |
204 |
25 |
if (block->g_modes[8] != G_43_1) { |
205 |
25 |
block->motion_to_be = settings->motion_mode; |
206 |
|
} |
207 |
5029 |
} else if (!axis_flag && !polar_flag && ijk_flag && (settings->motion_mode == G_2 || settings->motion_mode == G_3)) { |
208 |
|
// this is a block like simply "i1" which should be accepted if we're in arc mode |
209 |
|
block->motion_to_be = settings->motion_mode; |
210 |
|
} |
211 |
6553 |
CHKS((polar_flag && block->motion_to_be == -1), _("Polar coordinates can only be used for motion")); |
212 |
|
return INTERP_OK; |
213 |
|
} |
214 |
|
|
215 |
|
|
216 |
|
/****************************************************************************/ |
217 |
|
|
218 |
|
/*! init_block |
219 |
|
|
220 |
|
Returned Value: int (INTERP_OK) |
221 |
|
|
222 |
|
Side effects: |
223 |
|
Values in the block are reset as described below. |
224 |
|
|
225 |
|
Called by: parse_line |
226 |
|
|
227 |
|
This system reuses the same block over and over, rather than building |
228 |
|
a new one for each line of NC code. The block is re-initialized before |
229 |
|
each new line of NC code is read. |
230 |
|
|
231 |
|
The block contains many slots for values which may or may not be present |
232 |
|
on a line of NC code. For some of these slots, there is a flag which |
233 |
|
is turned on (at the time time value of the slot is read) if the item |
234 |
|
is present. For slots whose values are to be read which do not have a |
235 |
|
flag, there is always some excluded range of values. Setting the |
236 |
|
initial value of these slot to some number in the excluded range |
237 |
|
serves to show that a value for that slot has not been read. |
238 |
|
|
239 |
|
The rules for the indicators for slots whose values may be read are: |
240 |
|
1. If the value may be an arbitrary real number (which is always stored |
241 |
|
internally as a double), a flag is needed to indicate if a value has |
242 |
|
been read. All such flags are initialized to false. |
243 |
|
Note that the value itself is not initialized; there is no point in it. |
244 |
|
2. If the value must be a non-negative real number (which is always stored |
245 |
|
internally as a double), a value of -1.0 indicates the item is not present. |
246 |
|
3. If the value must be an unsigned integer (which is always stored |
247 |
|
internally as an int), a value of -1 indicates the item is not present. |
248 |
|
(RS274/NGC does not use any negative integers.) |
249 |
|
4. If the value is a character string (only the comment slot is one), the |
250 |
|
first character is set to 0 (NULL). |
251 |
|
|
252 |
|
*/ |
253 |
|
|
254 |
9455 |
int Interp::init_block(block_pointer block) //!< pointer to a block to be initialized or reset |
255 |
|
{ |
256 |
|
int n; |
257 |
9455 |
block->breadcrumbs = 0; // clear execution trail |
258 |
9455 |
block->executing_remap = NULL; |
259 |
9455 |
block->param_cnt = 0; |
260 |
9455 |
block->remappings.clear(); |
261 |
9455 |
block->builtin_used = false; |
262 |
|
|
263 |
9455 |
block->a_flag = false; |
264 |
9455 |
block->b_flag = false; |
265 |
9455 |
block->c_flag = false; |
266 |
9455 |
block->comment[0] = 0; |
267 |
9455 |
block->d_flag = false; |
268 |
9455 |
block->e_flag = false; |
269 |
9455 |
block->f_flag = false; |
270 |
160735 |
for (n = 0; n < 16; n++) { |
271 |
151280 |
block->g_modes[n] = -1; |
272 |
|
} |
273 |
9455 |
block->h_flag = false; |
274 |
9455 |
block->h_number = -1; |
275 |
9455 |
block->i_flag = false; |
276 |
9455 |
block->j_flag = false; |
277 |
9455 |
block->k_flag = false; |
278 |
9455 |
block->l_number = -1; |
279 |
9455 |
block->l_flag = false; |
280 |
9455 |
block->line_number = -1; |
281 |
9455 |
block->n_number = -1; |
282 |
9455 |
block->motion_to_be = -1; |
283 |
9455 |
block->m_count = 0; |
284 |
113460 |
for (n = 0; n < 11; n++) { |
285 |
104005 |
block->m_modes[n] = -1; |
286 |
|
} |
287 |
9455 |
block->user_m = 0; |
288 |
9455 |
block->p_number = -1.0; |
289 |
9455 |
block->p_flag = false; |
290 |
9455 |
block->q_flag = false; |
291 |
9455 |
block->q_number = -1.0; |
292 |
9455 |
block->r_flag = false; |
293 |
9455 |
block->s_flag = false; |
294 |
9455 |
block->t_flag = false; |
295 |
9455 |
block->u_flag = false; |
296 |
9455 |
block->v_flag = false; |
297 |
9455 |
block->w_flag = false; |
298 |
9455 |
block->x_flag = false; |
299 |
9455 |
block->y_flag = false; |
300 |
9455 |
block->z_flag = false; |
301 |
|
|
302 |
9455 |
block->theta_flag = false; |
303 |
9455 |
block->radius_flag = false; |
304 |
|
|
305 |
9455 |
block->o_type = O_none; |
306 |
9455 |
block->o_name = 0; |
307 |
9455 |
block->call_type = -1; |
308 |
|
|
309 |
9455 |
return INTERP_OK; |
310 |
|
} |
311 |
|
|
312 |
|
|
313 |
|
/****************************************************************************/ |
314 |
|
|
315 |
|
/*! parse_line |
316 |
|
|
317 |
|
Returned Value: int |
318 |
|
If any of the following functions returns an error code, |
319 |
|
this returns that code. |
320 |
|
init_block |
321 |
|
read_items |
322 |
|
enhance_block |
323 |
|
check_items |
324 |
|
Otherwise, it returns INTERP_OK. |
325 |
|
|
326 |
|
Side effects: |
327 |
|
One RS274 line is read into a block and the block is checked for |
328 |
|
errors. System parameters may be reset. |
329 |
|
|
330 |
|
Called by: Interp::read |
331 |
|
|
332 |
|
*/ |
333 |
|
|
334 |
9455 |
int Interp::parse_line(char *line, //!< array holding a line of RS274 code |
335 |
|
block_pointer block, //!< pointer to a block to be filled |
336 |
|
setup_pointer settings) //!< pointer to machine settings |
337 |
|
{ |
338 |
9455 |
CHP(init_block(block)); |
339 |
9455 |
CHP(read_items(block, line, settings->parameters)); |
340 |
|
|
341 |
9445 |
if(settings->skipping_o == 0) |
342 |
|
{ |
343 |
6554 |
CHP(enhance_block(block, settings)); |
344 |
6553 |
CHP(check_items(block, settings)); |
345 |
6551 |
int n = find_remappings(block,settings); |
346 |
6551 |
if (n) logRemap("parse_line: found %d remappings",n); |
347 |
|
} |
348 |
|
return INTERP_OK; |
349 |
|
} |
350 |
|
|
351 |
|
/****************************************************************************/ |
352 |
|
|
353 |
|
/*! precedence |
354 |
|
|
355 |
|
Returned Value: int |
356 |
|
This returns an integer representing the precedence level of an_operator |
357 |
|
|
358 |
|
Side Effects: None |
359 |
|
|
360 |
|
Called by: read_real_expression |
361 |
|
|
362 |
|
To add additional levels of operator precedence, edit this function. |
363 |
|
|
364 |
|
*/ |
365 |
|
|
366 |
14036 |
int Interp::precedence(int an_operator) |
367 |
|
{ |
368 |
|
switch(an_operator) |
369 |
|
{ |
370 |
|
case RIGHT_BRACKET: |
371 |
|
return 1; |
372 |
|
|
373 |
|
case AND2: |
374 |
|
case EXCLUSIVE_OR: |
375 |
|
case NON_EXCLUSIVE_OR: |
376 |
|
return 2; |
377 |
|
|
378 |
|
case LT: |
379 |
|
case EQ: |
380 |
|
case NE: |
381 |
|
case LE: |
382 |
|
case GE: |
383 |
|
case GT: |
384 |
|
return 3; |
385 |
|
|
386 |
|
case MINUS: |
387 |
|
case PLUS: |
388 |
|
return 4; |
389 |
|
|
390 |
|
case NO_OPERATION: |
391 |
|
case DIVIDED_BY: |
392 |
|
case MODULO: |
393 |
|
case TIMES: |
394 |
|
return 5; |
395 |
|
|
396 |
|
case POWER: |
397 |
|
return 6; |
398 |
|
} |
399 |
|
// should never happen |
400 |
|
return 0; |
401 |
|
} |
402 |
|
|
403 |
|
|
404 |
9 |
int Interp::refresh_actual_position(setup_pointer settings) |
405 |
|
{ |
406 |
9 |
settings->current_x = GET_EXTERNAL_POSITION_X(); |
407 |
9 |
settings->current_y = GET_EXTERNAL_POSITION_Y(); |
408 |
9 |
settings->current_z = GET_EXTERNAL_POSITION_Z(); |
409 |
9 |
settings->AA_current = GET_EXTERNAL_POSITION_A(); |
410 |
9 |
settings->BB_current = GET_EXTERNAL_POSITION_B(); |
411 |
9 |
settings->CC_current = GET_EXTERNAL_POSITION_C(); |
412 |
9 |
settings->u_current = GET_EXTERNAL_POSITION_U(); |
413 |
9 |
settings->v_current = GET_EXTERNAL_POSITION_V(); |
414 |
9 |
settings->w_current = GET_EXTERNAL_POSITION_W(); |
415 |
|
|
416 |
9 |
return INTERP_OK; |
417 |
|
} |
418 |
|
|
419 |
|
|
420 |
|
|
421 |
|
/****************************************************************************/ |
422 |
|
|
423 |
|
/*! set_probe_data |
424 |
|
|
425 |
|
Returned Value: int (INTERP_OK) |
426 |
|
|
427 |
|
Side effects: |
428 |
|
The current position is set. |
429 |
|
System parameters for probe position are set. |
430 |
|
|
431 |
|
Called by: Interp::read |
432 |
|
|
433 |
|
*/ |
434 |
|
|
435 |
|
int Interp::set_probe_data(setup_pointer settings) //!< pointer to machine settings |
436 |
|
{ |
437 |
|
double a, b, c; |
438 |
|
refresh_actual_position(settings); |
439 |
|
settings->parameters[5061] = GET_EXTERNAL_PROBE_POSITION_X(); |
440 |
|
settings->parameters[5062] = GET_EXTERNAL_PROBE_POSITION_Y(); |
441 |
|
settings->parameters[5063] = GET_EXTERNAL_PROBE_POSITION_Z(); |
442 |
|
|
443 |
|
a = GET_EXTERNAL_PROBE_POSITION_A(); |
444 |
|
if(settings->a_axis_wrapped) { |
445 |
|
a = fmod(a, 360.0); |
446 |
|
if(a<0) a += 360.0; |
447 |
|
} |
448 |
|
settings->parameters[5064] = a; |
449 |
|
|
450 |
|
b = GET_EXTERNAL_PROBE_POSITION_B(); |
451 |
|
if(settings->b_axis_wrapped) { |
452 |
|
b = fmod(b, 360.0); |
453 |
|
if(b<0) b += 360.0; |
454 |
|
} |
455 |
|
settings->parameters[5065] = b; |
456 |
|
|
457 |
|
c = GET_EXTERNAL_PROBE_POSITION_C(); |
458 |
|
if(settings->c_axis_wrapped) { |
459 |
|
c = fmod(c, 360.0); |
460 |
|
if(c<0) c += 360.0; |
461 |
|
} |
462 |
|
settings->parameters[5066] = c; |
463 |
|
|
464 |
|
settings->parameters[5067] = GET_EXTERNAL_PROBE_POSITION_U(); |
465 |
|
settings->parameters[5068] = GET_EXTERNAL_PROBE_POSITION_V(); |
466 |
|
settings->parameters[5069] = GET_EXTERNAL_PROBE_POSITION_W(); |
467 |
|
settings->parameters[5070] = (double) GET_EXTERNAL_PROBE_TRIPPED_VALUE(); |
468 |
|
|
469 |
|
// was an undocumented feature?: settings->parameters[5067] = GET_EXTERNAL_PROBE_VALUE(); |
470 |
|
return INTERP_OK; |
471 |
|
} |
472 |
|
|
473 |
44 |
int Interp::call_level(void) { return _setup.call_level; } |