1 |
|
/******************************************************************** |
2 |
|
* Description: rs274ngc_pre.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 |
|
/* rs274ngc.cc |
15 |
|
|
16 |
|
This rs274ngc.cc file contains the source code for (1) the kernel of |
17 |
|
several rs274ngc interpreters and (2) two of the four sets of interface |
18 |
|
functions declared in canon.hh: |
19 |
|
1. interface functions to call to tell the interpreter what to do. |
20 |
|
These all return a status value. |
21 |
|
2. interface functions to call to get information from the interpreter. |
22 |
|
|
23 |
|
Kernel functions call each other. A few kernel functions are called by |
24 |
|
interface functions. |
25 |
|
|
26 |
|
Interface function names all begin with "Interp::". |
27 |
|
|
28 |
|
Error handling is by returning a status value of either a non-error |
29 |
|
code (INTERP_OK, INTERP_EXIT, etc.) or some specific error code |
30 |
|
from each function where there is a possibility of error. If an error |
31 |
|
occurs, processing is always stopped, and control is passed back up |
32 |
|
through the function call hierarchy to an interface function; the |
33 |
|
error code is also passed back up. The stack of functions called is |
34 |
|
also recorded. The external program calling an interface function may |
35 |
|
then handle the error further as it sees fit. |
36 |
|
|
37 |
|
Since returned values are usually used as just described to handle the |
38 |
|
possibility of errors, an alternative method of passing calculated |
39 |
|
values is required. In general, if function A needs a value for |
40 |
|
variable V calculated by function B, this is handled by passing a |
41 |
|
pointer to V from A to B, and B calculates and sets V. |
42 |
|
|
43 |
|
There are a lot of functions named read_XXXX. All such functions read |
44 |
|
characters from a string using a counter. They all reset the counter |
45 |
|
to point at the character in the string following the last one used by |
46 |
|
the function. The counter is passed around from function to function |
47 |
|
by using pointers to it. The first character read by each of these |
48 |
|
functions is expected to be a member of some set of characters (often |
49 |
|
a specific character), and each function checks the first character. |
50 |
|
|
51 |
|
This version of the interpreter not saving input lines. A list of all |
52 |
|
lines will be needed in future versions to implement loops, and |
53 |
|
probably for other purposes. |
54 |
|
|
55 |
|
This version does not use any additional memory as it runs. No |
56 |
|
memory is allocated by the source code. |
57 |
|
|
58 |
|
This version does not suppress superfluous commands, such as a command |
59 |
|
to start the spindle when the spindle is already turning, or a command |
60 |
|
to turn on flood coolant, when flood coolant is already on. When the |
61 |
|
interpreter is being used for direct control of the machining center, |
62 |
|
suppressing superfluous commands might confuse the user and could be |
63 |
|
dangerous, but when it is used to translate from one file to another, |
64 |
|
suppression can produce more concise output. Future versions might |
65 |
|
include an option for suppressing superfluous commands. |
66 |
|
|
67 |
|
****************************************************************************/ |
68 |
|
#include "python_plugin.hh" |
69 |
|
#include <boost/python/dict.hpp> |
70 |
|
#include <boost/python/extract.hpp> |
71 |
|
#include <boost/python/import.hpp> |
72 |
|
#include <boost/python/list.hpp> |
73 |
|
#include <boost/python/scope.hpp> |
74 |
|
#include <boost/python/tuple.hpp> |
75 |
|
#include <unistd.h> |
76 |
|
#include <stdio.h> |
77 |
|
#include <stdlib.h> |
78 |
|
#include <math.h> |
79 |
|
#include <string.h> |
80 |
|
#include <ctype.h> |
81 |
|
#include <sys/types.h> |
82 |
|
#include <sys/stat.h> |
83 |
|
#include <stdarg.h> |
84 |
|
#include <sys/time.h> |
85 |
|
#include <time.h> |
86 |
|
#include <unistd.h> |
87 |
|
#include <libintl.h> |
88 |
|
#include <set> |
89 |
|
#include <stdexcept> |
90 |
|
|
91 |
|
#include "rtapi.h" |
92 |
|
#include "inifile.hh" // INIFILE |
93 |
|
#include "rs274ngc.hh" |
94 |
|
#include "rs274ngc_return.hh" |
95 |
|
#include "interp_internal.hh" // interpreter private definitions |
96 |
|
#include "interp_queue.hh" |
97 |
|
#include "rs274ngc_interp.hh" |
98 |
|
|
99 |
|
#include "units.h" |
100 |
|
|
101 |
|
namespace bp = boost::python; |
102 |
|
|
103 |
|
extern char * _rs274ngc_errors[]; |
104 |
|
|
105 |
|
const char *Interp::interp_status(int status) { |
106 |
|
static char statustext[50]; |
107 |
|
static const char *msgs[] = { "INTERP_OK", "INTERP_EXIT", |
108 |
|
"INTERP_EXECUTE_FINISH", "INTERP_ENDFILE", "INTERP_FILE_NOT_OPEN", |
109 |
|
"INTERP_ERROR" }; |
110 |
|
sprintf(statustext, "%s%s%d", ((status >= INTERP_OK) && (status |
111 |
|
<= INTERP_ERROR)) ? msgs[status] : "unknown interpreter error", |
112 |
|
(status > INTERP_MIN_ERROR) ? " - error: " : " - ", status); |
113 |
|
return statustext; |
114 |
|
} |
115 |
|
|
116 |
|
extern struct _inittab builtin_modules[]; |
117 |
|
int trace; |
118 |
|
static char savedError[LINELEN+1]; |
119 |
|
|
120 |
68 |
Interp::Interp() |
121 |
|
: log_file(stderr), |
122 |
136 |
_setup{} |
123 |
|
{ |
124 |
68 |
_setup.init_once = 1; |
125 |
68 |
init_named_parameters(); // need this before Python init. |
126 |
|
|
127 |
68 |
if (!PythonPlugin::instantiate(builtin_modules)) { // factory |
128 |
|
Error("Interp ctor: cant instantiate Python plugin"); |
129 |
68 |
return; |
130 |
|
} |
131 |
|
|
132 |
|
try { |
133 |
|
// this import will register the C++->Python converter for Interp |
134 |
136 |
bp::object interp_module = bp::import("interpreter"); |
135 |
|
|
136 |
|
// use a boost::cref to avoid per-call instantiation of the |
137 |
|
// Interp Python wrapper (used for the 'self' parameter in handlers) |
138 |
|
// since interp.init() may be called repeatedly this would create a new |
139 |
|
// wrapper instance on every init(), abandoning the old one and all user attributes |
140 |
|
// tacked onto it, so make sure this is done exactly once |
141 |
68 |
_setup.pythis = new boost::python::object(boost::cref(*this)); |
142 |
|
|
143 |
|
// alias to 'interpreter.this' for the sake of ';py, .... ' comments |
144 |
|
// besides 'this', eventually use proper instance names to handle |
145 |
|
// several instances |
146 |
204 |
bp::scope(interp_module).attr("this") = *_setup.pythis; |
147 |
|
|
148 |
|
// make "this" visible without importing interpreter explicitly |
149 |
68 |
bp::object retval; |
150 |
68 |
python_plugin->run_string("from interpreter import this", retval, false); |
151 |
|
} |
152 |
|
catch (bp::error_already_set) { |
153 |
|
std::string exception_msg; |
154 |
|
if (PyErr_Occurred()) { |
155 |
|
exception_msg = handle_pyerror(); |
156 |
|
} else |
157 |
|
exception_msg = "unknown exception"; |
158 |
|
bp::handle_exception(); |
159 |
|
PyErr_Clear(); |
160 |
|
Error("PYTHON: exception during 'this' export:\n%s\n",exception_msg.c_str()); |
161 |
|
} |
162 |
|
} |
163 |
|
|
164 |
1 |
InterpBase *makeInterp() |
165 |
|
{ |
166 |
1 |
return new Interp; |
167 |
|
} |
168 |
|
|
169 |
|
Interp::~Interp() { |
170 |
|
|
171 |
|
if(log_file) { |
172 |
|
if(log_file != stderr) |
173 |
|
fclose(log_file); |
174 |
|
log_file = 0; |
175 |
|
} |
176 |
|
} |
177 |
|
|
178 |
|
void Interp::doLog(unsigned int flags, const char *file, int line, |
179 |
|
const char *fmt, ...) |
180 |
|
{ |
181 |
|
struct timeval tv; |
182 |
|
struct tm *tm; |
183 |
|
va_list ap; |
184 |
|
|
185 |
|
if (flags & LOG_TIME) { |
186 |
|
gettimeofday(&tv, NULL); |
187 |
|
tm = localtime(&tv.tv_sec); |
188 |
|
|
189 |
|
fprintf(log_file, "%04d%02d%02d-%02d:%02d:%02d.%03ld ", |
190 |
|
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, |
191 |
|
tm->tm_hour, tm->tm_min, tm->tm_sec, |
192 |
|
tv.tv_usec/1000); |
193 |
|
} |
194 |
|
if (flags & LOG_PID) { |
195 |
|
fprintf(log_file, "%4d ",getpid()); |
196 |
|
} |
197 |
|
if (flags & LOG_FILENAME) { |
198 |
|
fprintf(log_file, "%s:%d: ",file,line); |
199 |
|
} |
200 |
|
|
201 |
|
va_start(ap, fmt); |
202 |
|
vfprintf(log_file, fmt, ap); |
203 |
|
fflush(log_file); |
204 |
|
va_end(ap); |
205 |
|
} |
206 |
|
|
207 |
|
/****************************************************************************/ |
208 |
|
|
209 |
|
/* |
210 |
|
|
211 |
|
The functions in this section of this file are functions for |
212 |
|
external programs to call to tell the rs274ngc interpreter |
213 |
|
what to do. They are declared in rs274ngc.hh. |
214 |
|
|
215 |
|
*/ |
216 |
|
|
217 |
|
/***********************************************************************/ |
218 |
|
|
219 |
|
/*! Interp::close |
220 |
|
|
221 |
|
Returned Value: int (INTERP_OK) |
222 |
|
|
223 |
|
Side Effects: |
224 |
|
The NC-code file is closed if open. |
225 |
|
The _setup world model is reset. |
226 |
|
|
227 |
|
Called By: external programs |
228 |
|
|
229 |
|
*/ |
230 |
|
|
231 |
71 |
int Interp::close() |
232 |
|
{ |
233 |
71 |
logOword("close()"); |
234 |
|
// be "lazy" only if we're not aborting a call in progress |
235 |
|
// in which case we need to reset() the call stack |
236 |
|
// this does not reset the filename properly |
237 |
71 |
if(_setup.use_lazy_close) // && (_setup.call_level == 0)) |
238 |
|
{ |
239 |
17 |
_setup.lazy_closing = 1; |
240 |
17 |
return INTERP_OK; |
241 |
|
} |
242 |
|
|
243 |
54 |
if (_setup.file_pointer != NULL) { |
244 |
53 |
fclose(_setup.file_pointer); |
245 |
53 |
_setup.file_pointer = NULL; |
246 |
53 |
_setup.percent_flag = false; |
247 |
|
} |
248 |
54 |
reset(); |
249 |
|
|
250 |
54 |
return INTERP_OK; |
251 |
|
} |
252 |
|
|
253 |
|
|
254 |
|
/***********************************************************************/ |
255 |
|
|
256 |
|
/*! Interp::execute |
257 |
|
|
258 |
|
Returned Value: int) |
259 |
|
If execute_block returns INTERP_EXIT, this returns that. |
260 |
|
If execute_block returns INTERP_EXECUTE_FINISH, this returns that. |
261 |
|
If execute_block returns an error code, this returns that code. |
262 |
|
Otherwise, this returns INTERP_OK. |
263 |
|
|
264 |
|
Side Effects: |
265 |
|
Calls to canonical machining commands are made. |
266 |
|
The interpreter variables are changed. |
267 |
|
At the end of the program, the file is closed. |
268 |
|
If using a file, the active G codes and M codes are updated. |
269 |
|
|
270 |
|
Called By: external programs |
271 |
|
|
272 |
|
This executes a previously parsed block. |
273 |
|
|
274 |
|
*/ |
275 |
|
|
276 |
11148 |
int Interp::_execute(const char *command) |
277 |
|
{ |
278 |
|
int status; |
279 |
|
int n; |
280 |
11148 |
int MDImode = 0; |
281 |
11148 |
block_pointer eblock = &EXECUTING_BLOCK(_setup); |
282 |
|
extern const char *call_statenames[]; |
283 |
|
extern const char *call_typenames[]; |
284 |
|
extern const char *o_ops[]; |
285 |
|
|
286 |
11148 |
if (NULL != command) { |
287 |
2 |
MDImode = 1; |
288 |
2 |
status = read(command); |
289 |
2 |
if (status != INTERP_OK) { |
290 |
|
// if (status > INTERP_MIN_ERROR) |
291 |
|
// _setup.remap_level = 0; |
292 |
|
return status; |
293 |
|
} |
294 |
|
} |
295 |
11148 |
logDebug("execute:%s %s='%s' mdi_int=%d o_type=%s o_name=%s cl=%d rl=%d type=%s state=%s", |
296 |
|
MDImode ? "MDI" : "auto", |
297 |
|
command ? "command" : "line", |
298 |
|
command ? command : _setup.linetext, |
299 |
|
_setup.mdi_interrupt, o_ops[eblock->o_type], eblock->o_name, |
300 |
|
_setup.call_level,_setup.remap_level, |
301 |
|
eblock->call_type < 0 ? "*unset*" : call_typenames[eblock->call_type], |
302 |
|
call_statenames[_setup.call_state]); |
303 |
|
|
304 |
|
// process control functions -- will skip if skipping |
305 |
11148 |
if ((eblock->o_name != 0) || _setup.mdi_interrupt) { |
306 |
3413 |
status = convert_control_functions(eblock, &_setup); |
307 |
3413 |
CHP(status); // relinquish control if INTERP_EXCUTE_FINISH, INTERP_ERROR etc |
308 |
|
|
309 |
|
// let MDI code call subroutines. |
310 |
|
// !!!KL not clear what happens if last execution failed while in |
311 |
|
// !!!KL a subroutine |
312 |
|
|
313 |
|
// NOTE: the last executed file will still be open, because "close" |
314 |
|
// is really a lazy close. |
315 |
|
|
316 |
|
// we had an INTERP_OK, so no need to set up another call to finish after sync() |
317 |
3412 |
if (_setup.mdi_interrupt) { |
318 |
1 |
_setup.mdi_interrupt = false; |
319 |
1 |
MDImode = 1; |
320 |
|
} |
321 |
3412 |
logDebug("!!!KL Open file is:%s:", _setup.filename); |
322 |
3412 |
logDebug("MDImode = %d", MDImode); |
323 |
3433 |
while(MDImode && _setup.call_level) // we are still in a subroutine |
324 |
|
{ |
325 |
22 |
status = read(0); // reads from current file and calls parse |
326 |
22 |
if (status > INTERP_MIN_ERROR) |
327 |
|
CHP(status); |
328 |
22 |
status = execute(); // special handling for mdi errors |
329 |
22 |
if (status != INTERP_OK) { |
330 |
1 |
if (status == INTERP_EXECUTE_FINISH) { |
331 |
1 |
_setup.mdi_interrupt = true; |
332 |
|
} |
333 |
1 |
CHP(status); |
334 |
|
} |
335 |
|
} |
336 |
3411 |
_setup.mdi_interrupt = false; |
337 |
3411 |
if (MDImode) |
338 |
2 |
FINISH(); |
339 |
|
return INTERP_OK; |
340 |
|
} |
341 |
|
|
342 |
|
// skip if skipping |
343 |
7735 |
if(_setup.skipping_o) |
344 |
|
{ |
345 |
2263 |
logDebug("skipping to: %s", _setup.skipping_o); |
346 |
|
return INTERP_OK; |
347 |
|
} |
348 |
|
|
349 |
451 |
for (n = 0; n < _setup.parameter_occurrence; n++) |
350 |
|
{ // copy parameter settings from parameter buffer into parameter table |
351 |
451 |
_setup.parameters[_setup.parameter_numbers[n]] |
352 |
451 |
= _setup.parameter_values[n]; |
353 |
|
} |
354 |
|
|
355 |
|
// logDebug("_setup.named_parameter_occurrence = %d", |
356 |
|
// _setup.named_parameter_occurrence); |
357 |
1670 |
for (n = 0; n < _setup.named_parameter_occurrence; n++) |
358 |
|
{ // copy parameter settings from parameter buffer into parameter table |
359 |
|
|
360 |
1671 |
logDebug("storing param:|%s|", _setup.named_parameters[n]); |
361 |
1671 |
CHP(store_named_param(&_setup, _setup.named_parameters[n], |
362 |
|
_setup.named_parameter_values[n])); |
363 |
|
} |
364 |
5471 |
_setup.named_parameter_occurrence = 0; |
365 |
|
|
366 |
5471 |
if (_setup.line_length != 0) { /* line not blank */ |
367 |
|
|
368 |
|
// at this point we have a parsed block |
369 |
|
// if items are to be remapped the flow is as follows: |
370 |
|
// |
371 |
|
// 1. push this block onto the remap stack because this might take several |
372 |
|
// interp invcocations to finish, while other blocks will be parsed and |
373 |
|
// executed by the oword subs. The top-of-stack block is the 'current |
374 |
|
// remapped block' or CONTROLLING_BLOCK. |
375 |
|
// |
376 |
|
// 2. execute the remap stack top level block, ticking off all items which are done. |
377 |
|
// |
378 |
|
// 3. when a remap operation is encountered, this will result in a call like so: |
379 |
|
// 'o<replacement>call'. |
380 |
|
// |
381 |
|
// this replacement call is parsed with read() into _setup.blocks[0] by the |
382 |
|
// corresponding routine (see e.g. handling of T in interp_execute.cc) |
383 |
|
// through calling into convert_remapped_code() |
384 |
|
// |
385 |
|
// 4. The oword call code might execute an optional prologue handler which is called |
386 |
|
// when the subroutine environment is set up (parameters set, execution of |
387 |
|
// body to begin). This is the way to set local named parameters e.g. for canned cycles. |
388 |
|
// |
389 |
|
// 5. The oword endsub/return code might call an epilogue handler |
390 |
|
// which finishes any work at the Python level on endsub/return, and thereafter |
391 |
|
// calls back into remap_finished(). |
392 |
|
// |
393 |
|
// 6. The execution stops after parsing, and returns with an indication of the |
394 |
|
// execution phase. We use negative values of enum steps to distinguish them |
395 |
|
// from normal INTERP_* type codes which are all >= 0. |
396 |
|
// |
397 |
|
// 7. In MDI mode, we have to kick execution by replicating code from above |
398 |
|
// to get the osub call going. |
399 |
|
// |
400 |
|
// 8. In Auto mode, we do an initial execute(0) to get things going, thereafer |
401 |
|
// task will do it for us. |
402 |
|
// |
403 |
|
// 9. When a replacment sub finishes, remap_finished() continues execution of |
404 |
|
// the current remapped block until done. |
405 |
|
// |
406 |
9020 |
if (eblock->remappings.size() > 0) { |
407 |
|
std::set<int>::iterator it; |
408 |
|
int next_remap = *eblock->remappings.begin(); |
409 |
|
logRemap("found remap %d in '%s', level=%d filename=%s line=%d", |
410 |
|
next_remap,_setup.blocktext,_setup.call_level,_setup.filename,_setup.sequence_number); |
411 |
|
|
412 |
|
|
413 |
|
CHP(enter_remap()); |
414 |
|
block_pointer cblock = &CONTROLLING_BLOCK(_setup); |
415 |
|
cblock->phase = next_remap; |
416 |
|
// execute up to the first remap including read() of its handler |
417 |
|
// this also sets cblock->executing_remap |
418 |
|
status = execute_block(cblock, &_setup); |
419 |
|
#if 0 |
420 |
|
// this is too naive a test and needs improving (aka: not segfault). |
421 |
|
// It needs to kick in only for new codes, not remapped ones, for which |
422 |
|
// recursion just means 'use builtin semantics' |
423 |
|
// add some kind of 'is_remapped_builtin()' macro or test method |
424 |
|
|
425 |
|
// detect a remapping recursion. |
426 |
|
// since each remapped item pushes a new block onto the remap stack, we walk |
427 |
|
// the remap stack searching for an identical remap below the TOS |
428 |
|
for (int i = _setup.remap_level - 1; i > 0; i--) { |
429 |
|
if (_setup.blocks[i].executing_remap == cblock->executing_remap) { |
430 |
|
ERS("recursive remapping for %s detected", cblock->executing_remap->name); |
431 |
|
} |
432 |
|
} |
433 |
|
#endif |
434 |
|
// All items up to the first remap item have been executed. |
435 |
|
// The remap item procedure call has been parsed into _setup.blocks[0], |
436 |
|
// the EXECUTING_BLOCK. |
437 |
|
// after parsing a handler, execute_block() either fails to toplevel or |
438 |
|
// returns the negative value of phase (to distinguish them from INTERP_* codes which are all >= 0) |
439 |
|
|
440 |
|
if (status < 0) { |
441 |
|
|
442 |
|
// the remap phase indicator was returned. |
443 |
|
// sanity: |
444 |
|
if (cblock->remappings.find(- status) == cblock->remappings.end()) { |
445 |
|
ERS("BUG: execute_block: got %d - not in remappings() !! (next_remap=%d)",- status,next_remap); |
446 |
|
} |
447 |
|
logRemap("inital phase %d",-status); |
448 |
|
if (MDImode) { |
449 |
|
// need to trigger execution of parsed _setup.block1 here |
450 |
|
// replicate MDI oword execution code here |
451 |
|
if ((eblock->o_name != 0) || |
452 |
|
(_setup.mdi_interrupt)) { |
453 |
|
|
454 |
|
status = convert_control_functions(eblock, &_setup); |
455 |
|
CHP(status); |
456 |
|
if (_setup.mdi_interrupt) { |
457 |
|
_setup.mdi_interrupt = false; |
458 |
|
MDImode = 1; |
459 |
|
} |
460 |
|
status = INTERP_OK; |
461 |
|
while(MDImode && _setup.call_level) { // we are still in a subroutine |
462 |
|
CHP(read(0)); // reads from current file and calls parse |
463 |
|
status = execute(); // special handling for mdi errors |
464 |
|
if (status == INTERP_EXECUTE_FINISH) |
465 |
|
_setup.mdi_interrupt = true; |
466 |
|
CHP(status); |
467 |
|
} |
468 |
|
_setup.mdi_interrupt = false; |
469 |
|
// at this point the MDI execution of a remapped block is complete. |
470 |
|
logRemap("MDI remap execution complete status=%s\n",interp_status(status)); |
471 |
|
write_g_codes(eblock, &_setup); |
472 |
|
write_m_codes(eblock, &_setup); |
473 |
|
write_settings(&_setup); |
474 |
|
return INTERP_OK; |
475 |
|
} |
476 |
|
} else { |
477 |
|
// this should get the osub going |
478 |
|
status = execute(0); |
479 |
|
CHP(status); |
480 |
|
// when this is done, blocks[0] will be executed as per standard case |
481 |
|
// on endsub/return and g_codes/m_codes/settings recorded there. |
482 |
|
} |
483 |
|
if ((status != INTERP_OK) && |
484 |
|
(status != INTERP_EXECUTE_FINISH) && (status != INTERP_EXIT)) |
485 |
|
ERP(status); |
486 |
|
} else { |
487 |
|
CHP(status); |
488 |
|
} |
489 |
|
} else { |
490 |
|
// standard case: unremapped block execution |
491 |
4510 |
status = execute_block(eblock, &_setup); |
492 |
|
|
493 |
4510 |
write_g_codes(eblock, &_setup); |
494 |
4510 |
write_m_codes(eblock, &_setup); |
495 |
4510 |
write_settings(&_setup); |
496 |
|
|
497 |
4551 |
if ((status == INTERP_EXIT) && |
498 |
41 |
(_setup.remap_level > 0) && |
499 |
|
(_setup.call_level > 0)) { |
500 |
|
// an M2 was encountered while executing a handler. |
501 |
|
logRemap("standard case status=%s remap_level=%d call_level=%d blocktext='%s' MDImode=%d", |
502 |
|
interp_status(status),_setup.remap_level,_setup.call_level, _setup.blocktext,MDImode); |
503 |
|
logRemap("_setup.filename = %s, fn[0]=%s, fn[1]=%s", |
504 |
|
_setup.filename, |
505 |
|
_setup.sub_context[0].filename, |
506 |
|
_setup.sub_context[1].filename); |
507 |
|
} |
508 |
|
} |
509 |
9020 |
if ((status != INTERP_OK) && |
510 |
4562 |
(status != INTERP_EXECUTE_FINISH) && (status != INTERP_EXIT)) |
511 |
11 |
ERP(status); |
512 |
|
} else /* blank line is OK */ |
513 |
|
status = INTERP_OK; |
514 |
5460 |
return status; |
515 |
|
} |
516 |
|
|
517 |
11148 |
int Interp::execute(const char *command) |
518 |
|
{ |
519 |
|
int status; |
520 |
11148 |
if ((status = _execute(command)) > INTERP_MIN_ERROR) { |
521 |
13 |
unwind_call(status, __FILE__,__LINE__,__FUNCTION__); |
522 |
|
} |
523 |
11148 |
return status; |
524 |
|
} |
525 |
|
|
526 |
11136 |
int Interp::execute() |
527 |
|
{ |
528 |
11136 |
return Interp::execute(0); |
529 |
|
} |
530 |
|
|
531 |
3 |
int Interp::execute(const char *command, int line_number) |
532 |
|
{ |
533 |
|
int status; |
534 |
|
|
535 |
3 |
if(command && line_number) |
536 |
|
_setup.sequence_number = line_number; |
537 |
3 |
status = Interp::execute(command); |
538 |
3 |
if (status > INTERP_MIN_ERROR) { |
539 |
|
unwind_call(status, __FILE__,__LINE__,__FUNCTION__); |
540 |
|
logDebug("<-- execute(): error returned, clearing remap and call stack"); |
541 |
|
} |
542 |
3 |
if ((_setup.call_level == 0) && |
543 |
|
(status == INTERP_EXECUTE_FINISH) && |
544 |
|
(_setup.mdi_interrupt)) { |
545 |
|
logDebug(" execute() clearing mdi_interrupt"); |
546 |
|
_setup.mdi_interrupt = false; // seems to work ok! FIXME mah is this needed? |
547 |
|
} |
548 |
3 |
return status; |
549 |
|
} |
550 |
|
|
551 |
|
// when a remapping sub finishes, the oword return/endsub handling code |
552 |
|
// calls back in here to continue block execution |
553 |
|
int Interp::remap_finished(int phase) |
554 |
|
{ |
555 |
|
int next_remap,status; |
556 |
|
block_pointer cblock = &CONTROLLING_BLOCK(_setup); |
557 |
|
|
558 |
|
logRemap("remap_finished phase=%d remap_level=%d call_level=%d filename=%s", |
559 |
|
phase, _setup.remap_level,_setup.call_level,_setup.filename); |
560 |
|
|
561 |
|
// the controlling block had a remapped item, which just finished and the |
562 |
|
// oword return/endsub code called in here. |
563 |
|
if (phase < 0) { |
564 |
|
// paranoia. |
565 |
|
if (cblock->remappings.find(-phase) == cblock->remappings.end()) { |
566 |
|
ERS("remap_finished: got %d - not in cblock.remappings!",phase); |
567 |
|
} |
568 |
|
// done with this phase. |
569 |
|
cblock->remappings.erase(-phase); |
570 |
|
// check the controlling block for the next remapped item |
571 |
|
std::set<int>::iterator it = cblock->remappings.begin(); |
572 |
|
|
573 |
|
if (it != cblock->remappings.end()) { |
574 |
|
next_remap = *it; |
575 |
|
cblock->phase = next_remap; |
576 |
|
|
577 |
|
logRemap("starting phase %d (remap_level=%d call_level=%d)",next_remap,_setup.remap_level,_setup.call_level); |
578 |
|
|
579 |
|
// this will execute up to the next remap, and return |
580 |
|
// after parsing the handler with read() |
581 |
|
// so blocks[0] is armed (meaning: a osub call is parsed, but not executed yet) |
582 |
|
status = execute_block(cblock, &_setup); |
583 |
|
logRemap("phase %d started, execute_block() returns %d", next_remap, status); |
584 |
|
|
585 |
|
if (status < 0) { |
586 |
|
// a remap was parsed, get the block going |
587 |
|
return execute(0); |
588 |
|
} else |
589 |
|
return status; |
590 |
|
} else { |
591 |
|
if (cblock->remappings.size()) { |
592 |
|
ERS("BUG - remappings not empty"); |
593 |
|
} |
594 |
|
// execution of controlling block finished executing a remap, and it contains no more |
595 |
|
// remapped items. Execute any leftover items. |
596 |
|
logRemap("no more remaps in controlling_block found (remap_level=%d call_level=%d), remappings size=%zd, dropping", |
597 |
|
_setup.remap_level,_setup.call_level,cblock->remappings.size()); |
598 |
|
|
599 |
|
status = execute_block(cblock, &_setup); |
600 |
|
|
601 |
|
if ((status < 0) || (status > INTERP_MIN_ERROR)) { |
602 |
|
// status < 0 is a bug; might happen if find_remappings() failed to indicate the next remap |
603 |
|
logRemap("executing block leftover items: %s status=%s remap_level=%d call_level=%d (failing)", |
604 |
|
status < 0 ? "BUG":"ERROR", interp_status(status),_setup.remap_level,_setup.call_level); |
605 |
|
if (status < 0) |
606 |
|
ERS("BUG - check find_remappings()!! status=%d nesting=%d",status,_setup.remap_level); |
607 |
|
} else { |
608 |
|
// we're done with this remapped block. |
609 |
|
// execute_block may return INTERP_EXECUTE_FINISH if a probe, input or toolchange |
610 |
|
// command was executed. |
611 |
|
// not sure what INTERP_ENDFILE & INTERP_EXIT really mean here. |
612 |
|
// if ((status == INTERP_OK) || (status == INTERP_ENDFILE) || (status == INTERP_EXIT) || (status == INTERP_EXECUTE_FINISH)) { |
613 |
|
// leftover items finished. Drop a remapping level. |
614 |
|
|
615 |
|
if (status != INTERP_EXIT) // Don't drop remap lev. at prog exit |
616 |
|
CHP(leave_remap()); |
617 |
|
logRemap("executing block leftover items complete, status=%s remap_level=%d call_level=%d tc=%d probe=%d input=%d mdi_interrupt=%d line=%d backtoline=%d", |
618 |
|
interp_status(status),_setup.remap_level,_setup.call_level,_setup.toolchange_flag, |
619 |
|
_setup.probe_flag,_setup.input_flag,_setup.mdi_interrupt,_setup.sequence_number, |
620 |
|
cblock->line_number); |
621 |
|
} |
622 |
|
} |
623 |
|
return status; |
624 |
|
} else { |
625 |
|
// "should not happen" |
626 |
|
ERS("BUG: remap_finished(): phase=%d nesting=%d", |
627 |
|
phase, _setup.remap_level); |
628 |
|
} |
629 |
|
return INTERP_OK; |
630 |
|
} |
631 |
|
|
632 |
|
|
633 |
|
// examine a block for an active items which are remapped |
634 |
|
// insert all remapped item phases into block.remapping set |
635 |
|
// return number of remaps found |
636 |
6551 |
int Interp::find_remappings(block_pointer block, setup_pointer settings) |
637 |
|
{ |
638 |
6551 |
if (block->f_flag && remapping("F")) { |
639 |
|
if (remap_in_progress("F")) |
640 |
|
CONTROLLING_BLOCK(*settings).builtin_used = true; |
641 |
|
else |
642 |
|
block->remappings.insert(STEP_SET_FEED_RATE); |
643 |
|
} |
644 |
6551 |
if (block->s_flag && remapping("S")) { |
645 |
|
if (remap_in_progress("S")) |
646 |
|
CONTROLLING_BLOCK(*settings).builtin_used = true; |
647 |
|
else |
648 |
|
block->remappings.insert(STEP_SET_SPINDLE_SPEED); |
649 |
|
} |
650 |
6551 |
if (block->t_flag && remapping("T")) { |
651 |
|
if (remap_in_progress("T")) |
652 |
|
CONTROLLING_BLOCK(*settings).builtin_used = true; |
653 |
|
else |
654 |
|
block->remappings.insert(STEP_PREPARE); |
655 |
|
} |
656 |
|
// User defined M-Codes in group 5 |
657 |
6551 |
if (IS_USER_MCODE(block,settings,5)) |
658 |
|
block->remappings.insert(STEP_M_5); |
659 |
|
|
660 |
|
// User defined M-Codes in group 6 (including M6, M61) |
661 |
|
// call the remap procedure if it the code in that group is remapped unless: |
662 |
|
// it's an M6 or M61 and a remap is in progress |
663 |
|
// (recursion case) |
664 |
13102 |
if (IS_USER_MCODE(block,settings,6) && |
665 |
|
!(((block->m_modes[6] == 6) && remap_in_progress("M6")) || |
666 |
|
((block->m_modes[6] == 61) && remap_in_progress("M61")))) { |
667 |
|
block->remappings.insert(STEP_M_6); // then call the remap procedure |
668 |
|
} // else we get the builtin behaviour |
669 |
|
|
670 |
|
// User defined M-Codes in group 7 |
671 |
6551 |
if (IS_USER_MCODE(block,settings,7)) |
672 |
|
block->remappings.insert(STEP_M_7); |
673 |
|
|
674 |
|
// User defined M-Codes in group 8 |
675 |
6551 |
if (IS_USER_MCODE(block,settings,8)) |
676 |
|
block->remappings.insert(STEP_M_8); |
677 |
|
|
678 |
|
// User defined M-Codes in group 9 |
679 |
6551 |
if (IS_USER_MCODE(block,settings,9)) |
680 |
|
block->remappings.insert(STEP_M_9); |
681 |
|
|
682 |
|
// User defined M-Codes in group 10 |
683 |
6551 |
if (IS_USER_MCODE(block,settings,10)) |
684 |
|
block->remappings.insert(STEP_M_10); |
685 |
|
|
686 |
|
// User-defined motion codes (G0 to G3, G33, G73, G76, G80 to G89) |
687 |
|
// as modified (possibly) by G53. |
688 |
6551 |
int mode = block->g_modes[GM_MOTION]; |
689 |
6551 |
if ((mode != -1) && IS_USER_GCODE(mode)) |
690 |
|
block->remappings.insert(STEP_MOTION); |
691 |
|
|
692 |
|
// this makes it possible to call remapped codes like cycles: |
693 |
|
// G84.2 x1 y2 |
694 |
|
// x3 |
695 |
|
// will execute 'G84.2 x1 y2', then 'G84.2 x3 y2' |
696 |
|
// provided the remap function explicitly sets motion_mode like so: |
697 |
|
|
698 |
|
// def g842(self,**words): |
699 |
|
// .... |
700 |
|
// self.motion_mode = 842 |
701 |
|
// return INTERP_OK |
702 |
|
|
703 |
6551 |
mode = block->motion_to_be; |
704 |
6551 |
if ((mode != -1) && IS_USER_GCODE(mode)) { |
705 |
|
block->remappings.insert(STEP_MOTION); |
706 |
|
} |
707 |
|
|
708 |
|
// User defined M-Codes in group 4 (stopping) |
709 |
6551 |
if (IS_USER_MCODE(block,settings,4)) { |
710 |
|
|
711 |
|
if (remap_in_progress("M0") || |
712 |
|
remap_in_progress("M1") || |
713 |
|
remap_in_progress("M60")) { // detect recursion case |
714 |
|
|
715 |
|
// these require real work. |
716 |
|
// remap_in_progress("M2") || |
717 |
|
// remap_in_progress("M60") |
718 |
|
CONTROLLING_BLOCK(*settings).builtin_used = true; |
719 |
|
} else { |
720 |
|
block->remappings.insert(STEP_MGROUP4); |
721 |
|
} |
722 |
|
} |
723 |
13102 |
return block->remappings.size(); |
724 |
|
} |
725 |
|
|
726 |
|
/***********************************************************************/ |
727 |
|
|
728 |
|
/*! Interp::exit |
729 |
|
|
730 |
|
Returned Value: int (INTERP_OK) |
731 |
|
|
732 |
|
Side Effects: See below |
733 |
|
|
734 |
|
Called By: external programs |
735 |
|
|
736 |
|
The system parameters are saved to a file and some parts of the world |
737 |
|
model are reset. If GET_EXTERNAL_PARAMETER_FILE_NAME provides a |
738 |
|
non-empty file name, that name is used for the file that is |
739 |
|
written. Otherwise, the default parameter file name is used. |
740 |
|
|
741 |
|
*/ |
742 |
|
|
743 |
67 |
int Interp::exit() |
744 |
|
{ |
745 |
|
char file_name[LINELEN]; |
746 |
|
|
747 |
67 |
GET_EXTERNAL_PARAMETER_FILE_NAME(file_name, (LINELEN - 1)); |
748 |
67 |
save_parameters(((file_name[0] == |
749 |
|
0) ? |
750 |
|
RS274NGC_PARAMETER_FILE_NAME_DEFAULT : |
751 |
134 |
file_name), _setup.parameters); |
752 |
67 |
reset(); |
753 |
|
|
754 |
|
// interpreter shutdown Python hook |
755 |
67 |
if (python_plugin->is_callable(NULL, DELETE_FUNC)) { |
756 |
|
|
757 |
|
bp::object retval, tupleargs, kwargs; |
758 |
|
bp::list plist; |
759 |
|
|
760 |
|
plist.append(*_setup.pythis); // self |
761 |
|
tupleargs = bp::tuple(plist); |
762 |
|
kwargs = bp::dict(); |
763 |
|
|
764 |
|
python_plugin->call(NULL, DELETE_FUNC, tupleargs, kwargs, retval); |
765 |
|
if (python_plugin->plugin_status() == PLUGIN_EXCEPTION) { |
766 |
|
ERM("pycall(%s):\n%s", INIT_FUNC, |
767 |
|
python_plugin->last_exception().c_str()); |
768 |
|
// this likely wont make it to the UI's any more so bark on stderr |
769 |
|
fprintf(stderr, "%s\n",savedError); |
770 |
|
} |
771 |
|
} |
772 |
|
|
773 |
67 |
return INTERP_OK; |
774 |
|
} |
775 |
|
|
776 |
|
void Interp::set_loglevel(int level) { _setup.loggingLevel = level; } |
777 |
|
|
778 |
|
|
779 |
|
/***********************************************************************/ |
780 |
|
|
781 |
|
/*! rs274_ngc_init |
782 |
|
|
783 |
|
Returned Value: int |
784 |
|
If any of the following errors occur, this returns the error code shown. |
785 |
|
Otherwise, this returns INTERP_OK. |
786 |
|
1. Interp::restore_parameters returns an error code. |
787 |
|
|
788 |
|
Side Effects: |
789 |
|
Many values in the _setup structure are reset. |
790 |
|
A USE_LENGTH_UNITS canonical command call is made. |
791 |
|
A SET_FEED_REFERENCE canonical command call is made. |
792 |
|
A SET_ORIGIN_OFFSETS canonical command call is made. |
793 |
|
An INIT_CANON call is made. |
794 |
|
|
795 |
|
Called By: external programs |
796 |
|
|
797 |
|
Currently we are running only in CANON_XYZ feed_reference mode. There |
798 |
|
is no command regarding feed_reference in the rs274 language (we |
799 |
|
should try to get one added). The initialization routine, therefore, |
800 |
|
always calls SET_FEED_REFERENCE(CANON_XYZ). |
801 |
|
|
802 |
|
*/ |
803 |
|
|
804 |
68 |
int Interp::init() |
805 |
|
{ |
806 |
|
int k; // starting index in parameters of origin offsets |
807 |
|
char filename[LINELEN]; |
808 |
|
double *pars; // short name for _setup.parameters |
809 |
|
char *iniFileName; |
810 |
|
IniFile::ErrorCode r; |
811 |
|
|
812 |
68 |
INIT_CANON(); |
813 |
|
|
814 |
68 |
iniFileName = getenv("INI_FILE_NAME"); |
815 |
|
|
816 |
|
// the default log file |
817 |
68 |
_setup.loggingLevel = 0; |
818 |
68 |
_setup.tool_change_at_g30 = 0; |
819 |
68 |
_setup.tool_change_quill_up = 0; |
820 |
68 |
_setup.tool_change_with_spindle_on = 0; |
821 |
68 |
_setup.a_axis_wrapped = 0; |
822 |
68 |
_setup.b_axis_wrapped = 0; |
823 |
68 |
_setup.c_axis_wrapped = 0; |
824 |
68 |
_setup.random_toolchanger = 0; |
825 |
68 |
_setup.a_indexer_jnum = -1; // -1 means not used |
826 |
68 |
_setup.b_indexer_jnum = -1; // -1 means not used |
827 |
68 |
_setup.c_indexer_jnum = -1; // -1 means not used |
828 |
68 |
_setup.return_value = 0; |
829 |
68 |
_setup.value_returned = 0; |
830 |
68 |
_setup.remap_level = 0; // remapped blocks stack index |
831 |
68 |
_setup.call_state = CS_NORMAL; |
832 |
|
|
833 |
|
// default arc radius tolerances |
834 |
|
// we'll try to override these from the ini file below |
835 |
68 |
_setup.center_arc_radius_tolerance_inch = CENTER_ARC_RADIUS_TOLERANCE_INCH; |
836 |
68 |
_setup.center_arc_radius_tolerance_mm = CENTER_ARC_RADIUS_TOLERANCE_MM; |
837 |
|
|
838 |
68 |
if(iniFileName != NULL) { |
839 |
|
|
840 |
13 |
IniFile inifile; |
841 |
13 |
if (inifile.Open(iniFileName) == false) { |
842 |
|
fprintf(stderr,"Unable to open inifile:%s:\n", iniFileName); |
843 |
|
} else { |
844 |
|
|
845 |
|
const char *inistring; |
846 |
|
|
847 |
13 |
inifile.Find(&_setup.tool_change_at_g30, "TOOL_CHANGE_AT_G30", "EMCIO"); |
848 |
13 |
inifile.Find(&_setup.tool_change_quill_up, "TOOL_CHANGE_QUILL_UP", "EMCIO"); |
849 |
13 |
inifile.Find(&_setup.tool_change_with_spindle_on, "TOOL_CHANGE_WITH_SPINDLE_ON", "EMCIO"); |
850 |
13 |
inifile.Find(&_setup.a_axis_wrapped, "WRAPPED_ROTARY", "AXIS_A"); |
851 |
13 |
inifile.Find(&_setup.b_axis_wrapped, "WRAPPED_ROTARY", "AXIS_B"); |
852 |
13 |
inifile.Find(&_setup.c_axis_wrapped, "WRAPPED_ROTARY", "AXIS_C"); |
853 |
13 |
inifile.Find(&_setup.random_toolchanger, "RANDOM_TOOLCHANGER", "EMCIO"); |
854 |
13 |
inifile.Find(&_setup.feature_set, "FEATURES", "RS274NGC"); |
855 |
|
|
856 |
13 |
if (NULL != (inistring =inifile.Find("LOCKING_INDEXER_JOINT", "AXIS_A"))) { |
857 |
|
_setup.a_indexer_jnum = atol(inistring); |
858 |
|
} |
859 |
13 |
if (NULL != (inistring =inifile.Find("LOCKING_INDEXER_JOINT", "AXIS_B"))) { |
860 |
|
_setup.b_indexer_jnum = atol(inistring); |
861 |
|
} |
862 |
13 |
if (NULL != (inistring =inifile.Find("LOCKING_INDEXER_JOINT", "AXIS_C"))) { |
863 |
|
_setup.c_indexer_jnum = atol(inistring); |
864 |
|
} |
865 |
13 |
inifile.Find(&_setup.orient_offset, "ORIENT_OFFSET", "RS274NGC"); |
866 |
|
|
867 |
13 |
inifile.Find(&_setup.debugmask, "DEBUG", "EMC"); |
868 |
|
|
869 |
13 |
_setup.debugmask |= EMC_DEBUG_UNCONDITIONAL; |
870 |
|
|
871 |
13 |
if(NULL != (inistring = inifile.Find("LOG_LEVEL", "RS274NGC"))) |
872 |
|
{ |
873 |
3 |
_setup.loggingLevel = atol(inistring); |
874 |
|
} |
875 |
|
|
876 |
|
// default the log_file to stderr. |
877 |
13 |
if(NULL != (inistring = inifile.Find("LOG_FILE", "RS274NGC"))) |
878 |
|
{ |
879 |
|
if ((log_file = fopen(inistring, "a")) == NULL) { |
880 |
|
log_file = stderr; |
881 |
|
logDebug( "(%d): Unable to open log file:%s, using stderr", |
882 |
|
getpid(), inistring); |
883 |
|
} |
884 |
|
} else { |
885 |
13 |
log_file = stderr; |
886 |
|
} |
887 |
|
|
888 |
13 |
_setup.use_lazy_close = 1; |
889 |
|
|
890 |
13 |
_setup.wizard_root[0] = 0; |
891 |
13 |
if(NULL != (inistring = inifile.Find("WIZARD_ROOT", "WIZARD"))) |
892 |
|
{ |
893 |
|
logDebug("[WIZARD]WIZARD_ROOT:%s", inistring); |
894 |
|
if (realpath(inistring, _setup.wizard_root) == NULL) { |
895 |
|
//realpath didn't find the file |
896 |
|
logDebug("realpath failed to find wizard_root:%s:", inistring); |
897 |
|
} |
898 |
|
} |
899 |
13 |
logDebug("_setup.wizard_root:%s:", _setup.wizard_root); |
900 |
|
|
901 |
13 |
_setup.program_prefix[0] = 0; |
902 |
13 |
if(NULL != (inistring = inifile.Find("PROGRAM_PREFIX", "DISPLAY"))) |
903 |
|
{ |
904 |
|
// found it |
905 |
|
char expandinistring[LINELEN]; |
906 |
|
if (inifile.TildeExpansion(inistring,expandinistring,sizeof(expandinistring))) { |
907 |
|
logDebug("TildeExpansion failed for: %s",inistring); |
908 |
|
} |
909 |
|
if (realpath(expandinistring, _setup.program_prefix) == NULL){ |
910 |
|
//realpath didn't find the file |
911 |
|
logDebug("realpath failed to find program_prefix:%s:", inistring); |
912 |
|
} |
913 |
|
logDebug("program prefix:%s: prefix:%s:", |
914 |
|
inistring, _setup.program_prefix); |
915 |
|
} |
916 |
|
else |
917 |
|
{ |
918 |
13 |
logDebug("PROGRAM_PREFIX not found"); |
919 |
|
} |
920 |
13 |
logDebug("_setup.program_prefix:%s:", _setup.program_prefix); |
921 |
|
|
922 |
|
|
923 |
13 |
if(NULL != (inistring = inifile.Find("SUBROUTINE_PATH", "RS274NGC"))) |
924 |
|
{ |
925 |
|
// found it |
926 |
|
int dct; |
927 |
|
char* nextdir; |
928 |
|
char tmpdirs[PATH_MAX+1]; |
929 |
|
|
930 |
60 |
for (dct=0; dct < MAX_SUB_DIRS; dct++) { |
931 |
60 |
_setup.subroutines[dct] = NULL; |
932 |
|
} |
933 |
|
|
934 |
6 |
strcpy(tmpdirs,inistring); |
935 |
6 |
nextdir = strtok(tmpdirs,":"); // first token |
936 |
6 |
dct = 0; |
937 |
|
while (1) { |
938 |
|
char tmp_path[PATH_MAX]; |
939 |
|
char expandnextdir[LINELEN]; |
940 |
6 |
if (inifile.TildeExpansion(nextdir,expandnextdir,sizeof(expandnextdir))) { |
941 |
|
logDebug("TildeExpansion failed for: %s",nextdir); |
942 |
|
} |
943 |
6 |
if (realpath(expandnextdir, tmp_path) == NULL){ |
944 |
|
//realpath didn't find the directory |
945 |
|
logDebug("realpath failed to find subroutines[%d]:%s:",dct,nextdir); |
946 |
|
_setup.subroutines[dct] = NULL; |
947 |
|
} else { |
948 |
6 |
_setup.subroutines[dct] = strstore(tmp_path); |
949 |
6 |
logDebug("program prefix[%d]:%s",dct,_setup.subroutines[dct]); |
950 |
6 |
dct++; |
951 |
|
} |
952 |
6 |
if (dct >= MAX_SUB_DIRS) { |
953 |
|
logDebug("too many entries in SUBROUTINE_PATH, max=%d", MAX_SUB_DIRS); |
954 |
6 |
break; |
955 |
|
} |
956 |
6 |
nextdir = strtok(NULL,":"); |
957 |
6 |
if (nextdir == NULL) break; // no more tokens |
958 |
|
} |
959 |
|
} |
960 |
|
else |
961 |
|
{ |
962 |
7 |
logDebug("SUBROUTINE_PATH not found"); |
963 |
|
} |
964 |
|
// subroutine to execute on aborts - for instance to retract |
965 |
|
// toolchange HAL pins |
966 |
13 |
if (NULL != (inistring = inifile.Find("ON_ABORT_COMMAND", "RS274NGC"))) { |
967 |
|
_setup.on_abort_command = strstore(inistring); |
968 |
|
logDebug("_setup.on_abort_command=%s", _setup.on_abort_command); |
969 |
|
} else { |
970 |
13 |
_setup.on_abort_command = NULL; |
971 |
|
} |
972 |
|
|
973 |
|
// initialize the Python plugin singleton |
974 |
13 |
if (NULL != (inistring = inifile.Find("TOPLEVEL", "PYTHON"))) { |
975 |
3 |
int status = python_plugin->configure(iniFileName,"PYTHON"); |
976 |
3 |
if (status != PLUGIN_OK) { |
977 |
|
Error("Python plugin configure() failed, status = %d", status); |
978 |
|
} |
979 |
|
} |
980 |
|
|
981 |
13 |
int n = 1; |
982 |
13 |
int lineno = -1; |
983 |
13 |
_setup.g_remapped.clear(); |
984 |
13 |
_setup.m_remapped.clear(); |
985 |
13 |
_setup.remaps.clear(); |
986 |
13 |
while (NULL != (inistring = inifile.Find("REMAP", "RS274NGC", |
987 |
|
n, &lineno))) { |
988 |
|
|
989 |
|
CHP(parse_remap( inistring, lineno)); |
990 |
|
n++; |
991 |
|
} |
992 |
|
|
993 |
|
// if exist and within bounds, apply ini file arc tolerances |
994 |
|
// limiting figures are defined in interp_internal.hh |
995 |
|
|
996 |
|
r = inifile.Find( |
997 |
|
&_setup.center_arc_radius_tolerance_inch, |
998 |
|
MIN_CENTER_ARC_RADIUS_TOLERANCE_INCH, |
999 |
|
CENTER_ARC_RADIUS_TOLERANCE_INCH, |
1000 |
|
"CENTER_ARC_RADIUS_TOLERANCE_INCH", |
1001 |
|
"RS274NGC" |
1002 |
13 |
); |
1003 |
13 |
if ((r != IniFile::ERR_NONE) && (r != IniFile::ERR_TAG_NOT_FOUND)) { |
1004 |
|
Error("invalid [RS274NGC]CENTER_ARC_RADIUS_TOLERANCE_INCH in ini file\n"); |
1005 |
|
} |
1006 |
|
|
1007 |
|
r = inifile.Find( |
1008 |
|
&_setup.center_arc_radius_tolerance_mm, |
1009 |
|
MIN_CENTER_ARC_RADIUS_TOLERANCE_MM, |
1010 |
|
CENTER_ARC_RADIUS_TOLERANCE_MM, |
1011 |
|
"CENTER_ARC_RADIUS_TOLERANCE_MM", |
1012 |
|
"RS274NGC" |
1013 |
13 |
); |
1014 |
13 |
if ((r != IniFile::ERR_NONE) && (r != IniFile::ERR_TAG_NOT_FOUND)) { |
1015 |
|
Error("invalid [RS274NGC]CENTER_ARC_RADIUS_TOLERANCE_MM in ini file\n"); |
1016 |
|
} |
1017 |
|
|
1018 |
|
// ini file g52/g92 offset persistence default setting |
1019 |
|
inifile.Find(&_setup.disable_g92_persistence, |
1020 |
|
"DISABLE_G92_PERSISTENCE", |
1021 |
13 |
"RS274NGC"); |
1022 |
|
|
1023 |
|
// close it |
1024 |
13 |
inifile.Close(); |
1025 |
|
} |
1026 |
|
} |
1027 |
|
|
1028 |
68 |
_setup.length_units = GET_EXTERNAL_LENGTH_UNIT_TYPE(); |
1029 |
68 |
USE_LENGTH_UNITS(_setup.length_units); |
1030 |
68 |
GET_EXTERNAL_PARAMETER_FILE_NAME(filename, LINELEN); |
1031 |
68 |
if (filename[0] == 0) |
1032 |
|
strcpy(filename, RS274NGC_PARAMETER_FILE_NAME_DEFAULT); |
1033 |
68 |
CHP(restore_parameters(filename)); |
1034 |
68 |
pars = _setup.parameters; |
1035 |
68 |
_setup.origin_index = (int) (pars[5220] + 0.0001); |
1036 |
68 |
if(_setup.origin_index < 1 || _setup.origin_index > 9) { |
1037 |
66 |
_setup.origin_index = 1; |
1038 |
66 |
pars[5220] = 1.0; |
1039 |
|
} |
1040 |
|
|
1041 |
68 |
k = (5200 + (_setup.origin_index * 20)); |
1042 |
68 |
_setup.origin_offset_x = USER_TO_PROGRAM_LEN(pars[k + 1]); |
1043 |
68 |
_setup.origin_offset_y = USER_TO_PROGRAM_LEN(pars[k + 2]); |
1044 |
68 |
_setup.origin_offset_z = USER_TO_PROGRAM_LEN(pars[k + 3]); |
1045 |
68 |
_setup.AA_origin_offset = USER_TO_PROGRAM_ANG(pars[k + 4]); |
1046 |
68 |
_setup.BB_origin_offset = USER_TO_PROGRAM_ANG(pars[k + 5]); |
1047 |
68 |
_setup.CC_origin_offset = USER_TO_PROGRAM_ANG(pars[k + 6]); |
1048 |
68 |
_setup.u_origin_offset = USER_TO_PROGRAM_LEN(pars[k + 7]); |
1049 |
68 |
_setup.v_origin_offset = USER_TO_PROGRAM_LEN(pars[k + 8]); |
1050 |
68 |
_setup.w_origin_offset = USER_TO_PROGRAM_LEN(pars[k + 9]); |
1051 |
|
|
1052 |
|
SET_G5X_OFFSET(_setup.origin_index, |
1053 |
|
_setup.origin_offset_x , |
1054 |
|
_setup.origin_offset_y , |
1055 |
|
_setup.origin_offset_z , |
1056 |
|
_setup.AA_origin_offset, |
1057 |
|
_setup.BB_origin_offset, |
1058 |
|
_setup.CC_origin_offset, |
1059 |
|
_setup.u_origin_offset , |
1060 |
|
_setup.v_origin_offset , |
1061 |
68 |
_setup.w_origin_offset); |
1062 |
|
|
1063 |
|
// Restore G92 offset if DISABLE_G92_PERSISTENCE not set in .ini file. |
1064 |
|
// This can't be done with the static _required_parameters[], where |
1065 |
|
// the .vars file contents would reflect that setting, so instead |
1066 |
|
// edit the restored parameters here. |
1067 |
68 |
if (_setup.disable_g92_persistence) |
1068 |
|
// Persistence disabled: clear g92 parameters |
1069 |
10 |
for (k = 5210; k < 5220; k++) |
1070 |
10 |
pars[k] = 0; |
1071 |
|
|
1072 |
68 |
if (pars[5210]) { |
1073 |
2 |
_setup.axis_offset_x = USER_TO_PROGRAM_LEN(pars[5211]); |
1074 |
2 |
_setup.axis_offset_y = USER_TO_PROGRAM_LEN(pars[5212]); |
1075 |
2 |
_setup.axis_offset_z = USER_TO_PROGRAM_LEN(pars[5213]); |
1076 |
2 |
_setup.AA_axis_offset = USER_TO_PROGRAM_ANG(pars[5214]); |
1077 |
2 |
_setup.BB_axis_offset = USER_TO_PROGRAM_ANG(pars[5215]); |
1078 |
2 |
_setup.CC_axis_offset = USER_TO_PROGRAM_ANG(pars[5216]); |
1079 |
2 |
_setup.u_axis_offset = USER_TO_PROGRAM_LEN(pars[5217]); |
1080 |
2 |
_setup.v_axis_offset = USER_TO_PROGRAM_LEN(pars[5218]); |
1081 |
2 |
_setup.w_axis_offset = USER_TO_PROGRAM_LEN(pars[5219]); |
1082 |
|
} else { |
1083 |
66 |
_setup.axis_offset_x = 0.0; |
1084 |
66 |
_setup.axis_offset_y = 0.0; |
1085 |
66 |
_setup.axis_offset_z = 0.0; |
1086 |
66 |
_setup.AA_axis_offset = 0.0; |
1087 |
66 |
_setup.BB_axis_offset = 0.0; |
1088 |
66 |
_setup.CC_axis_offset = 0.0; |
1089 |
66 |
_setup.u_axis_offset = 0.0; |
1090 |
66 |
_setup.v_axis_offset = 0.0; |
1091 |
66 |
_setup.w_axis_offset = 0.0; |
1092 |
|
} |
1093 |
|
|
1094 |
|
SET_G92_OFFSET(_setup.axis_offset_x , |
1095 |
|
_setup.axis_offset_y , |
1096 |
|
_setup.axis_offset_z , |
1097 |
|
_setup.AA_axis_offset, |
1098 |
|
_setup.BB_axis_offset, |
1099 |
|
_setup.CC_axis_offset, |
1100 |
|
_setup.u_axis_offset , |
1101 |
|
_setup.v_axis_offset , |
1102 |
68 |
_setup.w_axis_offset); |
1103 |
|
|
1104 |
68 |
_setup.rotation_xy = pars[k+10]; |
1105 |
68 |
SET_XY_ROTATION(pars[k+10]); |
1106 |
68 |
SET_FEED_REFERENCE(CANON_XYZ); |
1107 |
|
//_setup.active_g_codes initialized below |
1108 |
|
//_setup.active_m_codes initialized below |
1109 |
|
//_setup.active_settings initialized below |
1110 |
|
//_setup.block1 does not need initialization |
1111 |
68 |
_setup.blocktext[0] = 0; |
1112 |
|
//_setup.current_slot set in Interp::synch |
1113 |
|
//_setup.current_x set in Interp::synch |
1114 |
|
//_setup.current_y set in Interp::synch |
1115 |
|
//_setup.current_z set in Interp::synch |
1116 |
68 |
_setup.cutter_comp_side = false; |
1117 |
68 |
_setup.arc_not_allowed = false; |
1118 |
68 |
_setup.cycle_il_flag = false; |
1119 |
68 |
_setup.distance_mode = MODE_ABSOLUTE; |
1120 |
68 |
_setup.ijk_distance_mode = MODE_INCREMENTAL; // backwards compatability |
1121 |
68 |
_setup.feed_mode = UNITS_PER_MINUTE; |
1122 |
|
//_setup.feed_override set in Interp::synch |
1123 |
|
//_setup.feed_rate set in Interp::synch |
1124 |
68 |
_setup.filename[0] = 0; |
1125 |
68 |
_setup.file_pointer = NULL; |
1126 |
|
//_setup.flood set in Interp::synch |
1127 |
|
// _setup.tool_offset_index = 1; // unused - removed, mah |
1128 |
|
//_setup.length_units set in Interp::synch |
1129 |
68 |
_setup.line_length = 0; |
1130 |
68 |
_setup.linetext[0] = 0; |
1131 |
|
//_setup.mist set in Interp::synch |
1132 |
68 |
_setup.motion_mode = G_80; |
1133 |
|
//_setup.origin_index set above |
1134 |
|
//_setup.parameters set above |
1135 |
|
//_setup.parameter_occurrence does not need initialization |
1136 |
|
//_setup.parameter_numbers does not need initialization |
1137 |
|
//_setup.parameter_values does not need initialization |
1138 |
|
//_setup.percent_flag does not need initialization |
1139 |
|
//_setup.plane set in Interp::synch |
1140 |
68 |
_setup.probe_flag = false; |
1141 |
68 |
_setup.toolchange_flag = false; |
1142 |
68 |
_setup.input_flag = false; |
1143 |
68 |
_setup.input_index = -1; |
1144 |
68 |
_setup.input_digital = false; |
1145 |
68 |
_setup.program_x = 0.; /* for cutter comp */ |
1146 |
68 |
_setup.program_y = 0.; /* for cutter comp */ |
1147 |
68 |
_setup.program_z = 0.; /* for cutter comp */ |
1148 |
68 |
_setup.cutter_comp_firstmove = true; |
1149 |
|
//_setup.retract_mode does not need initialization |
1150 |
|
//_setup.selected_tool_slot set in Interp::synch |
1151 |
68 |
_setup.sequence_number = 0; /*DOES THIS NEED TO BE AT TOP? */ |
1152 |
|
//_setup.speed set in Interp::synch |
1153 |
68 |
_setup.speed_feed_mode = CANON_INDEPENDENT; |
1154 |
68 |
_setup.spindle_mode = CONSTANT_RPM; |
1155 |
|
//_setup.speed_override set in Interp::synch |
1156 |
|
//_setup.spindle_turning set in Interp::synch |
1157 |
|
//_setup.stack does not need initialization |
1158 |
|
//_setup.stack_index does not need initialization |
1159 |
68 |
ZERO_EMC_POSE(_setup.tool_offset); |
1160 |
|
//_setup.tool_max set in Interp::synch |
1161 |
|
//_setup.tool_table set in Interp::synch |
1162 |
|
//_setup.traverse_rate set in Interp::synch |
1163 |
|
//_setup.adaptive_feed set in Interp::synch |
1164 |
|
//_setup.feed_hold set in Interp::synch |
1165 |
|
|
1166 |
|
// initialization stuff for subroutines and control structures |
1167 |
68 |
_setup.call_level = 0; |
1168 |
68 |
_setup.defining_sub = 0; |
1169 |
68 |
_setup.skipping_o = 0; |
1170 |
68 |
_setup.offset_map.clear(); |
1171 |
|
|
1172 |
68 |
_setup.lathe_diameter_mode = false; |
1173 |
68 |
_setup.parameters[5599] = 1.0; // enable (DEBUG, ) output |
1174 |
|
|
1175 |
68 |
memcpy(_readers, default_readers, sizeof(default_readers)); |
1176 |
|
|
1177 |
68 |
long axis_mask = GET_EXTERNAL_AXIS_MASK(); |
1178 |
68 |
if(!(axis_mask & AXIS_MASK_X)) _readers[(int)'x'] = 0; |
1179 |
68 |
if(!(axis_mask & AXIS_MASK_Y)) _readers[(int)'y'] = 0; |
1180 |
68 |
if(!(axis_mask & AXIS_MASK_Z)) _readers[(int)'z'] = 0; |
1181 |
68 |
if(!(axis_mask & AXIS_MASK_A)) _readers[(int)'a'] = 0; |
1182 |
68 |
if(!(axis_mask & AXIS_MASK_B)) _readers[(int)'b'] = 0; |
1183 |
68 |
if(!(axis_mask & AXIS_MASK_C)) _readers[(int)'c'] = 0; |
1184 |
68 |
if(!(axis_mask & AXIS_MASK_U)) _readers[(int)'u'] = 0; |
1185 |
68 |
if(!(axis_mask & AXIS_MASK_V)) _readers[(int)'v'] = 0; |
1186 |
68 |
if(!(axis_mask & AXIS_MASK_W)) _readers[(int)'w'] = 0; |
1187 |
|
|
1188 |
68 |
synch(); //synch first, then update the interface |
1189 |
|
|
1190 |
68 |
write_g_codes((block_pointer) NULL, &_setup); |
1191 |
68 |
write_m_codes((block_pointer) NULL, &_setup); |
1192 |
68 |
write_settings(&_setup); |
1193 |
|
|
1194 |
68 |
init_tool_parameters(); |
1195 |
|
// Synch rest of settings to external world |
1196 |
|
|
1197 |
|
// call __init__(self) once in toplevel module if defined |
1198 |
|
// once fully set up and sync()ed |
1199 |
68 |
if ((iniFileName != NULL) && _setup.init_once && PYUSABLE ) { |
1200 |
|
|
1201 |
|
// initialize any python global predefined named parameters |
1202 |
|
// walk the namedparams module for callables and add their names as predefs |
1203 |
|
try { |
1204 |
26 |
bp::object npmod = python_plugin->main_namespace[NAMEDPARAMS_MODULE]; |
1205 |
|
bp::dict predef_dict = bp::extract<bp::dict>(npmod.attr("__dict__")); |
1206 |
|
bp::list iterkeys = (bp::list) predef_dict.iterkeys(); |
1207 |
|
for (int i = 0; i < bp::len(iterkeys); i++) { |
1208 |
|
std::string key = bp::extract<std::string>(iterkeys[i]); |
1209 |
|
bp::object value = predef_dict[key]; |
1210 |
|
if (PyCallable_Check(value.ptr())) { |
1211 |
|
CHP(init_python_predef_parameter(key.c_str())); |
1212 |
|
} |
1213 |
|
} |
1214 |
|
} |
1215 |
26 |
catch (bp::error_already_set) { |
1216 |
|
std::string exception_msg; |
1217 |
13 |
bool unexpected = false; |
1218 |
|
// KeyError is ok - this means the namedparams module doesnt exist |
1219 |
13 |
if (!PyErr_ExceptionMatches(PyExc_KeyError)) { |
1220 |
|
// something else, strange |
1221 |
|
exception_msg = handle_pyerror(); |
1222 |
|
unexpected = true; |
1223 |
|
} |
1224 |
13 |
bp::handle_exception(); |
1225 |
13 |
PyErr_Clear(); |
1226 |
26 |
CHKS(unexpected, "exception adding Python predefined named parameter: %s", exception_msg.c_str()); |
1227 |
|
} |
1228 |
|
|
1229 |
13 |
if (python_plugin->is_callable(NULL, INIT_FUNC)) { |
1230 |
|
|
1231 |
1 |
bp::object retval, tupleargs, kwargs; |
1232 |
|
bp::list plist; |
1233 |
|
|
1234 |
1 |
plist.append(*_setup.pythis); // self |
1235 |
2 |
tupleargs = bp::tuple(plist); |
1236 |
1 |
kwargs = bp::dict(); |
1237 |
|
|
1238 |
3 |
python_plugin->call(NULL, INIT_FUNC, tupleargs, kwargs, retval); |
1239 |
1 |
CHKS(python_plugin->plugin_status() == PLUGIN_EXCEPTION, |
1240 |
|
"pycall(%s):\n%s", INIT_FUNC, |
1241 |
|
python_plugin->last_exception().c_str()); |
1242 |
|
} |
1243 |
|
} |
1244 |
68 |
_setup.init_once = 0; |
1245 |
|
|
1246 |
68 |
return INTERP_OK; |
1247 |
|
} |
1248 |
|
|
1249 |
|
/***********************************************************************/ |
1250 |
|
|
1251 |
|
/*! Interp::load_tool_table |
1252 |
|
|
1253 |
|
Returned Value: int |
1254 |
|
If any of the following errors occur, this returns the error code shown. |
1255 |
|
Otherwise, this returns INTERP_OK. |
1256 |
|
1. _setup.tool_max is larger than CANON_TOOL_MAX: NCE_TOOL_MAX_TOO_LARGE |
1257 |
|
|
1258 |
|
Side Effects: |
1259 |
|
_setup.tool_table[] is modified. |
1260 |
|
|
1261 |
|
Called By: |
1262 |
|
Interp::synch |
1263 |
|
external programs |
1264 |
|
|
1265 |
|
This function calls the canonical interface function GET_EXTERNAL_TOOL_TABLE |
1266 |
|
to load the whole tool table into the _setup. |
1267 |
|
|
1268 |
|
The CANON_TOOL_MAX is an upper limit for this software. The |
1269 |
|
_setup.tool_max is intended to be set for a particular machine. |
1270 |
|
|
1271 |
|
*/ |
1272 |
|
|
1273 |
91 |
int Interp::load_tool_table() |
1274 |
|
{ |
1275 |
|
int n; |
1276 |
|
|
1277 |
91 |
CHKS((_setup.pockets_max > CANON_POCKETS_MAX), NCE_POCKET_MAX_TOO_LARGE); |
1278 |
5040 |
for (n = 0; n < _setup.pockets_max; n++) { |
1279 |
5040 |
_setup.tool_table[n] = GET_EXTERNAL_TOOL_TABLE(n); |
1280 |
|
} |
1281 |
56 |
for (; n < CANON_POCKETS_MAX; n++) { |
1282 |
56 |
_setup.tool_table[n].toolno = -1; |
1283 |
56 |
ZERO_EMC_POSE(_setup.tool_table[n].offset); |
1284 |
56 |
_setup.tool_table[n].diameter = 0; |
1285 |
56 |
_setup.tool_table[n].orientation = 0; |
1286 |
56 |
_setup.tool_table[n].frontangle = 0; |
1287 |
56 |
_setup.tool_table[n].backangle = 0; |
1288 |
|
} |
1289 |
91 |
set_tool_parameters(); |
1290 |
91 |
return INTERP_OK; |
1291 |
|
} |
1292 |
|
|
1293 |
|
/***********************************************************************/ |
1294 |
|
|
1295 |
|
/*! Interp::open |
1296 |
|
|
1297 |
|
Returned Value: int |
1298 |
|
If any of the following errors occur, this returns the error code shown. |
1299 |
|
Otherwise it returns INTERP_OK. |
1300 |
|
1. A file is already open: NCE_A_FILE_IS_ALREADY_OPEN |
1301 |
|
2. The name of the file is too long: NCE_FILE_NAME_TOO_LONG |
1302 |
|
3. The file cannot be opened: NCE_UNABLE_TO_OPEN_FILE |
1303 |
|
|
1304 |
|
Side Effects: See below |
1305 |
|
|
1306 |
|
Called By: external programs |
1307 |
|
|
1308 |
|
The file is opened for reading and _setup.file_pointer is set. |
1309 |
|
The file name is copied into _setup.filename. |
1310 |
|
The _setup.sequence_number, is set to zero. |
1311 |
|
Interp::reset() is called, changing several more _setup attributes. |
1312 |
|
|
1313 |
|
The manual [NCMS, page 3] discusses the use of the "%" character at the |
1314 |
|
beginning of a "tape". It is not clear whether it is intended that |
1315 |
|
every NC-code file should begin with that character. |
1316 |
|
|
1317 |
|
In the following, "uses percents" means the first non-blank line |
1318 |
|
of the file must consist of nothing but the percent sign, with optional |
1319 |
|
leading and trailing white space, and there must be a second line |
1320 |
|
of the same sort later on in the file. If a file uses percents, |
1321 |
|
execution stops at the second percent line. Any lines after the |
1322 |
|
second percent line are ignored. |
1323 |
|
|
1324 |
|
In this interpreter (recalling that M2 and M30 always ends execution): |
1325 |
|
1. If execution of a file is ended by M2 or M30 (not necessarily on |
1326 |
|
the last line of the file), then it is optional that the file |
1327 |
|
uses percents. |
1328 |
|
2. If execution of a file is not ended by M2 or M30, then it is |
1329 |
|
required that the file uses percents. |
1330 |
|
|
1331 |
|
If the file being opened uses percents, this function turns on the |
1332 |
|
_setup.percent flag, reads any initial blank lines, and reads the |
1333 |
|
first line with the "%". If not, after reading enough to determine |
1334 |
|
that, this function puts the file pointer back at the beginning of the |
1335 |
|
file. |
1336 |
|
|
1337 |
|
*/ |
1338 |
|
|
1339 |
65 |
int Interp::open(const char *filename) //!< string: the name of the input NC-program file |
1340 |
|
{ |
1341 |
|
char *line; |
1342 |
|
int index; |
1343 |
|
int length; |
1344 |
|
|
1345 |
65 |
logOword("open()"); |
1346 |
65 |
if(_setup.use_lazy_close && _setup.lazy_closing) |
1347 |
|
{ |
1348 |
1 |
_setup.use_lazy_close = 0; // so that close will work |
1349 |
1 |
close(); |
1350 |
1 |
_setup.use_lazy_close = 1; |
1351 |
1 |
_setup.lazy_closing = 0; |
1352 |
|
} |
1353 |
65 |
CHKS((_setup.file_pointer != NULL), NCE_A_FILE_IS_ALREADY_OPEN); |
1354 |
65 |
CHKS((strlen(filename) > (LINELEN - 1)), NCE_FILE_NAME_TOO_LONG); |
1355 |
65 |
_setup.file_pointer = fopen(filename, "r"); |
1356 |
65 |
CHKS((_setup.file_pointer == NULL), NCE_UNABLE_TO_OPEN_FILE, filename); |
1357 |
65 |
line = _setup.linetext; |
1358 |
205 |
for (index = -1; index == -1;) { /* skip blank lines */ |
1359 |
75 |
CHKS((fgets(line, LINELEN, _setup.file_pointer) == |
1360 |
|
NULL), NCE_FILE_ENDED_WITH_NO_PERCENT_SIGN); |
1361 |
75 |
length = strlen(line); |
1362 |
75 |
if (length == (LINELEN - 1)) { // line is too long. need to finish reading the line to recover |
1363 |
|
for (; fgetc(_setup.file_pointer) != '\n';); // could look for EOF |
1364 |
|
ERS(NCE_COMMAND_TOO_LONG); |
1365 |
|
} |
1366 |
75 |
for (index = (length - 1); // index set on last char |
1367 |
142 |
(index >= 0) && (isspace(line[index])); index--); |
1368 |
|
} |
1369 |
65 |
if (line[index] == '%') { |
1370 |
9 |
for (index--; (index >= 0) && (isspace(line[index])); index--); |
1371 |
9 |
if (index == -1) { |
1372 |
3 |
_setup.percent_flag = true; |
1373 |
3 |
_setup.sequence_number = 1; // We have already read the first line |
1374 |
|
// and we are not going back to it. |
1375 |
|
} else { |
1376 |
6 |
fseek(_setup.file_pointer, 0, SEEK_SET); |
1377 |
6 |
_setup.percent_flag = false; |
1378 |
6 |
_setup.sequence_number = 0; // Going back to line 0 |
1379 |
|
} |
1380 |
|
} else { |
1381 |
56 |
fseek(_setup.file_pointer, 0, SEEK_SET); |
1382 |
56 |
_setup.percent_flag = false; |
1383 |
56 |
_setup.sequence_number = 0; // Going back to line 0 |
1384 |
|
} |
1385 |
65 |
strcpy(_setup.filename, filename); |
1386 |
65 |
reset(); |
1387 |
65 |
return INTERP_OK; |
1388 |
|
} |
1389 |
|
|
1390 |
11161 |
int Interp::read_inputs(setup_pointer settings) |
1391 |
|
{ |
1392 |
|
// logDebug("read_inputs probe=%d input=%d toolchange=%d", |
1393 |
|
// settings->probe_flag, settings->toolchange_flag, settings->input_flag); |
1394 |
11161 |
if (settings->probe_flag) { |
1395 |
|
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1396 |
|
NCE_QUEUE_IS_NOT_EMPTY_AFTER_PROBING); |
1397 |
|
set_probe_data(&_setup); |
1398 |
|
settings->probe_flag = false; |
1399 |
|
} |
1400 |
11161 |
if (settings->toolchange_flag) { |
1401 |
9 |
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1402 |
|
_("Queue is not empty after tool change")); |
1403 |
9 |
refresh_actual_position(&_setup); |
1404 |
9 |
load_tool_table(); |
1405 |
9 |
settings->toolchange_flag = false; |
1406 |
|
} |
1407 |
|
// always track toolchanger-fault and toolchanger-reason codes |
1408 |
11161 |
settings->parameters[5600] = GET_EXTERNAL_TC_FAULT(); |
1409 |
11161 |
settings->parameters[5601] = GET_EXTERNAL_TC_REASON(); |
1410 |
|
|
1411 |
11161 |
if (settings->input_flag) { |
1412 |
1 |
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1413 |
|
NCE_QUEUE_IS_NOT_EMPTY_AFTER_INPUT); |
1414 |
1 |
if (settings->input_digital) { // we are checking for a digital input |
1415 |
|
settings->parameters[5399] = |
1416 |
|
GET_EXTERNAL_DIGITAL_INPUT(settings->input_index, |
1417 |
1 |
(settings->parameters[5399] != 0.0)); |
1418 |
|
} else { // checking for analog input |
1419 |
|
settings->parameters[5399] = |
1420 |
|
GET_EXTERNAL_ANALOG_INPUT(settings->input_index, settings->parameters[5399]); |
1421 |
|
} |
1422 |
1 |
settings->input_flag = false; |
1423 |
|
} |
1424 |
|
return INTERP_OK; |
1425 |
|
} |
1426 |
|
|
1427 |
|
/***********************************************************************/ |
1428 |
|
|
1429 |
|
/*! Interp::read |
1430 |
|
|
1431 |
|
Returned Value: int |
1432 |
|
If any of the following errors occur, this returns the error code shown. |
1433 |
|
Otherwise, this returns: |
1434 |
|
a. INTERP_ENDFILE if the only non-white character on the line is %, |
1435 |
|
b. INTERP_EXECUTE_FINISH if the first character of the |
1436 |
|
close_and_downcased line is a slash, and |
1437 |
|
c. INTERP_OK otherwise. |
1438 |
|
1. The command and_setup.file_pointer are both NULL: INTERP_FILE_NOT_OPEN |
1439 |
|
2. The probe_flag is true but the HME command queue is not empty: |
1440 |
|
NCE_QUEUE_IS_NOT_EMPTY_AFTER_PROBING |
1441 |
|
3. If read_text (which gets a line of NC code from file) or parse_line |
1442 |
|
(which parses the line) returns an error code, this returns that code. |
1443 |
|
|
1444 |
|
Side Effects: |
1445 |
|
_setup.sequence_number is incremented. |
1446 |
|
The executing block is filled with data. |
1447 |
|
|
1448 |
|
Called By: external programs |
1449 |
|
|
1450 |
|
This reads a line of NC-code from the command string or, (if the |
1451 |
|
command string is NULL) from the currently open file. The |
1452 |
|
_setup.line_length will be set by read_text. This will be zero if the |
1453 |
|
line is blank or consists of nothing but a slash. If the length is not |
1454 |
|
zero, this parses the line into the _setup.block1. |
1455 |
|
|
1456 |
|
*/ |
1457 |
|
|
1458 |
11161 |
int Interp::_read(const char *command) //!< may be NULL or a string to read |
1459 |
|
{ |
1460 |
|
static char name[] = "Interp::read"; |
1461 |
|
int read_status; |
1462 |
|
|
1463 |
|
// this input reading code is in the wrong place. It should be executed |
1464 |
|
// in sync(), not here. This would make correct parameter values available |
1465 |
|
// without doing a read() (e.g. from Python). |
1466 |
|
// Unfortunately synch() isnt called in preview (gcodemodule) |
1467 |
|
|
1468 |
|
#if 0 |
1469 |
|
if (_setup.probe_flag) { |
1470 |
|
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1471 |
|
NCE_QUEUE_IS_NOT_EMPTY_AFTER_PROBING); |
1472 |
|
set_probe_data(&_setup); |
1473 |
|
_setup.probe_flag = false; |
1474 |
|
} |
1475 |
|
if (_setup.toolchange_flag) { |
1476 |
|
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1477 |
|
_("Queue is not empty after tool change")); |
1478 |
|
refresh_actual_position(&_setup); |
1479 |
|
load_tool_table(); |
1480 |
|
_setup.toolchange_flag = false; |
1481 |
|
} |
1482 |
|
// always track toolchanger-fault and toolchanger-reason codes |
1483 |
|
_setup.parameters[5600] = GET_EXTERNAL_TC_FAULT(); |
1484 |
|
_setup.parameters[5601] = GET_EXTERNAL_TC_REASON(); |
1485 |
|
|
1486 |
|
if (_setup.input_flag) { |
1487 |
|
CHKS((GET_EXTERNAL_QUEUE_EMPTY() == 0), |
1488 |
|
NCE_QUEUE_IS_NOT_EMPTY_AFTER_INPUT); |
1489 |
|
if (_setup.input_digital) { // we are checking for a digital input |
1490 |
|
_setup.parameters[5399] = |
1491 |
|
GET_EXTERNAL_DIGITAL_INPUT(_setup.input_index, |
1492 |
|
(_setup.parameters[5399] != 0.0)); |
1493 |
|
} else { // checking for analog input |
1494 |
|
_setup.parameters[5399] = |
1495 |
|
GET_EXTERNAL_ANALOG_INPUT(_setup.input_index, _setup.parameters[5399]); |
1496 |
|
} |
1497 |
|
_setup.input_flag = false; |
1498 |
|
} |
1499 |
|
#endif |
1500 |
|
|
1501 |
|
// Support for restartable Python handlers during Auto mode |
1502 |
|
// Conceptually a O_call or O_endsub/O_return block might need to be executed several |
1503 |
|
// times in a row until it finally returns INTERP_OK, the reason being that all Python |
1504 |
|
// procedures might 'yield INTERP_EXECUTE_FINISH' or execute a queue buster an arbitrary number |
1505 |
|
// of times. So they need to be called again post-sync and post-read-input possibly several times. |
1506 |
|
// |
1507 |
|
// the task readahead logic assumes a block execution may result in a single INTERP_EXECUTE_FINISH |
1508 |
|
// and readahead is started therafter immediately. Modifying the readahead logic would be a massive |
1509 |
|
// change. Therefore we use the trick to suppress reading the next block as required, which means |
1510 |
|
// we will get several calls to execute() in a row which are used to finish the handlers. This is |
1511 |
|
// needed for remapped codes which might involve up to three Python handlers, and Python oword subs. |
1512 |
|
// Note this is not an issue for NGC oword procedures. The call/return logic will set _setup.call_state to |
1513 |
|
// CS_REEXEC_PROLOG, CS_REEXEC_PYBODY, CS_REEXEC_EPILOG or CS_REEXEC_PYOSUB before returning, which |
1514 |
|
// also indicates the point which handler needs to be restarted |
1515 |
|
// |
1516 |
|
// We use the following conditions to 'skip reading the next block and stay on the same block' |
1517 |
|
// until done as follows: |
1518 |
|
// |
1519 |
|
// 1. block.o_type = O_call and |
1520 |
|
// block.call_type in {CT_PYTHON_OWORD_SUB, CT_REMAP} and |
1521 |
|
// _setup.call_state > CS_NORMAL |
1522 |
|
// |
1523 |
|
// 2. block.o_type in {O_endsub, O_return} and |
1524 |
|
// block.call_type in {CT_PYTHON_OWORD_SUB, CT_REMAP} and |
1525 |
|
// _setup.call_state > CS_NORMAL |
1526 |
|
// |
1527 |
|
// handlers eventually return INTERP_OK, which sets _setup.call_state to CS_NORMAL. Then |
1528 |
|
// normal readahead continues. |
1529 |
|
// A call frame is tagged with the eblock->call_type since this potentially needs to persist across |
1530 |
|
// several blocks. Inside the execute_call()/execute_return() logic we use the frame call type |
1531 |
|
// to decide what to do. |
1532 |
|
// The handler reexec code will call read_inputs() just before continuation. |
1533 |
|
|
1534 |
11161 |
block_pointer eblock = &EXECUTING_BLOCK(_setup); |
1535 |
|
|
1536 |
22322 |
if ((_setup.call_state > CS_NORMAL) && |
1537 |
11161 |
(eblock->call_type > CT_NGC_OWORD_SUB) && |
1538 |
|
((eblock->o_type == O_call) || |
1539 |
|
(eblock->o_type == O_return) || |
1540 |
|
(eblock->o_type == O_endsub))) { |
1541 |
|
|
1542 |
|
logDebug("read(): skipping read"); |
1543 |
|
_setup.line_length = 0; |
1544 |
|
_setup.linetext[0] = 0; |
1545 |
|
return INTERP_OK; |
1546 |
|
} |
1547 |
11161 |
_setup.call_state = CS_NORMAL; |
1548 |
11161 |
CHP(read_inputs(&_setup)); |
1549 |
|
|
1550 |
|
|
1551 |
11161 |
CHKN(((command == NULL) && (_setup.file_pointer == NULL)), |
1552 |
|
INTERP_FILE_NOT_OPEN); |
1553 |
|
|
1554 |
11161 |
_setup.parameters[5420] = _setup.current_x; |
1555 |
11161 |
_setup.parameters[5421] = _setup.current_y; |
1556 |
11161 |
_setup.parameters[5422] = _setup.current_z; |
1557 |
11161 |
_setup.parameters[5423] = _setup.AA_current; |
1558 |
11161 |
_setup.parameters[5424] = _setup.BB_current; |
1559 |
11161 |
_setup.parameters[5425] = _setup.CC_current; |
1560 |
11161 |
_setup.parameters[5426] = _setup.u_current; |
1561 |
11161 |
_setup.parameters[5427] = _setup.v_current; |
1562 |
11161 |
_setup.parameters[5428] = _setup.w_current; |
1563 |
|
|
1564 |
11161 |
if(_setup.file_pointer) |
1565 |
|
{ |
1566 |
11149 |
EXECUTING_BLOCK(_setup).offset = ftell(_setup.file_pointer); |
1567 |
|
} |
1568 |
|
|
1569 |
|
read_status = |
1570 |
|
read_text(command, _setup.file_pointer, _setup.linetext, |
1571 |
11161 |
_setup.blocktext, &_setup.line_length); |
1572 |
|
|
1573 |
11161 |
if (read_status == INTERP_ERROR && _setup.skipping_to_sub) { |
1574 |
|
_setup.skipping_to_sub = NULL; |
1575 |
|
} |
1576 |
|
|
1577 |
11161 |
if(command)logDebug("%s:[cmd]:|%s|", name, command); |
1578 |
11149 |
else logDebug("%s:|%s|", name, _setup.linetext); |
1579 |
|
|
1580 |
22322 |
if ((read_status == INTERP_EXECUTE_FINISH) |
1581 |
11161 |
|| (read_status == INTERP_OK)) { |
1582 |
11160 |
if (_setup.line_length != 0) { |
1583 |
9455 |
CHP(parse_line(_setup.blocktext, &(EXECUTING_BLOCK(_setup)), &_setup)); |
1584 |
|
} |
1585 |
|
|
1586 |
|
else // Blank line (zero length) |
1587 |
|
{ |
1588 |
|
/* RUM - this case reached when the block delete '/' character |
1589 |
|
is used, or READ_FULL_COMMENT is false and a comment is the |
1590 |
|
only content of a line. |
1591 |
|
If a block o-type is in effect, block->o_number needs to be |
1592 |
|
incremented to allow o-extensions to work. |
1593 |
|
Note that the the block is 'refreshed' by init_block(), |
1594 |
|
not created new, so this is a legal operation on block1. */ |
1595 |
|
|
1596 |
|
// mah: FIXME test this - no idea what this is about; o_number is history |
1597 |
1705 |
if (EXECUTING_BLOCK(_setup).o_type != O_none) { |
1598 |
|
// Clear o_type, this isn't line isn't a command... |
1599 |
158 |
EXECUTING_BLOCK(_setup).o_type = 0; |
1600 |
|
} |
1601 |
|
} |
1602 |
1 |
} else if (read_status == INTERP_ENDFILE); |
1603 |
|
else |
1604 |
|
ERP(read_status); |
1605 |
11148 |
return read_status; |
1606 |
|
} |
1607 |
|
|
1608 |
11161 |
int Interp::read(const char *command) |
1609 |
|
{ |
1610 |
|
int status; |
1611 |
11161 |
if ((status = _read(command)) > INTERP_MIN_ERROR) { |
1612 |
13 |
unwind_call(status, __FILE__,__LINE__,__FUNCTION__); |
1613 |
|
} |
1614 |
11161 |
return status; |
1615 |
|
} |
1616 |
|
|
1617 |
|
// Reset interpreter state and terminate a call in progress by |
1618 |
|
// falling back to toplevel in a controlled way. Idempotent. |
1619 |
256 |
int Interp::unwind_call(int status, const char *file, int line, const char *function) |
1620 |
|
{ |
1621 |
256 |
logDebug("unwind_call: call_level=%d status=%s from %s %s:%d", |
1622 |
|
_setup.call_level, interp_status(status), function, file, line); |
1623 |
|
|
1624 |
3 |
for(; _setup.call_level > 0; _setup.call_level--) { |
1625 |
|
int i; |
1626 |
3 |
context * sub = _setup.sub_context + _setup.call_level - 1; |
1627 |
3 |
free_named_parameters(&_setup.sub_context[_setup.call_level]); |
1628 |
3 |
if(sub->subName) { |
1629 |
2 |
logDebug("unwind_call leaving sub '%s'", sub->subName); |
1630 |
2 |
sub->subName = 0; |
1631 |
|
} |
1632 |
|
|
1633 |
90 |
for(i=0; i<INTERP_SUB_PARAMS; i++) { |
1634 |
90 |
_setup.parameters[i+INTERP_FIRST_SUBROUTINE_PARAM] = |
1635 |
90 |
sub->saved_params[i]; |
1636 |
|
} |
1637 |
|
|
1638 |
|
// When called from Interp::close via Interp::reset, this one is NULL |
1639 |
3 |
if (!_setup.file_pointer) continue; |
1640 |
|
|
1641 |
|
// some frames may not have a filename and hence a position to seek to |
1642 |
|
// on return, like Python handlers |
1643 |
|
// needed to make sure this works in rs274 -n 0 (continue on error) mode |
1644 |
2 |
if (sub->filename && sub->filename[0]) { |
1645 |
2 |
if(0 != strcmp(_setup.filename, sub->filename)) { |
1646 |
2 |
fclose(_setup.file_pointer); |
1647 |
2 |
_setup.file_pointer = fopen(sub->filename, "r"); |
1648 |
2 |
logDebug("unwind_call: reopening '%s' at %ld", |
1649 |
|
sub->filename, sub->position); |
1650 |
2 |
strcpy(_setup.filename, sub->filename); |
1651 |
|
} |
1652 |
2 |
fseek(_setup.file_pointer, sub->position, SEEK_SET); |
1653 |
|
} |
1654 |
2 |
_setup.sequence_number = sub->sequence_number; |
1655 |
2 |
logDebug("unwind_call: setting sequence number=%d from frame %d", |
1656 |
|
_setup.sequence_number,_setup.call_level); |
1657 |
|
|
1658 |
|
} |
1659 |
|
// call_level == 0 here. |
1660 |
|
|
1661 |
256 |
if(_setup.sub_name) { |
1662 |
11 |
logDebug("unwind_call: exiting current sub '%s'\n", _setup.sub_name); |
1663 |
11 |
_setup.sub_name = 0; |
1664 |
|
} |
1665 |
256 |
_setup.remap_level = 0; // reset remapping stack |
1666 |
256 |
_setup.defining_sub = 0; |
1667 |
256 |
_setup.skipping_o = 0; |
1668 |
256 |
_setup.skipping_to_sub = 0; |
1669 |
256 |
_setup.offset_map.clear(); |
1670 |
256 |
_setup.mdi_interrupt = false; |
1671 |
|
|
1672 |
256 |
qc_reset(); |
1673 |
256 |
return INTERP_OK; |
1674 |
|
} |
1675 |
|
|
1676 |
11127 |
int Interp::read() { |
1677 |
11127 |
return read(0); |
1678 |
|
} |
1679 |
|
/***********************************************************************/ |
1680 |
|
|
1681 |
|
/*! Interp::reset |
1682 |
|
|
1683 |
|
Returned Value: int (INTERP_OK) |
1684 |
|
|
1685 |
|
Side Effects: See below |
1686 |
|
|
1687 |
|
Called By: |
1688 |
|
external programs |
1689 |
|
Interp::close |
1690 |
|
Interp::exit |
1691 |
|
Interp::open |
1692 |
|
|
1693 |
|
This function resets the parts of the _setup model having to do with |
1694 |
|
reading and interpreting one line. It does not affect the parts of the |
1695 |
|
model dealing with a file being open; Interp::open and Interp::close |
1696 |
|
do that. |
1697 |
|
|
1698 |
|
There is a hierarchy of resetting the interpreter. Each of the following |
1699 |
|
calls does everything the ones above it do. |
1700 |
|
|
1701 |
|
Interp::reset() |
1702 |
|
Interp::close() |
1703 |
|
Interp::init() |
1704 |
|
|
1705 |
|
In addition, Interp::synch and Interp::restore_parameters (both of |
1706 |
|
which are called by Interp::init) change the model. |
1707 |
|
|
1708 |
|
*/ |
1709 |
|
|
1710 |
189 |
int Interp::reset() |
1711 |
|
{ |
1712 |
189 |
_setup.call_state = CS_NORMAL; |
1713 |
|
//!!!KL According to the comment, |
1714 |
|
//!!!KL this should not be here because this is for |
1715 |
|
//!!!KL more than one line. |
1716 |
|
//!!!KL But the comment seems wrong -- it is only called at open, close, |
1717 |
|
//!!!KL init times which should affect the more global structure. |
1718 |
|
//!!!KL (also called by external -- but probably OK) |
1719 |
|
// |
1720 |
|
// initialization stuff for subroutines and control structures |
1721 |
189 |
_setup.linetext[0] = 0; |
1722 |
189 |
_setup.blocktext[0] = 0; |
1723 |
189 |
_setup.line_length = 0; |
1724 |
|
|
1725 |
189 |
unwind_call(INTERP_OK, __FILE__,__LINE__,__FUNCTION__); |
1726 |
189 |
return INTERP_OK; |
1727 |
|
} |
1728 |
|
|
1729 |
|
/***********************************************************************/ |
1730 |
|
|
1731 |
|
/*! Interp::restore_parameters |
1732 |
|
|
1733 |
|
Returned Value: |
1734 |
|
If any of the following errors occur, this returns the error code shown. |
1735 |
|
Otherwise it returns INTERP_OK. |
1736 |
|
1. The parameter file cannot be opened for reading: NCE_UNABLE_TO_OPEN_FILE |
1737 |
|
2. A parameter index is out of range: NCE_PARAMETER_NUMBER_OUT_OF_RANGE |
1738 |
|
3. The parameter file is not in increasing order: |
1739 |
|
NCE_PARAMETER_FILE_OUT_OF_ORDER |
1740 |
|
|
1741 |
|
Side Effects: See below |
1742 |
|
|
1743 |
|
Called By: |
1744 |
|
external programs |
1745 |
|
Interp::init |
1746 |
|
|
1747 |
|
This function restores the parameters from a file, modifying the |
1748 |
|
parameters array. Usually parameters is _setup.parameters. The file |
1749 |
|
contains lines of the form: |
1750 |
|
|
1751 |
|
<variable number> <value> |
1752 |
|
|
1753 |
|
e.g. |
1754 |
|
|
1755 |
|
5161 10.456 |
1756 |
|
|
1757 |
|
The variable numbers must be in increasing order, and certain |
1758 |
|
parameters must be included, as given in the _required_parameters |
1759 |
|
array. These are the axis offsets, the origin index (5220), and nine |
1760 |
|
sets of origin offsets. Any parameter not given a value in the file |
1761 |
|
has its value set to zero. |
1762 |
|
|
1763 |
|
*/ |
1764 |
68 |
int Interp::restore_parameters(const char *filename) //!< name of parameter file to read |
1765 |
|
{ |
1766 |
|
FILE *infile; |
1767 |
|
char line[256]; |
1768 |
|
int variable; |
1769 |
|
double value; |
1770 |
|
int required; // number of next required parameter |
1771 |
|
int index; // index into _required_parameters |
1772 |
|
double *pars; // short name for _setup.parameters |
1773 |
|
int k; |
1774 |
|
|
1775 |
|
// it's OK if the parameter file doesn't exist yet |
1776 |
|
// it'll be created in due course with some default values |
1777 |
68 |
if(access(filename, F_OK) == -1) |
1778 |
|
return INTERP_OK; |
1779 |
|
// open original for reading |
1780 |
3 |
infile = fopen(filename, "r"); |
1781 |
3 |
CHKS((infile == NULL), _("Unable to open parameter file: '%s'"), filename); |
1782 |
|
|
1783 |
3 |
pars = _setup.parameters; |
1784 |
3 |
k = 0; |
1785 |
3 |
index = 0; |
1786 |
3 |
required = _required_parameters[index++]; |
1787 |
254 |
while (feof(infile) == 0) { |
1788 |
251 |
if (fgets(line, 256, infile) == NULL) { |
1789 |
|
break; |
1790 |
|
} |
1791 |
|
// try for a variable-value match in the file |
1792 |
248 |
if (sscanf(line, "%d %lf", &variable, &value) == 2) { |
1793 |
248 |
CHKS(((variable <= 0) |
1794 |
|
|| (variable >= RS274NGC_MAX_PARAMETERS)), |
1795 |
|
NCE_PARAMETER_NUMBER_OUT_OF_RANGE); |
1796 |
15754 |
for (; k < RS274NGC_MAX_PARAMETERS; k++) { |
1797 |
16002 |
if (k > variable) { |
1798 |
|
fclose(infile); |
1799 |
|
ERS(NCE_PARAMETER_FILE_OUT_OF_ORDER); |
1800 |
16002 |
} else if (k == variable) { |
1801 |
248 |
pars[k] = value; |
1802 |
|
if (k == required) |
1803 |
|
required = _required_parameters[index++]; |
1804 |
248 |
k++; |
1805 |
248 |
break; |
1806 |
|
} else // if (k < variable) |
1807 |
|
{ |
1808 |
|
if (k == required) |
1809 |
|
required = _required_parameters[index++]; |
1810 |
15754 |
pars[k] = 0; |
1811 |
|
} |
1812 |
|
} |
1813 |
|
} |
1814 |
|
} |
1815 |
3 |
fclose(infile); |
1816 |
804 |
for (; k < RS274NGC_MAX_PARAMETERS; k++) { |
1817 |
804 |
pars[k] = 0; |
1818 |
|
} |
1819 |
|
return INTERP_OK; |
1820 |
|
} |
1821 |
|
|
1822 |
|
/***********************************************************************/ |
1823 |
|
|
1824 |
|
/*! Interp::save_parameters |
1825 |
|
|
1826 |
|
Returned Value: |
1827 |
|
If any of the following errors occur, this returns the error code shown. |
1828 |
|
Otherwise it returns INTERP_OK. |
1829 |
|
1. The existing file cannot be renamed: NCE_CANNOT_CREATE_BACKUP_FILE |
1830 |
|
2. The renamed file cannot be opened to read: NCE_CANNOT_OPEN_BACKUP_FILE |
1831 |
|
3. The new file cannot be opened to write: NCE_CANNOT_OPEN_VARIABLE_FILE |
1832 |
|
4. A parameter index is out of range: NCE_PARAMETER_NUMBER_OUT_OF_RANGE |
1833 |
|
5. The renamed file is out of order: NCE_PARAMETER_FILE_OUT_OF_ORDER |
1834 |
|
|
1835 |
|
Side Effects: See below |
1836 |
|
|
1837 |
|
Called By: |
1838 |
|
external programs |
1839 |
|
Interp::exit |
1840 |
|
|
1841 |
|
A file containing variable-value assignments is updated. The old |
1842 |
|
version of the file is saved under a different name. For each |
1843 |
|
variable-value pair in the old file, a line is written in the new file |
1844 |
|
giving the current value of the variable. File lines have the form: |
1845 |
|
|
1846 |
|
<variable number> <value> |
1847 |
|
|
1848 |
|
e.g. |
1849 |
|
|
1850 |
|
5161 10.456 |
1851 |
|
|
1852 |
|
If a required parameter is missing from the input file, this does not |
1853 |
|
complain, but does write it in the output file. |
1854 |
|
|
1855 |
|
*/ |
1856 |
149 |
int Interp::save_parameters(const char *filename, //!< name of file to write |
1857 |
|
const double parameters[]) //!< parameters to save |
1858 |
|
{ |
1859 |
|
FILE *infile; |
1860 |
|
FILE *outfile; |
1861 |
|
char line[PATH_MAX]; |
1862 |
|
int variable; |
1863 |
|
double value; |
1864 |
|
int required; // number of next required parameter |
1865 |
|
int index; // index into _required_parameters |
1866 |
|
int k; |
1867 |
|
|
1868 |
298 |
std::string tempfile = std::string(filename) + ".new"; |
1869 |
149 |
outfile = fopen(tempfile.c_str(), "w"); |
1870 |
149 |
CHKS((outfile == NULL), NCE_CANNOT_OPEN_VARIABLE_FILE); |
1871 |
|
|
1872 |
25 |
infile = fopen(filename, "r"); |
1873 |
25 |
if(!infile) |
1874 |
3 |
infile = fopen("/dev/null", "r"); |
1875 |
|
|
1876 |
25 |
k = 0; |
1877 |
25 |
index = 0; |
1878 |
25 |
required = _required_parameters[index++]; |
1879 |
2559 |
while (feof(infile) == 0) { |
1880 |
2534 |
if (fgets(line, sizeof(line), infile) == NULL) { |
1881 |
|
break; |
1882 |
|
} |
1883 |
|
// try for a variable-value match |
1884 |
2509 |
if (sscanf(line, "%d %lf", &variable, &value) == 2) { |
1885 |
2509 |
CHKS(((variable <= 0) |
1886 |
|
|| (variable >= RS274NGC_MAX_PARAMETERS)), |
1887 |
|
NCE_PARAMETER_NUMBER_OUT_OF_RANGE); |
1888 |
115922 |
for (; k < RS274NGC_MAX_PARAMETERS; k++) { |
1889 |
118431 |
if (k > variable) { |
1890 |
|
fclose(infile); |
1891 |
|
fclose(outfile); |
1892 |
|
ERS(NCE_PARAMETER_FILE_OUT_OF_ORDER); |
1893 |
118431 |
} else if (k == variable) { |
1894 |
2509 |
sprintf(line, "%d\t%f\n", k, parameters[k]); |
1895 |
2509 |
fputs(line, outfile); |
1896 |
2509 |
if (k == required) |
1897 |
2509 |
required = _required_parameters[index++]; |
1898 |
2509 |
k++; |
1899 |
2509 |
break; |
1900 |
115922 |
} else if (k == required) // know (k < variable) |
1901 |
|
{ |
1902 |
18 |
sprintf(line, "%d\t%f\n", k, parameters[k]); |
1903 |
18 |
fputs(line, outfile); |
1904 |
18 |
required = _required_parameters[index++]; |
1905 |
|
} |
1906 |
|
} |
1907 |
|
} |
1908 |
|
} |
1909 |
25 |
fclose(infile); |
1910 |
21619 |
for (; k < RS274NGC_MAX_PARAMETERS; k++) { |
1911 |
21619 |
if (k == required) { |
1912 |
448 |
sprintf(line, "%d\t%f\n", k, parameters[k]); |
1913 |
448 |
fputs(line, outfile); |
1914 |
448 |
required = _required_parameters[index++]; |
1915 |
|
} |
1916 |
|
} |
1917 |
|
|
1918 |
25 |
fflush(outfile); |
1919 |
25 |
fdatasync(fileno(outfile)); |
1920 |
25 |
fclose(outfile); |
1921 |
|
std::string bakfile = std::string(filename) |
1922 |
75 |
+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX; |
1923 |
25 |
unlink(bakfile.c_str()); |
1924 |
25 |
if(link(filename, bakfile.c_str()) < 0) |
1925 |
3 |
perror("link (updating variable file)"); |
1926 |
25 |
if(rename(tempfile.c_str(), filename) < 0) |
1927 |
|
perror("rename (updating variable file)"); |
1928 |
149 |
return INTERP_OK; |
1929 |
|
} |
1930 |
|
|
1931 |
|
/***********************************************************************/ |
1932 |
|
|
1933 |
|
/*! Interp::synch |
1934 |
|
|
1935 |
|
Returned Value: int (INTERP_OK) |
1936 |
|
|
1937 |
|
Side Effects: |
1938 |
|
sets the value of many attribute of _setup by calling various |
1939 |
|
GET_EXTERNAL_xxx functions. |
1940 |
|
|
1941 |
|
Called By: |
1942 |
|
Interp::init |
1943 |
|
external programs |
1944 |
|
|
1945 |
|
This function gets the _setup world model in synch with the rest of |
1946 |
|
the controller. |
1947 |
|
|
1948 |
|
*/ |
1949 |
|
|
1950 |
82 |
int Interp::synch() |
1951 |
|
{ |
1952 |
|
|
1953 |
|
char file_name[LINELEN]; |
1954 |
|
|
1955 |
82 |
_setup.control_mode = GET_EXTERNAL_MOTION_CONTROL_MODE(); |
1956 |
82 |
_setup.AA_current = GET_EXTERNAL_POSITION_A(); |
1957 |
82 |
_setup.BB_current = GET_EXTERNAL_POSITION_B(); |
1958 |
82 |
_setup.CC_current = GET_EXTERNAL_POSITION_C(); |
1959 |
82 |
_setup.current_pocket = GET_EXTERNAL_TOOL_SLOT(); |
1960 |
82 |
_setup.current_x = GET_EXTERNAL_POSITION_X(); |
1961 |
82 |
_setup.current_y = GET_EXTERNAL_POSITION_Y(); |
1962 |
82 |
_setup.current_z = GET_EXTERNAL_POSITION_Z(); |
1963 |
82 |
_setup.u_current = GET_EXTERNAL_POSITION_U(); |
1964 |
82 |
_setup.v_current = GET_EXTERNAL_POSITION_V(); |
1965 |
82 |
_setup.w_current = GET_EXTERNAL_POSITION_W(); |
1966 |
82 |
_setup.feed_rate = GET_EXTERNAL_FEED_RATE(); |
1967 |
82 |
_setup.flood = GET_EXTERNAL_FLOOD(); |
1968 |
82 |
_setup.length_units = GET_EXTERNAL_LENGTH_UNIT_TYPE(); |
1969 |
82 |
_setup.mist = GET_EXTERNAL_MIST(); |
1970 |
82 |
_setup.plane = GET_EXTERNAL_PLANE(); |
1971 |
82 |
_setup.selected_pocket = GET_EXTERNAL_SELECTED_TOOL_SLOT(); |
1972 |
82 |
_setup.speed = GET_EXTERNAL_SPEED(); |
1973 |
82 |
_setup.spindle_turning = GET_EXTERNAL_SPINDLE(); |
1974 |
82 |
_setup.pockets_max = GET_EXTERNAL_POCKETS_MAX(); |
1975 |
82 |
_setup.traverse_rate = GET_EXTERNAL_TRAVERSE_RATE(); |
1976 |
82 |
_setup.feed_override = GET_EXTERNAL_FEED_OVERRIDE_ENABLE(); |
1977 |
82 |
_setup.speed_override = GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(); |
1978 |
82 |
_setup.adaptive_feed = GET_EXTERNAL_ADAPTIVE_FEED_ENABLE(); |
1979 |
82 |
_setup.feed_hold = GET_EXTERNAL_FEED_HOLD_ENABLE(); |
1980 |
|
|
1981 |
82 |
GET_EXTERNAL_PARAMETER_FILE_NAME(file_name, (LINELEN - 1)); |
1982 |
82 |
save_parameters(((file_name[0] == |
1983 |
|
0) ? |
1984 |
|
RS274NGC_PARAMETER_FILE_NAME_DEFAULT : |
1985 |
164 |
file_name), _setup.parameters); |
1986 |
|
|
1987 |
82 |
load_tool_table(); /* must set _setup.tool_max first */ |
1988 |
|
|
1989 |
|
// read_inputs(&_setup); // input/probe/toolchange |
1990 |
|
|
1991 |
82 |
return INTERP_OK; |
1992 |
|
} |
1993 |
|
|
1994 |
|
/***********************************************************************/ |
1995 |
|
/***********************************************************************/ |
1996 |
|
|
1997 |
|
/* |
1998 |
|
|
1999 |
|
The functions in this section are to extract information from the |
2000 |
|
interpreter. |
2001 |
|
|
2002 |
|
*/ |
2003 |
|
|
2004 |
|
/***********************************************************************/ |
2005 |
|
|
2006 |
|
/*! Interp::active_g_codes |
2007 |
|
|
2008 |
|
Returned Value: none |
2009 |
|
|
2010 |
|
Side Effects: copies active G codes into the codes array |
2011 |
|
|
2012 |
|
Called By: external programs |
2013 |
|
|
2014 |
|
See documentation of write_g_codes. |
2015 |
|
|
2016 |
|
*/ |
2017 |
|
|
2018 |
2181 |
void Interp::active_g_codes(int *codes) //!< array of codes to copy into |
2019 |
|
{ |
2020 |
|
int n; |
2021 |
|
|
2022 |
37077 |
for (n = 0; n < ACTIVE_G_CODES; n++) { |
2023 |
34896 |
codes[n] = _setup.active_g_codes[n]; |
2024 |
|
} |
2025 |
2181 |
} |
2026 |
|
|
2027 |
|
/***********************************************************************/ |
2028 |
|
|
2029 |
|
/*! Interp::active_m_codes |
2030 |
|
|
2031 |
|
Returned Value: none |
2032 |
|
|
2033 |
|
Side Effects: copies active M codes into the codes array |
2034 |
|
|
2035 |
|
Called By: external programs |
2036 |
|
|
2037 |
|
See documentation of write_m_codes. |
2038 |
|
|
2039 |
|
*/ |
2040 |
|
|
2041 |
2181 |
void Interp::active_m_codes(int *codes) //!< array of codes to copy into |
2042 |
|
{ |
2043 |
|
int n; |
2044 |
|
|
2045 |
23991 |
for (n = 0; n < ACTIVE_M_CODES; n++) { |
2046 |
21810 |
codes[n] = _setup.active_m_codes[n]; |
2047 |
|
} |
2048 |
2181 |
} |
2049 |
|
|
2050 |
|
/***********************************************************************/ |
2051 |
|
|
2052 |
|
/*! Interp::active_settings |
2053 |
|
|
2054 |
|
Returned Value: none |
2055 |
|
|
2056 |
|
Side Effects: copies active F, S settings into array |
2057 |
|
|
2058 |
|
Called By: external programs |
2059 |
|
|
2060 |
|
See documentation of write_settings. |
2061 |
|
|
2062 |
|
*/ |
2063 |
|
|
2064 |
2181 |
void Interp::active_settings(double *settings) //!< array of settings to copy into |
2065 |
|
{ |
2066 |
|
int n; |
2067 |
|
|
2068 |
8724 |
for (n = 0; n < ACTIVE_SETTINGS; n++) { |
2069 |
6543 |
settings[n] = _setup.active_settings[n]; |
2070 |
|
} |
2071 |
2181 |
} |
2072 |
|
|
2073 |
300 |
void Interp::setError(const char *fmt, ...) |
2074 |
|
{ |
2075 |
|
va_list ap; |
2076 |
|
|
2077 |
300 |
va_start(ap, fmt); |
2078 |
|
|
2079 |
300 |
vsnprintf(savedError, LINELEN, fmt, ap); |
2080 |
|
|
2081 |
300 |
va_end(ap); |
2082 |
300 |
} |
2083 |
|
|
2084 |
|
const char *Interp::getSavedError() |
2085 |
|
{ |
2086 |
|
return savedError; |
2087 |
|
} |
2088 |
|
|
2089 |
|
// set error message text without going through printf format interpretation |
2090 |
1 |
int Interp::setSavedError(const char *msg) |
2091 |
|
{ |
2092 |
1 |
savedError[0] = '\0'; |
2093 |
1 |
strncpy(savedError, msg, LINELEN); |
2094 |
1 |
return INTERP_OK; |
2095 |
|
} |
2096 |
|
|
2097 |
|
/***********************************************************************/ |
2098 |
|
|
2099 |
|
/*! Interp::error_text |
2100 |
|
|
2101 |
|
Returned Value: none |
2102 |
|
|
2103 |
|
Side Effects: see below |
2104 |
|
|
2105 |
|
Called By: external programs |
2106 |
|
|
2107 |
|
This copies the error string whose index in the _rs274ngc_errors array |
2108 |
|
is error_code into the error_text array -- unless the error_code is |
2109 |
|
an out-of-bounds index or the length of the error string is not less |
2110 |
|
than max_size, in which case an empty string is put into the |
2111 |
|
error_text. The length of the error_text array should be at least |
2112 |
|
max_size. |
2113 |
|
|
2114 |
|
*/ |
2115 |
|
|
2116 |
51 |
char * Interp::error_text(int error_code, //!< code number of error |
2117 |
|
char *error_text, //!< char array to copy error text into |
2118 |
|
size_t max_size) //!< maximum number of characters to copy |
2119 |
|
{ |
2120 |
51 |
if(error_code == INTERP_ERROR) |
2121 |
|
{ |
2122 |
51 |
strncpy(error_text, savedError, max_size); |
2123 |
51 |
error_text[max_size-1] = 0; |
2124 |
|
|
2125 |
51 |
return error_text; |
2126 |
|
} |
2127 |
|
else if (error_code == INTERP_FILE_NOT_OPEN) |
2128 |
|
{ |
2129 |
|
strncpy(error_text, "File not open", max_size); |
2130 |
|
error_text[max_size-1] = 0; |
2131 |
|
|
2132 |
|
return error_text; |
2133 |
|
} |
2134 |
|
|
2135 |
|
error_text[0] = 0; |
2136 |
|
return error_text; |
2137 |
|
} |
2138 |
|
|
2139 |
|
/***********************************************************************/ |
2140 |
|
|
2141 |
|
/*! Interp::file_name |
2142 |
|
|
2143 |
|
Returned Value: none |
2144 |
|
|
2145 |
|
Side Effects: see below |
2146 |
|
|
2147 |
|
Called By: external programs |
2148 |
|
|
2149 |
|
This copies the _setup.filename (the name of the currently open |
2150 |
|
file) into the file_name array -- unless the name is not shorter than |
2151 |
|
max_size, in which case a null string is put in the file_name array. |
2152 |
|
|
2153 |
|
|
2154 |
|
*/ |
2155 |
|
|
2156 |
2244 |
char *Interp::file_name(char *file_name, //!< string: to copy file name into |
2157 |
|
size_t max_size) //!< maximum number of characters to copy |
2158 |
|
{ |
2159 |
2244 |
if (strlen(_setup.filename) < ((size_t) max_size)) |
2160 |
2180 |
strcpy(file_name, _setup.filename); |
2161 |
|
else |
2162 |
64 |
file_name[0] = 0; |
2163 |
2244 |
return file_name; |
2164 |
|
} |
2165 |
|
|
2166 |
|
/***********************************************************************/ |
2167 |
|
|
2168 |
|
/*! Interp::line_length |
2169 |
|
|
2170 |
|
Returned Value: the length of the most recently read line |
2171 |
|
|
2172 |
|
Side Effects: none |
2173 |
|
|
2174 |
|
Called By: external programs |
2175 |
|
|
2176 |
|
*/ |
2177 |
|
|
2178 |
65 |
size_t Interp::line_length() |
2179 |
|
{ |
2180 |
65 |
return _setup.line_length; |
2181 |
|
} |
2182 |
|
|
2183 |
|
/***********************************************************************/ |
2184 |
|
|
2185 |
|
/*! Interp::line_text |
2186 |
|
|
2187 |
|
Returned Value: none |
2188 |
|
|
2189 |
|
Side Effects: See below |
2190 |
|
|
2191 |
|
Called By: external programs |
2192 |
|
|
2193 |
|
This copies at most (max_size - 1) non-null characters of the most |
2194 |
|
recently read line into the line_text string and puts a NULL after the |
2195 |
|
last non-null character. |
2196 |
|
|
2197 |
|
*/ |
2198 |
|
|
2199 |
5037 |
char *Interp::line_text(char *line_text, //!< string: to copy line into |
2200 |
|
size_t max_size) //!< maximum number of characters to copy |
2201 |
|
{ |
2202 |
|
size_t n; |
2203 |
|
char *the_text; |
2204 |
|
|
2205 |
5037 |
the_text = _setup.linetext; |
2206 |
165465 |
for (n = 0; n < (max_size - 1); n++) { |
2207 |
165465 |
if (the_text[n] != 0) |
2208 |
160428 |
line_text[n] = the_text[n]; |
2209 |
|
else |
2210 |
|
break; |
2211 |
|
} |
2212 |
5037 |
line_text[n] = 0; |
2213 |
5037 |
return line_text; |
2214 |
|
} |
2215 |
|
|
2216 |
|
/***********************************************************************/ |
2217 |
|
|
2218 |
|
/*! Interp::sequence_number |
2219 |
|
|
2220 |
|
Returned Value: the current interpreter sequence number (how many |
2221 |
|
lines read since the last time the sequence number was reset to zero, |
2222 |
|
which happens only when Interp::init or Interp::open is called). |
2223 |
|
|
2224 |
|
Side Effects: none |
2225 |
|
|
2226 |
|
Called By: external programs |
2227 |
|
|
2228 |
|
*/ |
2229 |
|
|
2230 |
83 |
int Interp::sequence_number() |
2231 |
|
{ |
2232 |
83 |
return _setup.sequence_number; |
2233 |
|
} |
2234 |
|
|
2235 |
|
/***********************************************************************/ |
2236 |
|
|
2237 |
|
/*! Interp::stack_name |
2238 |
|
|
2239 |
|
Returned Value: none |
2240 |
|
|
2241 |
|
Side Effects: see below |
2242 |
|
|
2243 |
|
Called By: external programs |
2244 |
|
|
2245 |
|
This copies at most (max_size - 1) non-null characters from the |
2246 |
|
string whose index in the _setup.stack array is stack_index into the |
2247 |
|
function_name string and puts a NULL after the last non-null character -- |
2248 |
|
unless the stack_index is an out-of-bounds index, in which case an |
2249 |
|
empty string is put into the function_name. |
2250 |
|
|
2251 |
|
This function is intended to be used several times in a row to get the |
2252 |
|
stack of function calls that existed when the most recent error |
2253 |
|
occurred. It should be called first with stack_index equal to 0, |
2254 |
|
next with stack_index equal to 1, and so on, stopping when an |
2255 |
|
empty string is returned for the name. |
2256 |
|
|
2257 |
|
*/ |
2258 |
|
|
2259 |
4 |
char *Interp::stack_name(int stack_index, //!< index into stack of function names |
2260 |
|
char *function_name, //!< string: to copy function name into |
2261 |
|
size_t max_size) //!< maximum number of characters to copy |
2262 |
|
{ |
2263 |
|
size_t n; |
2264 |
|
char *the_name; |
2265 |
|
|
2266 |
4 |
if ((stack_index > -1) && (stack_index < STACK_LEN)) { |
2267 |
4 |
the_name = _setup.stack[stack_index]; |
2268 |
167 |
for (n = 0; n < (max_size - 1); n++) { |
2269 |
167 |
if (the_name[n] != 0) |
2270 |
163 |
function_name[n] = the_name[n]; |
2271 |
|
else |
2272 |
|
break; |
2273 |
|
} |
2274 |
4 |
function_name[n] = 0; |
2275 |
|
} else |
2276 |
|
function_name[0] = 0; |
2277 |
4 |
return function_name; |
2278 |
|
} |
2279 |
|
|
2280 |
|
/***********************************************************************/ |
2281 |
|
|
2282 |
|
/* Interp::ini_load() |
2283 |
|
|
2284 |
|
Returned Value: INTERP_OK, RS274NGC_ERROR |
2285 |
|
|
2286 |
|
Side Effects: |
2287 |
|
An INI file containing values for global variables is used to |
2288 |
|
update the globals |
2289 |
|
|
2290 |
|
Called By: |
2291 |
|
emctask before calling Interp::init() |
2292 |
|
|
2293 |
|
The file looks like this: |
2294 |
|
|
2295 |
|
[RS274NGC] |
2296 |
|
VARIABLE_FILE = rs274ngc.var |
2297 |
|
|
2298 |
|
*/ |
2299 |
|
|
2300 |
|
|
2301 |
2 |
int Interp::ini_load(const char *filename) |
2302 |
|
{ |
2303 |
2 |
IniFile inifile; |
2304 |
|
const char *inistring; |
2305 |
|
|
2306 |
|
// open it |
2307 |
2 |
if (inifile.Open(filename) == false) { |
2308 |
|
logDebug("Unable to open inifile:%s:", filename); |
2309 |
|
return -1; |
2310 |
|
} |
2311 |
|
|
2312 |
2 |
logDebug("Opened inifile:%s:", filename); |
2313 |
|
|
2314 |
|
|
2315 |
2 |
if (NULL != (inistring = inifile.Find("PARAMETER_FILE", "RS274NGC"))) { |
2316 |
|
// found it |
2317 |
2 |
strncpy(_parameter_file_name, inistring, LINELEN); |
2318 |
2 |
if (_parameter_file_name[LINELEN-1] != '\0') { |
2319 |
|
logDebug("%s:[RS274NGC]PARAMETER_FILE is too long (max len %d)", filename, LINELEN-1); |
2320 |
|
inifile.Close(); |
2321 |
|
_parameter_file_name[0] = '\0'; |
2322 |
|
return -1; |
2323 |
|
} |
2324 |
2 |
logDebug("found PARAMETER_FILE:%s:", _parameter_file_name); |
2325 |
|
} else { |
2326 |
|
// not found, leave RS274NGC_PARAMETER_FILE alone |
2327 |
|
logDebug("did not find PARAMETER_FILE"); |
2328 |
|
} |
2329 |
|
|
2330 |
|
// close it |
2331 |
2 |
inifile.Close(); |
2332 |
|
|
2333 |
|
return 0; |
2334 |
|
} |
2335 |
|
|
2336 |
68 |
int Interp::init_tool_parameters() |
2337 |
|
{ |
2338 |
68 |
if (_setup.random_toolchanger) { |
2339 |
|
// random_toolchanger: tool at startup expected |
2340 |
|
_setup.parameters[5400] = _setup.tool_table[0].toolno; |
2341 |
|
_setup.parameters[5401] = _setup.tool_table[0].offset.tran.x; |
2342 |
|
_setup.parameters[5402] = _setup.tool_table[0].offset.tran.y; |
2343 |
|
_setup.parameters[5403] = _setup.tool_table[0].offset.tran.z; |
2344 |
|
_setup.parameters[5404] = _setup.tool_table[0].offset.a; |
2345 |
|
_setup.parameters[5405] = _setup.tool_table[0].offset.b; |
2346 |
|
_setup.parameters[5406] = _setup.tool_table[0].offset.c; |
2347 |
|
_setup.parameters[5407] = _setup.tool_table[0].offset.u; |
2348 |
|
_setup.parameters[5408] = _setup.tool_table[0].offset.v; |
2349 |
|
_setup.parameters[5409] = _setup.tool_table[0].offset.w; |
2350 |
|
_setup.parameters[5410] = _setup.tool_table[0].diameter; |
2351 |
|
_setup.parameters[5411] = _setup.tool_table[0].frontangle; |
2352 |
|
_setup.parameters[5412] = _setup.tool_table[0].backangle; |
2353 |
|
_setup.parameters[5413] = _setup.tool_table[0].orientation; |
2354 |
|
} else { |
2355 |
|
// non random_toolchanger: no tool at startup, one-time init |
2356 |
68 |
if (_setup.tool_table[0].toolno == -1) { |
2357 |
5 |
default_tool_parameters(); |
2358 |
|
} |
2359 |
|
} |
2360 |
68 |
return 0; |
2361 |
|
} |
2362 |
|
|
2363 |
87 |
int Interp::default_tool_parameters() |
2364 |
|
{ |
2365 |
87 |
_setup.parameters[5400] = 0; // toolno |
2366 |
87 |
_setup.parameters[5401] = 0; // x offset |
2367 |
87 |
_setup.parameters[5402] = 0; // y offset RESERVED |
2368 |
87 |
_setup.parameters[5403] = 0; // z offset |
2369 |
87 |
_setup.parameters[5404] = 0; // a offset RESERVED |
2370 |
87 |
_setup.parameters[5405] = 0; // b offset RESERVED |
2371 |
87 |
_setup.parameters[5406] = 0; // c offset RESERVED |
2372 |
87 |
_setup.parameters[5407] = 0; // u offset RESERVED |
2373 |
87 |
_setup.parameters[5408] = 0; // v offset RESERVED |
2374 |
87 |
_setup.parameters[5409] = 0; // w offset RESERVED |
2375 |
87 |
_setup.parameters[5410] = 0; // diameter |
2376 |
87 |
_setup.parameters[5411] = 0; // frontangle |
2377 |
87 |
_setup.parameters[5412] = 0; // backangle |
2378 |
87 |
_setup.parameters[5413] = 0; // orientation |
2379 |
87 |
return 0; |
2380 |
|
} |
2381 |
|
|
2382 |
100 |
int Interp::set_tool_parameters() |
2383 |
|
{ |
2384 |
|
// invoke to set tool parameters for current tool (pocket==0) |
2385 |
|
// when a tool is absent, set default (zero offset) tool parameters |
2386 |
|
|
2387 |
100 |
if ((! _setup.random_toolchanger) && (_setup.tool_table[0].toolno <= 0)) { |
2388 |
82 |
default_tool_parameters(); |
2389 |
82 |
return 0; |
2390 |
|
} |
2391 |
18 |
_setup.parameters[5400] = _setup.tool_table[0].toolno; |
2392 |
18 |
_setup.parameters[5401] = _setup.tool_table[0].offset.tran.x; |
2393 |
18 |
_setup.parameters[5402] = _setup.tool_table[0].offset.tran.y; |
2394 |
18 |
_setup.parameters[5403] = _setup.tool_table[0].offset.tran.z; |
2395 |
18 |
_setup.parameters[5404] = _setup.tool_table[0].offset.a; |
2396 |
18 |
_setup.parameters[5405] = _setup.tool_table[0].offset.b; |
2397 |
18 |
_setup.parameters[5406] = _setup.tool_table[0].offset.c; |
2398 |
18 |
_setup.parameters[5407] = _setup.tool_table[0].offset.u; |
2399 |
18 |
_setup.parameters[5408] = _setup.tool_table[0].offset.v; |
2400 |
18 |
_setup.parameters[5409] = _setup.tool_table[0].offset.w; |
2401 |
18 |
_setup.parameters[5410] = _setup.tool_table[0].diameter; |
2402 |
18 |
_setup.parameters[5411] = _setup.tool_table[0].frontangle; |
2403 |
18 |
_setup.parameters[5412] = _setup.tool_table[0].backangle; |
2404 |
18 |
_setup.parameters[5413] = _setup.tool_table[0].orientation; |
2405 |
|
|
2406 |
18 |
return 0; |
2407 |
|
} |
2408 |
|
|
2409 |
|
int Interp::enter_remap(void) |
2410 |
|
{ |
2411 |
|
_setup.remap_level++; |
2412 |
|
if (_setup.remap_level == MAX_NESTED_REMAPS) { |
2413 |
|
_setup.remap_level = 0; |
2414 |
|
ERS("maximum nesting of remapped blocks execeeded"); |
2415 |
|
} |
2416 |
|
|
2417 |
|
// push onto block stack |
2418 |
|
CONTROLLING_BLOCK(_setup) = EXECUTING_BLOCK(_setup); |
2419 |
|
CONTROLLING_BLOCK(_setup).breadcrumbs = 0; // clear trail |
2420 |
|
// set later but tested for in remap_in_progress() |
2421 |
|
CONTROLLING_BLOCK(_setup).executing_remap = NULL; |
2422 |
|
|
2423 |
|
// remember the line where remap was discovered |
2424 |
|
logRemap("enter_remap into %d - saved_line_number=%d", |
2425 |
|
_setup.remap_level, _setup.sequence_number); |
2426 |
|
CONTROLLING_BLOCK(_setup).saved_line_number = _setup.sequence_number; |
2427 |
|
_setup.sequence_number = 0; |
2428 |
|
return INTERP_OK; |
2429 |
|
} |
2430 |
|
|
2431 |
|
int Interp::leave_remap(void) |
2432 |
|
{ |
2433 |
|
// restore the line number where remap was found |
2434 |
|
logRemap("leave_remap from %d propagate saved_line_number=%d", |
2435 |
|
_setup.remap_level, CONTROLLING_BLOCK(_setup).saved_line_number); |
2436 |
|
|
2437 |
|
_setup.sequence_number = CONTROLLING_BLOCK(_setup).saved_line_number; |
2438 |
|
_setup.blocks[_setup.remap_level].executing_remap = NULL; |
2439 |
|
_setup.remap_level--; // drop one nesting level |
2440 |
|
if (_setup.remap_level < 0) { |
2441 |
|
ERS("BUG: remap_level < 0 : %d",_setup.remap_level); |
2442 |
|
} |
2443 |
|
return INTERP_OK; |
2444 |
|
} |
2445 |
|
|
2446 |
|
|
2447 |
44 |
void Interp::program_end_cleanup(setup_pointer settings) { |
2448 |
|
int index; |
2449 |
|
|
2450 |
44 |
settings->current_x += settings->origin_offset_x; |
2451 |
44 |
settings->current_y += settings->origin_offset_y; |
2452 |
44 |
settings->current_z += settings->origin_offset_z; |
2453 |
44 |
settings->AA_current += settings->AA_origin_offset; |
2454 |
44 |
settings->BB_current += settings->BB_origin_offset; |
2455 |
44 |
settings->CC_current += settings->CC_origin_offset; |
2456 |
44 |
settings->u_current += settings->u_origin_offset; |
2457 |
44 |
settings->v_current += settings->v_origin_offset; |
2458 |
44 |
settings->w_current += settings->w_origin_offset; |
2459 |
44 |
rotate(&settings->current_x, &settings->current_y, settings->rotation_xy); |
2460 |
|
|
2461 |
44 |
settings->origin_index = 1; |
2462 |
44 |
settings->parameters[5220] = 1.0; |
2463 |
44 |
settings->origin_offset_x = USER_TO_PROGRAM_LEN(settings->parameters[5221]); |
2464 |
44 |
settings->origin_offset_y = USER_TO_PROGRAM_LEN(settings->parameters[5222]); |
2465 |
44 |
settings->origin_offset_z = USER_TO_PROGRAM_LEN(settings->parameters[5223]); |
2466 |
44 |
settings->AA_origin_offset = USER_TO_PROGRAM_ANG(settings->parameters[5224]); |
2467 |
44 |
settings->BB_origin_offset = USER_TO_PROGRAM_ANG(settings->parameters[5225]); |
2468 |
44 |
settings->CC_origin_offset = USER_TO_PROGRAM_ANG(settings->parameters[5226]); |
2469 |
44 |
settings->u_origin_offset = USER_TO_PROGRAM_LEN(settings->parameters[5227]); |
2470 |
44 |
settings->v_origin_offset = USER_TO_PROGRAM_LEN(settings->parameters[5228]); |
2471 |
44 |
settings->w_origin_offset = USER_TO_PROGRAM_LEN(settings->parameters[5229]); |
2472 |
44 |
settings->rotation_xy = settings->parameters[5230]; |
2473 |
|
|
2474 |
44 |
rotate(&settings->current_x, &settings->current_y, -settings->rotation_xy); |
2475 |
44 |
settings->current_x -= settings->origin_offset_x; |
2476 |
44 |
settings->current_y -= settings->origin_offset_y; |
2477 |
44 |
settings->current_z -= settings->origin_offset_z; |
2478 |
44 |
settings->AA_current -= settings->AA_origin_offset; |
2479 |
44 |
settings->BB_current -= settings->BB_origin_offset; |
2480 |
44 |
settings->CC_current -= settings->CC_origin_offset; |
2481 |
44 |
settings->u_current -= settings->u_origin_offset; |
2482 |
44 |
settings->v_current -= settings->v_origin_offset; |
2483 |
44 |
settings->w_current -= settings->w_origin_offset; |
2484 |
|
|
2485 |
|
SET_G5X_OFFSET(settings->origin_index, |
2486 |
|
settings->origin_offset_x, |
2487 |
|
settings->origin_offset_y, |
2488 |
|
settings->origin_offset_z, |
2489 |
|
settings->AA_origin_offset, |
2490 |
|
settings->BB_origin_offset, |
2491 |
|
settings->CC_origin_offset, |
2492 |
|
settings->u_origin_offset, |
2493 |
|
settings->v_origin_offset, |
2494 |
44 |
settings->w_origin_offset); |
2495 |
44 |
SET_XY_ROTATION(settings->rotation_xy); |
2496 |
|
|
2497 |
44 |
if (settings->plane != CANON_PLANE_XY) { |
2498 |
7 |
SELECT_PLANE(CANON_PLANE_XY); |
2499 |
7 |
settings->plane = CANON_PLANE_XY; |
2500 |
|
} |
2501 |
|
|
2502 |
44 |
settings->distance_mode = MODE_ABSOLUTE; |
2503 |
|
|
2504 |
44 |
settings->feed_mode = UNITS_PER_MINUTE; |
2505 |
44 |
SET_FEED_MODE(0); |
2506 |
44 |
settings->feed_rate = 0; |
2507 |
44 |
SET_FEED_RATE(0); |
2508 |
|
|
2509 |
44 |
if (!settings->feed_override) { |
2510 |
|
ENABLE_FEED_OVERRIDE(); |
2511 |
|
settings->feed_override = true; |
2512 |
|
} |
2513 |
44 |
if (!settings->speed_override) { |
2514 |
|
ENABLE_SPEED_OVERRIDE(); |
2515 |
|
settings->speed_override = true; |
2516 |
|
} |
2517 |
|
|
2518 |
44 |
settings->cutter_comp_side = false; |
2519 |
44 |
settings->cutter_comp_firstmove = true; |
2520 |
|
|
2521 |
44 |
STOP_SPINDLE_TURNING(); |
2522 |
44 |
settings->spindle_turning = CANON_STOPPED; |
2523 |
|
|
2524 |
|
/* turn off FPR */ |
2525 |
44 |
SET_SPINDLE_MODE(0); |
2526 |
|
|
2527 |
44 |
settings->motion_mode = G_1; |
2528 |
|
|
2529 |
44 |
if (settings->mist) { |
2530 |
1 |
MIST_OFF(); |
2531 |
1 |
settings->mist = false; |
2532 |
|
} |
2533 |
44 |
if (settings->flood) { |
2534 |
1 |
FLOOD_OFF(); |
2535 |
1 |
settings->flood = false; |
2536 |
|
} |
2537 |
|
|
2538 |
|
/*10*/ |
2539 |
44 |
if (settings->disable_g92_persistence) |
2540 |
|
// Clear G92/G52 offset |
2541 |
10 |
for (index=5210; index<=5219; index++) |
2542 |
10 |
settings->parameters[index] = 0; |
2543 |
44 |
} |
2544 |
|
|
2545 |
|
|
2546 |
3 |
int Interp::on_abort(int reason, const char *message) |
2547 |
|
{ |
2548 |
3 |
logDebug("on_abort reason=%d message='%s'", reason, message); |
2549 |
|
|
2550 |
3 |
reset(); |
2551 |
3 |
_setup.mdi_interrupt = false; |
2552 |
|
|
2553 |
|
// clear in case set by an interrupted remapped procedure |
2554 |
|
// if set, may cause a "Queue is not empty after tool change" error |
2555 |
3 |
_setup.toolchange_flag = false; |
2556 |
3 |
_setup.probe_flag = false; |
2557 |
3 |
_setup.input_flag = false; |
2558 |
|
|
2559 |
3 |
logDebug("interp: %s simulating M30\n", __func__); |
2560 |
3 |
program_end_cleanup(&_setup); |
2561 |
|
|
2562 |
3 |
if (_setup.on_abort_command == NULL) |
2563 |
|
return -1; |
2564 |
|
|
2565 |
|
char cmd[LINELEN]; |
2566 |
|
|
2567 |
|
snprintf(cmd,sizeof(cmd), "%s [%d]",_setup.on_abort_command, reason); |
2568 |
|
int status = execute(cmd); |
2569 |
|
|
2570 |
|
ERP(status); |
2571 |
|
return status; |
2572 |
|
} |
2573 |
|
|
2574 |
|
// spun out from interp_o_word so we can use it to test ngc file accessibility during |
2575 |
|
// config file parsing (REMAP... ngc=<basename>) |
2576 |
6 |
FILE *Interp::find_ngc_file(setup_pointer settings,const char *basename, char *foundhere ) |
2577 |
|
{ |
2578 |
|
FILE *newFP; |
2579 |
|
char tmpFileName[PATH_MAX+1]; |
2580 |
|
char newFileName[PATH_MAX+1]; |
2581 |
|
char foundPlace[PATH_MAX+1]; |
2582 |
|
int dct; |
2583 |
|
|
2584 |
|
// look for a new file |
2585 |
6 |
sprintf(tmpFileName, "%s.ngc", basename); |
2586 |
|
|
2587 |
|
// find subroutine by search: program_prefix, subroutines, wizard_root |
2588 |
|
// use first file found |
2589 |
|
|
2590 |
|
// first look in the program_prefix place |
2591 |
6 |
sprintf(newFileName, "%s/%s", settings->program_prefix, tmpFileName); |
2592 |
6 |
newFP = fopen(newFileName, "r"); |
2593 |
|
|
2594 |
|
// then look in the subroutines place |
2595 |
6 |
if (!newFP) { |
2596 |
10 |
for (dct = 0; dct < MAX_SUB_DIRS; dct++) { |
2597 |
15 |
if (!settings->subroutines[dct]) |
2598 |
|
continue; |
2599 |
5 |
sprintf(newFileName, "%s/%s", settings->subroutines[dct], tmpFileName); |
2600 |
5 |
newFP = fopen(newFileName, "r"); |
2601 |
5 |
if (newFP) { |
2602 |
|
// logOword("fopen: |%s|", newFileName); |
2603 |
|
break; // use first occurrence in dir search |
2604 |
|
} |
2605 |
|
} |
2606 |
|
} |
2607 |
|
// if not found, search the wizard tree |
2608 |
6 |
if (!newFP) { |
2609 |
|
int ret; |
2610 |
1 |
ret = findFile(settings->wizard_root, tmpFileName, foundPlace); |
2611 |
|
|
2612 |
1 |
if (INTERP_OK == ret) { |
2613 |
|
// create the long name |
2614 |
|
sprintf(newFileName, "%s/%s", |
2615 |
|
foundPlace, tmpFileName); |
2616 |
|
newFP = fopen(newFileName, "r"); |
2617 |
|
} |
2618 |
|
} |
2619 |
6 |
if (foundhere && (newFP != NULL)) |
2620 |
5 |
strcpy(foundhere, newFileName); |
2621 |
6 |
return newFP; |
2622 |
|
} |
2623 |
|
|
2624 |
69 |
static std::set<std::string> stringtable; |
2625 |
|
|
2626 |
10461 |
const char *strstore(const char *s) |
2627 |
|
{ |
2628 |
|
using namespace std; |
2629 |
|
|
2630 |
10461 |
if (s == NULL) |
2631 |
|
throw invalid_argument("strstore(): NULL argument"); |
2632 |
20922 |
pair< set<string>::iterator, bool > pair = stringtable.insert(s); |
2633 |
20922 |
return pair.first->c_str(); |
2634 |
|
} |
2635 |
|
|
2636 |
680 |
context_struct::context_struct() |
2637 |
|
: position(0), sequence_number(0), filename(""), subName(""), |
2638 |
1360 |
context_status(0), call_type(0) |
2639 |
|
|
2640 |
|
{ |
2641 |
680 |
memset(saved_params, 0, sizeof(saved_params)); |
2642 |
680 |
memset(saved_g_codes, 0, sizeof(saved_g_codes)); |
2643 |
680 |
memset(saved_m_codes, 0, sizeof(saved_m_codes)); |
2644 |
680 |
memset(saved_settings, 0, sizeof(saved_settings)); |
2645 |
887 |
} |