GCC Code Coverage Report
Directory: emc/rs274ngc/ Exec Total Coverage
File: emc/rs274ngc/rs274ngc_pre.cc Lines: 727 982 74.0 %
Date: 2016-10-27 Branches: 511 1284 39.8 %

Line Exec Source
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
}