GCC Code Coverage Report
Directory: emc/rs274ngc/ Exec Total Coverage
File: emc/rs274ngc/interp_convert.cc Lines: 1273 2046 62.2 %
Date: 2016-10-27 Branches: 1146 2970 38.6 %

Line Exec Source
1
/********************************************************************
2
* Description: interp_convert.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
********************************************************************/
13
#ifndef _GNU_SOURCE
14
#define _GNU_SOURCE
15
#endif
16
#include <unistd.h>
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <math.h>
20
#include <string.h>
21
#include <ctype.h>
22
#include <sys/types.h>
23
#include <sys/stat.h>
24
#include <string>
25
#include "rtapi_math.h"
26
#include "rs274ngc.hh"
27
#include "rs274ngc_return.hh"
28
#include "rs274ngc_interp.hh"
29
#include "interp_internal.hh"
30
#include "interp_queue.hh"
31
32
#include "units.h"
33
#define TOOL_INSIDE_ARC(side, turn) (((side)==LEFT&&(turn)>0)||((side)==RIGHT&&(turn)<0))
34
#define DEBUG_EMC
35
36
37
// These four functions help make the rest of cutter comp
38
// plane-agnostic in much the same way the ARC_FEED canon call is.
39
// The programmer can gleefully think of only the XY plane when
40
// reading convert_[straight|arc]_comp[1|2].
41
//
42
// Because the STRAIGHT_[FEED|TRAVERSE] canon calls are not
43
// plane-agnostic, the opposite plane conversion happens in
44
// enqueue_STRAIGHT_[FEED|TRAVERSE] when adding to the interp queue.
45
46
1096
int Interp::comp_get_current(setup_pointer settings, double *x, double *y, double *z) {
47
1096
    switch(settings->plane) {
48
    case CANON_PLANE_XY:
49
979
        *x = settings->current_x;
50
979
        *y = settings->current_y;
51
979
        *z = settings->current_z;
52
979
        break;
53
    case CANON_PLANE_XZ:
54
114
        *x = settings->current_z;
55
114
        *y = settings->current_x;
56
114
        *z = settings->current_y;
57
114
        break;
58
    default:
59
3
        ERS("BUG: Invalid plane in comp_get_current");
60
    }
61
    return INTERP_OK;
62
}
63
64
743
int Interp::comp_set_current(setup_pointer settings, double x, double y, double z) {
65
743
    switch(settings->plane) {
66
    case CANON_PLANE_XY:
67
665
        settings->current_x = x;
68
665
        settings->current_y = y;
69
665
        settings->current_z = z;
70
665
        break;
71
    case CANON_PLANE_XZ:
72
78
        settings->current_x = y;
73
78
        settings->current_y = z;
74
78
        settings->current_z = x;
75
78
        break;
76
    default:
77
        ERS("BUG: Invalid plane in comp_set_current");
78
    }
79
    return INTERP_OK;
80
}
81
82
685
int Interp::comp_get_programmed(setup_pointer settings, double *x, double *y, double *z) {
83
685
    switch(settings->plane) {
84
    case CANON_PLANE_XY:
85
609
        *x = settings->program_x;
86
609
        *y = settings->program_y;
87
609
        *z = settings->program_z;
88
609
        break;
89
    case CANON_PLANE_XZ:
90
76
        *x = settings->program_z;
91
76
        *y = settings->program_x;
92
76
        *z = settings->program_y;
93
76
        break;
94
    default:
95
        ERS("BUG: Invalid plane in comp_get_programmed");
96
    }
97
    return INTERP_OK;
98
}
99
100
743
int Interp::comp_set_programmed(setup_pointer settings, double x, double y, double z) {
101
743
    switch(settings->plane) {
102
    case CANON_PLANE_XY:
103
665
        settings->program_x = x;
104
665
        settings->program_y = y;
105
665
        settings->program_z = z;
106
665
        break;
107
    case CANON_PLANE_XZ:
108
78
        settings->program_x = y;
109
78
        settings->program_y = z;
110
78
        settings->program_z = x;
111
78
        break;
112
    default:
113
        ERS("BUG: Invalid plane in comp_set_programmed");
114
    }
115
    return INTERP_OK;
116
}
117
118
/****************************************************************************/
119
120
/*! convert_nurbs
121
 *
122
 * Returned value: int
123
 * Returns a rs274ngc error code, or INTERP_OK if everything is OK.
124
 *
125
 * Side effects: Generates a nurbs move and updates the position of the tool
126
 */
127
128
static unsigned int nurbs_order;
129
69
static std::vector<CONTROL_POINT> nurbs_control_points;
130
131
5
int Interp::convert_nurbs(int mode,
132
      block_pointer block,     //!< pointer to a block of RS274 instructions
133
      setup_pointer settings)  //!< pointer to machine settings
134
{
135
    double end_z, AA_end, BB_end, CC_end, u_end, v_end, w_end;
136
    CONTROL_POINT CP;
137
138
5
    if (mode == G_5_2)  {
139
4
	CHKS((((block->x_flag) && !(block->y_flag)) || (!(block->x_flag) && (block->y_flag))), (
140
             _("You must specify both X and Y coordinates for Control Points")));
141
4
	CHKS((!(block->x_flag) && !(block->y_flag) && (block->p_number > 0) &&
142
             (!nurbs_control_points.empty())), (
143
             _("Can specify P without X and Y only for the first control point")));
144
145
4
        CHKS(((block->p_number <= 0) && (!nurbs_control_points.empty())), (
146
             _("Must specify positive weight P for every Control Point")));
147
4
        if (settings->feed_mode == UNITS_PER_MINUTE) {
148
4
            CHKS((settings->feed_rate == 0.0), (
149
                 _("Cannot make a NURBS with 0 feedrate")));
150
        }
151
4
        if (settings->motion_mode != mode) nurbs_control_points.clear();
152
153
4
        if (nurbs_control_points.empty()) {
154
1
            CP.X = settings->current_x;
155
1
            CP.Y = settings->current_y;
156
1
            if (!(block->x_flag) && !(block->y_flag) && (block->p_number > 0)) {
157
                CP.W = block->p_number;
158
            } else {
159
1
                CP.W = 1;
160
            }
161
1
            nurbs_order = 3;
162
1
            nurbs_control_points.push_back(CP);
163
        }
164
4
        if (block->l_number != -1 && block->l_number > 3) {
165
            nurbs_order = block->l_number;
166
        }
167
4
        if ((block->x_flag) && (block->y_flag)) {
168
4
            CHP(find_ends(block, settings, &CP.X, &CP.Y, &end_z, &AA_end, &BB_end, &CC_end,
169
                          &u_end, &v_end, &w_end));
170
4
            CP.W = block->p_number;
171
4
            nurbs_control_points.push_back(CP);
172
            }
173
174
//for (i=0;i<nurbs_control_points.size();i++){
175
//                printf( "X %8.4f, Y %8.4f, W %8.4f\n",
176
//              nurbs_control_points[i].X,
177
//               nurbs_control_points[i].Y,
178
//               nurbs_control_points[i].W);
179
//       }
180
//        printf("*-----------------------------------------*\n");
181
4
        settings->motion_mode = mode;
182
    }
183
184
1
    else if (mode == G_5_3){
185
1
        CHKS((settings->motion_mode != G_5_2), (
186
             _("Cannot use G5.3 without G5.2 first")));
187
1
        CHKS((nurbs_control_points.size()<nurbs_order), _("You must specify a number of control points at least equal to the order L = %d"), nurbs_order);
188
2
	settings->current_x = nurbs_control_points[nurbs_control_points.size()-1].X;
189
1
        settings->current_y = nurbs_control_points[nurbs_control_points.size()-1].Y;
190
2
        NURBS_FEED(block->line_number, nurbs_control_points, nurbs_order);
191
	//printf("hello\n");
192
	nurbs_control_points.clear();
193
	//printf("%d\n", 	nurbs_control_points.size());
194
1
	settings->motion_mode = -1;
195
    }
196
    return INTERP_OK;
197
}
198
199
 /****************************************************************************/
200
201
/*! convert_spline
202
 *
203
 * Returned value: int
204
 * Returns a rs274ngc error code, or INTERP_OK if everything is OK.
205
 *
206
 * Side effects: Generates a spline move and updates the position of the tool
207
 */
208
int Interp::convert_spline(int mode,
209
       block_pointer block,     //!< pointer to a block of RS274 instructions
210
       setup_pointer settings)  //!< pointer to machine settings
211
{
212
    double x1, y1, x2, y2, x3, y3;
213
    double end_z, AA_end, BB_end, CC_end, u_end, v_end, w_end;
214
    CONTROL_POINT cp;
215
216
    CHKS((settings->cutter_comp_side), _("Cannot convert spline with cutter radius compensation")); // XXX
217
218
    if (settings->feed_mode == UNITS_PER_MINUTE) {
219
      CHKS((settings->feed_rate == 0.0),
220
        NCE_CANNOT_MAKE_ARC_WITH_ZERO_FEED_RATE);
221
    } else if (settings->feed_mode == INVERSE_TIME) {
222
      CHKS((!block->f_flag),
223
        NCE_F_WORD_MISSING_WITH_INVERSE_TIME_ARC_MOVE);
224
    }
225
226
    CHKS((settings->plane != CANON_PLANE_XY), _("Splines must be in the XY plane")); // XXX
227
       //Error (for now): Splines must be in XY plane
228
229
    CHKS((block->z_flag || block->a_flag || block->b_flag
230
          || block->c_flag),
231
          _("Splines may not have motion in Z, A, B, or C"));
232
233
    if(mode == G_5_1) {
234
      CHKS(!block->i_flag || !block->j_flag,
235
                  _("Must specify both I and J with G5.1"));
236
      x1 = settings->current_x + block->i_number;
237
      y1 = settings->current_y + block->j_number;
238
      CHP(find_ends(block, settings, &x2, &y2, &end_z, &AA_end, &BB_end, &CC_end,
239
                    &u_end, &v_end, &w_end));
240
      cp.W = 1;
241
      cp.X = settings->current_x, cp.Y = settings->current_y;
242
      nurbs_control_points.push_back(cp);
243
      cp.X = x1, cp.Y = y1;
244
      nurbs_control_points.push_back(cp);
245
      cp.X = x2, cp.Y = y2;
246
      nurbs_control_points.push_back(cp);
247
      NURBS_FEED(block->line_number, nurbs_control_points, 3);
248
      nurbs_control_points.clear();
249
      settings->current_x = x2;
250
      settings->current_y = y2;
251
    } else {
252
      if(!block->i_flag || !block->j_flag) {
253
          CHKS(block->i_flag || block->j_flag,
254
                  _("Must specify both I and J, or neither"));
255
          x1 = settings->current_x + settings->cycle_i;
256
          y1 = settings->current_y + settings->cycle_j;
257
      } else {
258
          x1 = settings->current_x + block->i_number;
259
          y1 = settings->current_y + block->j_number;
260
      }
261
      CHP(find_ends(block, settings, &x3, &y3, &end_z, &AA_end, &BB_end, &CC_end,
262
                    &u_end, &v_end, &w_end));
263
264
      CHKS(!block->p_flag || !block->q_flag,
265
	      _("Must specify both P and Q with G5"));
266
      x2 = x3 + block->p_number;
267
      y2 = y3 + block->q_number;
268
269
      cp.W = 1;
270
      cp.X = settings->current_x, cp.Y = settings->current_y;
271
      nurbs_control_points.push_back(cp);
272
      cp.X = x1, cp.Y = y1;
273
      nurbs_control_points.push_back(cp);
274
      cp.X = x2, cp.Y = y2;
275
      nurbs_control_points.push_back(cp);
276
      cp.X = x3, cp.Y = y3;
277
      nurbs_control_points.push_back(cp);
278
      NURBS_FEED(block->line_number, nurbs_control_points, 4);
279
      nurbs_control_points.clear();
280
281
      settings->cycle_i = -block->p_number;
282
      settings->cycle_j = -block->q_number;
283
      settings->current_x = x3;
284
      settings->current_y = y3;
285
    }
286
    return INTERP_OK;
287
}
288
289
/****************************************************************************/
290
291
/*! convert_arc
292
293
Returned Value: int
294
   If one of the following functions returns an error code,
295
   this returns that error code.
296
      convert_arc_comp1
297
      convert_arc_comp2
298
      convert_arc2
299
   If any of the following errors occur, this returns the error code shown.
300
   Otherwise, this returns INTERP_OK.
301
   1. The block has neither an r value nor any i,j,k values:
302
      NCE_R_I_J_K_WORDS_ALL_MISSING_FOR_ARC
303
   2. The block has both an r value and one or more i,j,k values:
304
      NCE_MIXED_RADIUS_IJK_FORMAT_FOR_ARC
305
   3. In the ijk format the XY-plane is selected and
306
      the block has a k value: NCE_K_WORD_GIVEN_FOR_ARC_IN_XY_PLANE
307
   4. In the ijk format the YZ-plane is selected and
308
      the block has an i value: NCE_I_WORD_GIVEN_FOR_ARC_IN_YZ_PLANE
309
   5. In the ijk format the XZ-plane is selected and
310
      the block has a j value: NCE_J_WORD_GIVEN_FOR_ARC_IN_XZ_PLANE
311
   6. In either format any of the following occurs.
312
      a. The XY-plane is selected and the block has no x or y value:
313
         NCE_X_AND_Y_WORDS_MISSING_FOR_ARC_IN_XY_PLANE
314
      b. The YZ-plane is selected and the block has no y or z value:
315
         NCE_Y_AND_Z_WORDS_MISSING_FOR_ARC_IN_YZ_PLANE
316
      c. The ZX-plane is selected and the block has no z or x value:
317
         NCE_X_AND_Z_WORDS_MISSING_FOR_ARC_IN_XZ_PLANE
318
   7. The selected plane is an unknown plane:
319
      NCE_BUG_PLANE_NOT_XY_YZ__OR_XZ
320
   8. The feed rate mode is UNITS_PER_MINUTE and feed rate is zero:
321
      NCE_CANNOT_MAKE_ARC_WITH_ZERO_FEED_RATE
322
   9. The feed rate mode is INVERSE_TIME and the block has no f word:
323
      NCE_F_WORD_MISSING_WITH_INVERSE_TIME_ARC_MOVE
324
325
Side effects:
326
   This generates and executes an arc command at feed rate
327
   (and, possibly a second arc command). It also updates the setting
328
   of the position of the tool point to the end point of the move.
329
330
Called by: convert_motion.
331
332
This converts a helical or circular arc.  The function calls:
333
convert_arc2 (when cutter radius compensation is off) or
334
convert_arc_comp1 (when cutter comp is on and this is the first move) or
335
convert_arc_comp2 (when cutter comp is on and this is not the first move).
336
337
If the ijk format is used, at least one of the offsets in the current
338
plane must be given in the block; it is common but not required to
339
give both offsets. The offsets are always incremental [NCMS, page 21].
340
341
If cutter compensation is in use, the path's length may increase or
342
decrease.  Also an arc may be added, to go around a corner, before the
343
original arc move.  For the purpose of calculating the feed rate when in
344
inverse time mode, this length increase or decrease is ignored.  The
345
feed is still set to the original programmed arc length divided by the F
346
number (with the above lower bound).  The new arc (if needed) and the
347
new longer or shorter original arc are taken at this feed.
348
349
*/
350
351
537
int Interp::convert_arc(int move,        //!< either G_2 (cw arc) or G_3 (ccw arc)
352
                       block_pointer block,     //!< pointer to a block of RS274 instructions
353
                       setup_pointer settings)  //!< pointer to machine settings
354
{
355
  int status;
356
  int first;                    /* flag set true if this is first move after comp true */
357
  int ijk_flag;                 /* flag set true if any of i,j,k present in NC code  */
358
  double end_x;
359
  double end_y;
360
  double end_z;
361
  double AA_end;
362
  double BB_end;
363
  double CC_end;
364
  double u_end, v_end, w_end;
365
366
537
  CHKS((settings->arc_not_allowed), (_("The move just after exiting cutter compensation mode must be straight, not an arc")));
367
368
536
  ijk_flag = block->i_flag || block->j_flag || block->k_flag;
369
536
  first = settings->cutter_comp_firstmove;
370
371
536
  CHKS((settings->plane == CANON_PLANE_UV
372
            || settings->plane == CANON_PLANE_VW
373
            || settings->plane == CANON_PLANE_UW),
374
    _("Cannot do an arc in planes G17.1, G18.1, or G19.1"));
375
536
  CHKS(((!block->r_flag) && (!ijk_flag)),
376
      NCE_R_I_J_K_WORDS_ALL_MISSING_FOR_ARC);
377
535
  CHKS(((block->r_flag) && (ijk_flag)),
378
      NCE_MIXED_RADIUS_IJK_FORMAT_FOR_ARC);
379
535
  if (settings->feed_mode == UNITS_PER_MINUTE) {
380
532
    CHKS((settings->feed_rate == 0.0),
381
        NCE_CANNOT_MAKE_ARC_WITH_ZERO_FEED_RATE);
382
3
  } else if(settings->feed_mode == UNITS_PER_REVOLUTION) {
383
    CHKS((settings->feed_rate == 0.0),
384
        NCE_CANNOT_MAKE_ARC_WITH_ZERO_FEED_RATE);
385
    CHKS((settings->speed == 0.0),
386
	_("Cannot feed with zero spindle speed in feed per rev mode"));
387
3
  } else if (settings->feed_mode == INVERSE_TIME) {
388
3
    CHKS((!block->f_flag),
389
        NCE_F_WORD_MISSING_WITH_INVERSE_TIME_ARC_MOVE);
390
  }
391
392
535
  if (ijk_flag) {
393
535
    if (settings->plane == CANON_PLANE_XY) {
394
466
      CHKS((block->k_flag), NCE_K_WORD_GIVEN_FOR_ARC_IN_XY_PLANE);
395
466
      if (!block->i_flag) { /* i or j flag on to get here */
396
138
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
397
	  ERS(_("%c word missing in absolute center arc"), 'I');
398
	} else {
399
138
	  block->i_number = 0.0;
400
	}
401
328
      } else if (!block->j_flag) {
402
12
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
403
	  ERS(_("%c word missing in absolute center arc"), 'J');
404
	} else {
405
12
	  block->j_number = 0.0;
406
	}
407
      }
408
69
    } else if (settings->plane == CANON_PLANE_YZ) {
409
      CHKS((block->i_flag), NCE_I_WORD_GIVEN_FOR_ARC_IN_YZ_PLANE);
410
      if (!block->j_flag) { /* j or k flag on to get here */
411
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
412
	  ERS(_("%c word missing in absolute center arc"), 'J');
413
	} else {
414
	  block->j_number = 0.0;
415
	}
416
      } else if (!block->k_flag) {
417
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
418
	  ERS(_("%c word missing in absolute center arc"), 'K');
419
	} else {
420
	  block->k_number = 0.0;
421
	}
422
      }
423
69
    } else if (settings->plane == CANON_PLANE_XZ) {
424
69
      CHKS((block->j_flag), NCE_J_WORD_GIVEN_FOR_ARC_IN_XZ_PLANE);
425
69
      if (!block->i_flag) { /* i or k flag on to get here */
426
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
427
	  ERS(_("%c word missing in absolute center arc"), 'I');
428
	} else {
429
	  block->i_number = 0.0;
430
	}
431
69
      } else if (!block->k_flag) {
432
	if (settings->ijk_distance_mode == MODE_ABSOLUTE) {
433
	  ERS(_("%c word missing in absolute center arc"), 'K');
434
	} else {
435
	  block->k_number = 0.0;
436
	}
437
      }
438
    } else {
439
      ERS(NCE_BUG_PLANE_NOT_XY_YZ_OR_XZ);
440
    }
441
  } else {
442
    // in R format, we need some XYZ words specified because a full circle is not allowed.
443
    if (settings->plane == CANON_PLANE_XY) {
444
        CHKS(((!block->x_flag) && (!block->y_flag) && (!block->radius_flag) && (!block->theta_flag)),
445
            NCE_X_AND_Y_WORDS_MISSING_FOR_ARC_IN_XY_PLANE);
446
    } else if (settings->plane == CANON_PLANE_YZ) {
447
        CHKS(((!block->y_flag) && (!block->z_flag)),
448
            NCE_Y_AND_Z_WORDS_MISSING_FOR_ARC_IN_YZ_PLANE);
449
    } else if (settings->plane == CANON_PLANE_XZ) {
450
        CHKS(((!block->x_flag) && (!block->z_flag)),
451
            NCE_X_AND_Z_WORDS_MISSING_FOR_ARC_IN_XZ_PLANE);
452
    }
453
  }
454
455
456
535
  CHP(find_ends(block, settings, &end_x, &end_y, &end_z,
457
                &AA_end, &BB_end, &CC_end,
458
                &u_end, &v_end, &w_end));
459
460
535
  settings->motion_mode = move;
461
462
463
535
  if (settings->plane == CANON_PLANE_XY) {
464
878
    if ((!settings->cutter_comp_side) ||
465
412
        (settings->cutter_comp_radius == 0.0)) {
466
      status =
467
        convert_arc2(move, block, settings,
468
                     &(settings->current_x), &(settings->current_y),
469
                     &(settings->current_z), end_x, end_y, end_z,
470
                     AA_end, BB_end, CC_end,
471
                     u_end, v_end, w_end,
472
78
                     block->i_number, block->j_number);
473
78
      CHP(status);
474
388
    } else if (first) {
475
      status = convert_arc_comp1(move, block, settings, end_x, end_y, end_z,
476
                                 block->i_number, block->j_number,
477
                                 AA_end, BB_end, CC_end,
478
                                 u_end, v_end, w_end);
479
      CHP(status);
480
    } else {
481
      status = convert_arc_comp2(move, block, settings, end_x, end_y, end_z,
482
                                 block->i_number, block->j_number,
483
                                 AA_end, BB_end, CC_end,
484
388
                                 u_end, v_end, w_end);
485
388
      CHP(status);
486
    }
487
69
  } else if (settings->plane == CANON_PLANE_XZ) {
488
138
    if ((!settings->cutter_comp_side) ||
489
69
        (settings->cutter_comp_radius == 0.0)) {
490
      status =
491
        convert_arc2(move, block, settings,
492
                     &(settings->current_z), &(settings->current_x),
493
                     &(settings->current_y), end_z, end_x, end_y,
494
                     AA_end, BB_end, CC_end,
495
                     u_end, v_end, w_end,
496
23
                     block->k_number, block->i_number);
497
23
      CHP(status);
498
46
    } else if (first) {
499
      status = convert_arc_comp1(move, block, settings, end_z, end_x, end_y,
500
                                 block->k_number, block->i_number,
501
                                 AA_end, BB_end, CC_end,
502
                                 u_end, v_end, w_end);
503
      CHP(status);
504
    } else {
505
      status = convert_arc_comp2(move, block, settings, end_z, end_x, end_y,
506
                                 block->k_number, block->i_number,
507
                                 AA_end, BB_end, CC_end,
508
46
                                 u_end, v_end, w_end);
509
510
46
      CHP(status);
511
    }
512
  } else if (settings->plane == CANON_PLANE_YZ) {
513
    status =
514
      convert_arc2(move, block, settings,
515
                   &(settings->current_y), &(settings->current_z),
516
                   &(settings->current_x), end_y, end_z, end_x,
517
                   AA_end, BB_end, CC_end,
518
                   u_end, v_end, w_end,
519
                   block->j_number, block->k_number);
520
    CHP(status);
521
  } else
522
    ERS(NCE_BUG_PLANE_NOT_XY_YZ_OR_XZ);
523
  return INTERP_OK;
524
}
525
526
/****************************************************************************/
527
528
/*! convert_arc2
529
530
Returned Value: int
531
   If arc_data_ijk or arc_data_r returns an error code,
532
   this returns that code.
533
   Otherwise, it returns INTERP_OK.
534
535
Side effects:
536
   This executes an arc command at feed rate. It also updates the
537
   setting of the position of the tool point to the end point of the move.
538
539
Called by: convert_arc.
540
541
This converts a helical or circular arc.
542
543
*/
544
545
101
int Interp::convert_arc2(int move,       //!< either G_2 (cw arc) or G_3 (ccw arc)
546
                        block_pointer block,    //!< pointer to a block of RS274 instructions
547
                        setup_pointer settings, //!< pointer to machine settings
548
                        double *current1,       //!< pointer to current value of coordinate 1
549
                        double *current2,       //!< pointer to current value of coordinate 2
550
                        double *current3,       //!< pointer to current value of coordinate 3
551
                        double end1,    //!< coordinate 1 value at end of arc
552
                        double end2,    //!< coordinate 2 value at end of arc
553
                        double end3,    //!< coordinate 3 value at end of arc
554
                        double AA_end,  //!< a-value at end of arc
555
                        double BB_end,  //!< b-value at end of arc
556
                        double CC_end,  //!< c-value at end of arc
557
                         double u, double v, double w, //!< values at end of arc
558
                        double offset1, //!< center, either abs or offset from current
559
                        double offset2)
560
{
561
  double center1;
562
  double center2;
563
  int turn;                     /* number of full or partial turns CCW in arc */
564
101
  int plane = settings->plane;
565
566
  // Spiral tolerance is the amount of "spiral" allowed in a given arc segment, or (r2-r1)/theta
567
101
  double spiral_abs_tolerance = (settings->length_units == CANON_UNITS_INCHES) ?
568
101
    settings->center_arc_radius_tolerance_inch : settings->center_arc_radius_tolerance_mm;
569
570
  // Radius tolerance allows a bit of leeway on the minimum radius for a radius defined arc.
571
  double radius_tolerance = (settings->length_units == CANON_UNITS_INCHES) ?
572
101
    RADIUS_TOLERANCE_INCH : RADIUS_TOLERANCE_MM;
573
574
101
  if (block->r_flag) {
575
      CHP(arc_data_r(move, plane, *current1, *current2, end1, end2,
576
                     block->r_number, block->p_flag? round_to_int(block->p_number) : 1,
577
                     &center1, &center2, &turn, radius_tolerance));
578
  } else {
579
101
      CHP(arc_data_ijk(move, plane, *current1, *current2, end1, end2,
580
                       (settings->ijk_distance_mode == MODE_ABSOLUTE),
581
                       offset1, offset2, block->p_flag? round_to_int(block->p_number) : 1,
582
                       &center1, &center2, &turn, radius_tolerance, spiral_abs_tolerance, SPIRAL_RELATIVE_TOLERANCE));
583
  }
584
  inverse_time_rate_arc(*current1, *current2, *current3, center1, center2,
585
95
                        turn, end1, end2, end3, block, settings);
586
587
  ARC_FEED(block->line_number, end1, end2, center1, center2, turn, end3,
588
95
           AA_end, BB_end, CC_end, u, v, w);
589
95
  *current1 = end1;
590
95
  *current2 = end2;
591
95
  *current3 = end3;
592
95
  settings->AA_current = AA_end;
593
95
  settings->BB_current = BB_end;
594
95
  settings->CC_current = CC_end;
595
95
  settings->u_current = u;
596
95
  settings->v_current = v;
597
95
  settings->w_current = w;
598
599
95
  return INTERP_OK;
600
}
601
602
/****************************************************************************/
603
604
/*! convert_arc_comp1
605
606
Returned Value: int
607
   If arc_data_comp_ijk or arc_data_comp_r returns an error code,
608
   this returns that code.
609
   Otherwise, it returns INTERP_OK.
610
611
Side effects:
612
   This executes an arc command at
613
   feed rate. It also updates the setting of the position of
614
   the tool point to the end point of the move.
615
616
Called by: convert_arc.
617
618
This function converts a helical or circular arc, generating only one
619
arc. This is called when cutter radius compensation is on and this is
620
the first cut after the turning on.
621
622
The arc which is generated is derived from a second arc which passes
623
through the programmed end point and is tangent to the cutter at its
624
current location. The generated arc moves the tool so that it stays
625
tangent to the second arc throughout the move.
626
627
*/
628
629
int Interp::convert_arc_comp1(int move,  //!< either G_2 (cw arc) or G_3 (ccw arc)
630
                              block_pointer block,       //!< pointer to a block of RS274/NGC instructions
631
                              setup_pointer settings,    //!< pointer to machine settings
632
                              double end_x,      //!< x-value at end of programmed (then actual) arc
633
                              double end_y,      //!< y-value at end of programmed (then actual) arc
634
                              double end_z,      //!< z-value at end of arc
635
                              double offset_x, double offset_y,
636
                              double AA_end,     //!< a-value at end of arc
637
                             double BB_end,     //!< b-value at end of arc
638
                              double CC_end,     //!< c-value at end of arc
639
                              double u_end, double v_end, double w_end) //!< uvw at end of arc
640
{
641
    double center_x, center_y;
642
    double gamma;                 /* direction of perpendicular to arc at end */
643
    int side;                     /* offset side - right or left              */
644
    double tool_radius;
645
    int turn;                     /* 1 for counterclockwise, -1 for clockwise */
646
    double cx, cy, cz; // current
647
    int plane = settings->plane;
648
649
    side = settings->cutter_comp_side;
650
    tool_radius = settings->cutter_comp_radius;   /* always is positive */
651
652
    double spiral_abs_tolerance = (settings->length_units == CANON_UNITS_INCHES) ? settings->center_arc_radius_tolerance_inch : settings->center_arc_radius_tolerance_mm;
653
    double radius_tolerance = (settings->length_units == CANON_UNITS_INCHES) ? RADIUS_TOLERANCE_INCH : RADIUS_TOLERANCE_MM;
654
655
    comp_get_current(settings, &cx, &cy, &cz);
656
657
    CHKS((hypot((end_x - cx), (end_y - cy)) <= tool_radius),
658
         _("Radius of cutter compensation entry arc is not greater than the tool radius"));
659
660
    if (block->r_flag) {
661
        CHP(arc_data_comp_r(move, plane, side, tool_radius, cx, cy, end_x, end_y,
662
                            block->r_number, block->p_flag? round_to_int(block->p_number): 1,
663
                            &center_x, &center_y, &turn, radius_tolerance));
664
    } else {
665
        CHP(arc_data_comp_ijk(move, plane, side, tool_radius, cx, cy, end_x, end_y,
666
                              (settings->ijk_distance_mode == MODE_ABSOLUTE),
667
                              offset_x, offset_y, block->p_flag? round_to_int(block->p_number): 1,
668
                              &center_x, &center_y, &turn, radius_tolerance, spiral_abs_tolerance, SPIRAL_RELATIVE_TOLERANCE));
669
    }
670
671
    inverse_time_rate_arc(cx, cy, cz, center_x, center_y,
672
                          turn, end_x, end_y, end_z, block, settings);
673
674
675
    // the tool will end up in gamma direction from the programmed arc endpoint
676
    if TOOL_INSIDE_ARC(side, turn) {
677
        // tool inside the arc: ends up toward the center
678
        gamma = atan2((center_y - end_y), (center_x - end_x));
679
    } else {
680
        // outside: away from the center
681
        gamma = atan2((end_y - center_y), (end_x - center_x));
682
    }
683
684
    settings->cutter_comp_firstmove = false;
685
686
    comp_set_programmed(settings, end_x, end_y, end_z);
687
688
    // move endpoint to the compensated position.  This changes the radius and center.
689
    end_x += tool_radius * cos(gamma);
690
    end_y += tool_radius * sin(gamma);
691
692
    /* To find the new center:
693
       imagine a right triangle ABC with A being the endpoint of the
694
       compensated arc, B being the center of the compensated arc, C being
695
       the midpoint between start and end of the compensated arc. AB_ang
696
       is the direction of A->B.  A_ang is the angle of the triangle
697
       itself.  We need to find a new center for the compensated arc
698
       (point B). */
699
700
    double b_len = hypot(cy - end_y, cx - end_x) / 2.0;
701
    double AB_ang = atan2(center_y - end_y, center_x - end_x);
702
    double A_ang = atan2(cy - end_y, cx - end_x) - AB_ang;
703
704
    CHKS((fabs(cos(A_ang)) < TOLERANCE_EQUAL), NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP);
705
706
    double c_len = b_len/cos(A_ang);
707
708
    // center of the arc is c_len from end in direction AB
709
    center_x = end_x + c_len * cos(AB_ang);
710
    center_y = end_y + c_len * sin(AB_ang);
711
712
    /* center to endpoint distances matched before - they still should. */
713
    CHKS((fabs(hypot(center_x-end_x,center_y-end_y) -
714
              hypot(center_x-cx,center_y-cy)) > spiral_abs_tolerance),
715
        NCE_BUG_IN_TOOL_RADIUS_COMP);
716
717
    // need this move for lathes to move the tool origin first.  otherwise, the arc isn't an arc.
718
    if (settings->cutter_comp_orientation != 0 && settings->cutter_comp_orientation != 9) {
719
        enqueue_STRAIGHT_FEED(settings, block->line_number,
720
                              0, 0, 0,
721
                              cx, cy, cz,
722
                              AA_end, BB_end, CC_end, u_end, v_end, w_end);
723
        set_endpoint(cx, cy);
724
    }
725
726
    enqueue_ARC_FEED(settings, block->line_number,
727
                     find_turn(cx, cy, center_x, center_y, turn, end_x, end_y),
728
                     end_x, end_y, center_x, center_y, turn, end_z,
729
                     AA_end, BB_end, CC_end, u_end, v_end, w_end);
730
731
    comp_set_current(settings, end_x, end_y, end_z);
732
    settings->AA_current = AA_end;
733
    settings->BB_current = BB_end;
734
    settings->CC_current = CC_end;
735
    settings->u_current = u_end;
736
    settings->v_current = v_end;
737
    settings->w_current = w_end;
738
739
    return INTERP_OK;
740
}
741
742
/****************************************************************************/
743
744
/*! convert_arc_comp2
745
746
Returned Value: int
747
   If arc_data_ijk or arc_data_r returns an error code,
748
   this returns that code.
749
   If any of the following errors occurs, this returns the error code shown.
750
   Otherwise, it returns INTERP_OK.
751
   1. A concave corner is found: NCE_CONCAVE_CORNER_WITH_CUTTER_RADIUS_COMP
752
   2. The tool will not fit inside an arc:
753
      NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP
754
755
Side effects:
756
   This executes an arc command feed rate. If needed, at also generates
757
   an arc to go around a convex corner. It also updates the setting of
758
   the position of the tool point to the end point of the move.
759
Called by: convert_arc.
760
761
This function converts a helical or circular arc. The axis must be
762
parallel to the z-axis. This is called when cutter radius compensation
763
is on and this is not the first cut after the turning on.
764
765
If one or more rotary axes is moved in this block and an extra arc is
766
required to go around a sharp corner, all the rotary axis motion
767
occurs on the main arc and none on the extra arc.  An alternative
768
might be to distribute the rotary axis motion over the extra arc and
769
the programmed arc in proportion to their lengths.
770
771
If the Z-axis is moved in this block and an extra arc is required to
772
go around a sharp corner, all the Z-axis motion occurs on the main arc
773
and none on the extra arc.  An alternative might be to distribute the
774
Z-axis motion over the extra arc and the main arc in proportion to
775
their lengths.
776
777
*/
778
779
434
int Interp::convert_arc_comp2(int move,  //!< either G_2 (cw arc) or G_3 (ccw arc)
780
                              block_pointer block,       //!< pointer to a block of RS274/NGC instructions
781
                              setup_pointer settings,    //!< pointer to machine settings
782
                              double end_x,      //!< x-value at end of programmed (then actual) arc
783
                              double end_y,      //!< y-value at end of programmed (then actual) arc
784
                              double end_z,      //!< z-value at end of arc
785
                              double offset_x, double offset_y,
786
                              double AA_end,     //!< a-value at end of arc
787
                              double BB_end,     //!< b-value at end of arc
788
                              double CC_end,     //!< c-value at end of arc
789
                              double u, double v, double w) //!< uvw at end of arc
790
{
791
    double alpha;                 /* direction of tangent to start of arc */
792
    double arc_radius;
793
    double beta;                  /* angle between two tangents above */
794
    double centerx, centery;              /* center of arc */
795
    double delta;                 /* direction of radius from start of arc to center of arc */
796
    double gamma;                 /* direction of perpendicular to arc at end */
797
    double midx, midy;
798
    int side;
799
434
    double small = TOLERANCE_CONCAVE_CORNER;      /* angle for testing corners */
800
    double opx, opy, opz;
801
    double theta;                 /* direction of tangent to last cut */
802
    double tool_radius;
803
    int turn;                     /* number of full or partial circles CCW */
804
434
    int plane = settings->plane;
805
    double cx, cy, cz;
806
    double new_end_x, new_end_y;
807
808
434
    double spiral_abs_tolerance = (settings->length_units == CANON_UNITS_INCHES) ? settings->center_arc_radius_tolerance_inch : settings->center_arc_radius_tolerance_mm;
809
434
    double radius_tolerance = (settings->length_units == CANON_UNITS_INCHES) ? RADIUS_TOLERANCE_INCH : RADIUS_TOLERANCE_MM;
810
811
    /* find basic arc data: center_x, center_y, and turn */
812
813
434
    comp_get_programmed(settings, &opx, &opy, &opz);
814
434
    comp_get_current(settings, &cx, &cy, &cz);
815
816
817
434
    if (block->r_flag) {
818
        CHP(arc_data_r(move, plane, opx, opy, end_x, end_y,
819
                       block->r_number, block->p_flag? round_to_int(block->p_number): 1,
820
                       &centerx, &centery, &turn, radius_tolerance));
821
    } else {
822
434
        CHP(arc_data_ijk(move, plane,
823
                         opx, opy, end_x, end_y,
824
                         (settings->ijk_distance_mode == MODE_ABSOLUTE),
825
                         offset_x, offset_y, block->p_flag? round_to_int(block->p_number): 1,
826
                         &centerx, &centery, &turn, radius_tolerance, spiral_abs_tolerance, SPIRAL_RELATIVE_TOLERANCE));
827
    }
828
829
    inverse_time_rate_arc(opx, opy, opz, centerx, centery,
830
434
                          turn, end_x, end_y, end_z, block, settings);
831
832
434
    side = settings->cutter_comp_side;
833
434
    tool_radius = settings->cutter_comp_radius;   /* always is positive */
834
434
    arc_radius = hypot((centerx - end_x), (centery - end_y));
835
434
    theta = atan2(cy - opy, cx - opx);
836
434
    theta = (side == LEFT) ? (theta - M_PI_2l) : (theta + M_PI_2l);
837
434
    delta = atan2(centery - opy, centerx - opx);
838
434
    alpha = (move == G_3) ? (delta - M_PI_2l) : (delta + M_PI_2l);
839
434
    beta = (side == LEFT) ? (theta - alpha) : (alpha - theta);
840
841
    // normalize beta -90 to +270?
842
434
    beta = (beta > (1.5 * M_PIl)) ? (beta - (2 * M_PIl)) : (beta < -M_PI_2l) ? (beta + (2 * M_PIl)) : beta;
843
844
434
    if (((side == LEFT) && (move == G_3)) || ((side == RIGHT) && (move == G_2))) {
845
        // we are cutting inside the arc
846
287
        gamma = atan2((centery - end_y), (centerx - end_x));
847
287
        CHKS((arc_radius <= tool_radius),
848
            NCE_TOOL_RADIUS_NOT_LESS_THAN_ARC_RADIUS_WITH_COMP);
849
    } else {
850
147
        gamma = atan2((end_y - centery), (end_x - centerx));
851
147
        delta = (delta + M_PIl);
852
    }
853
854
    // move arc endpoint to the compensated position
855
434
    new_end_x = end_x + tool_radius * cos(gamma);
856
434
    new_end_y = end_y + tool_radius * sin(gamma);
857
858
841
    if (beta < -small ||
859
787
        beta > M_PIl + small ||
860
        // special detection for convex corner on tangent arc->arc (like atop the middle of "m" shape)
861
        // or tangent line->arc (atop "h" shape)
862
392
        (fabs(beta - M_PIl) < small && !TOOL_INSIDE_ARC(side, turn))
863
        ) {
864
        // concave
865
120
        if (qc().front().type != QARC_FEED) {
866
            // line->arc
867
34
            double cy = arc_radius * sin(beta - M_PI_2l);
868
            double toward_nominal;
869
            double dist_from_center;
870
            double angle_from_center;
871
872
34
            if TOOL_INSIDE_ARC(side, turn) {
873
                // tool is inside the arc
874
8
                dist_from_center = arc_radius - tool_radius;
875
8
                toward_nominal = cy + tool_radius;
876
8
                double l = toward_nominal / dist_from_center;
877
8
                CHKS((l > 1.0 || l < -1.0), _("Arc move in concave corner cannot be reached by the tool without gouging"));
878
8
                if(turn > 0) {
879
4
                    angle_from_center = theta + asin(l);
880
                } else {
881
4
                    angle_from_center = theta - asin(l);
882
                }
883
            } else {
884
26
                dist_from_center = arc_radius + tool_radius;
885
26
                toward_nominal = cy - tool_radius;
886
26
                double l = toward_nominal / dist_from_center;
887
26
                CHKS((l > 1.0 || l < -1.0), _("Arc move in concave corner cannot be reached by the tool without gouging"));
888
26
                if(turn > 0) {
889
10
                    angle_from_center = theta + M_PIl - asin(l);
890
                } else {
891
16
                    angle_from_center = theta + M_PIl + asin(l);
892
                }
893
            }
894
895
34
            midx = centerx + dist_from_center * cos(angle_from_center);
896
34
            midy = centery + dist_from_center * sin(angle_from_center);
897
898
34
            CHP(move_endpoint_and_flush(settings, midx, midy));
899
        } else {
900
            // arc->arc
901
52
            struct arc_feed &prev = qc().front().data.arc_feed;
902
26
            double oldrad = hypot(prev.center2 - prev.end2, prev.center1 - prev.end1);
903
            double newrad;
904
26
            if TOOL_INSIDE_ARC(side, turn) {
905
5
                newrad = arc_radius - tool_radius;
906
            } else {
907
21
                newrad = arc_radius + tool_radius;
908
            }
909
910
            double arc_cc, pullback, cc_dir, a;
911
26
            arc_cc = hypot(prev.center2 - centery, prev.center1 - centerx);
912
913
26
            CHKS((oldrad == 0 || arc_cc == 0), _("Arc to arc motion is invalid because the arcs have the same center"));
914
78
            a = (SQ(oldrad) + SQ(arc_cc) - SQ(newrad)) / (2 * oldrad * arc_cc);
915
916
26
            CHKS((a > 1.0 || a < -1.0), (_("Arc to arc motion makes a corner the compensated tool can't fit in without gouging")));
917
26
            pullback = acos(a);
918
26
            cc_dir = atan2(centery - prev.center2, centerx - prev.center1);
919
920
            double dir;
921
26
            if TOOL_INSIDE_ARC(side, prev.turn) {
922
10
                if(turn > 0)
923
5
                    dir = cc_dir + pullback;
924
                else
925
5
                    dir = cc_dir - pullback;
926
            } else {
927
16
                if(turn > 0)
928
9
                    dir = cc_dir - pullback;
929
                else
930
7
                    dir = cc_dir + pullback;
931
            }
932
933
26
            midx = prev.center1 + oldrad * cos(dir);
934
26
            midy = prev.center2 + oldrad * sin(dir);
935
936
26
            CHP(move_endpoint_and_flush(settings, midx, midy));
937
        }
938
        enqueue_ARC_FEED(settings, block->line_number,
939
                         find_turn(opx, opy, centerx, centery, turn, end_x, end_y),
940
                         new_end_x, new_end_y, centerx, centery, turn, end_z,
941
60
                         AA_end, BB_end, CC_end, u, v, w);
942
374
    } else if (beta > small) {           /* convex, two arcs needed */
943
60
        midx = opx + tool_radius * cos(delta);
944
60
        midy = opy + tool_radius * sin(delta);
945
60
        dequeue_canons(settings);
946
        enqueue_ARC_FEED(settings, block->line_number,
947
                         0.0, // doesn't matter since we won't move this arc's endpoint
948
                         midx, midy, opx, opy, ((side == LEFT) ? -1 : 1),
949
                         cz,
950
60
                         AA_end, BB_end, CC_end, u, v, w);
951
60
        dequeue_canons(settings);
952
60
        set_endpoint(midx, midy);
953
        enqueue_ARC_FEED(settings, block->line_number,
954
                         find_turn(opx, opy, centerx, centery, turn, end_x, end_y),
955
                         new_end_x, new_end_y, centerx, centery, turn, end_z,
956
60
                         AA_end, BB_end, CC_end, u, v, w);
957
    } else {                      /* convex, one arc needed */
958
314
        dequeue_canons(settings);
959
314
        set_endpoint(cx, cy);
960
        enqueue_ARC_FEED(settings, block->line_number,
961
                         find_turn(opx, opy, centerx, centery, turn, end_x, end_y),
962
                         new_end_x, new_end_y, centerx, centery, turn, end_z,
963
314
                         AA_end, BB_end, CC_end, u, v, w);
964
    }
965
966
434
    comp_set_programmed(settings, end_x, end_y, end_z);
967
434
    comp_set_current(settings, new_end_x, new_end_y, end_z);
968
434
    settings->AA_current = AA_end;
969
434
    settings->BB_current = BB_end;
970
434
    settings->CC_current = CC_end;
971
434
    settings->u_current = u;
972
434
    settings->v_current = v;
973
434
    settings->w_current = w;
974
975
434
    return INTERP_OK;
976
}
977
978
/****************************************************************************/
979
980
/*! convert_axis_offsets
981
982
Returned Value: int
983
   If any of the following errors occur, this returns the error code shown.
984
   Otherwise, it returns INTERP_OK.
985
   1. The function is called when cutter radius compensation is on:
986
      NCE_CANNOT_CHANGE_AXIS_OFFSETS_WITH_CUTTER_RADIUS_COMP
987
   2. The g_code argument is not G_52, G_92, G_92_1, G_92_2, or G_92_3
988
      NCE_BUG_CODE_NOT_IN_G52_G92_SERIES
989
990
Side effects:
991
   SET_G92_OFFSET is called, and the coordinate
992
   values for the axis offsets are reset. The coordinates of the
993
   current point are reset. Parameters may be set.
994
995
Called by: convert_modal_0.
996
997
The action of G92 is described in [NCMS, pages 10 - 11] and {Fanuc,
998
pages 61 - 63]. [NCMS] is ambiguous about the intent, but [Fanuc]
999
is clear. When G92 is executed, an offset of the origin is calculated
1000
so that the coordinates of the current point with respect to the moved
1001
origin are as specified on the line containing the G92. If an axis
1002
is not mentioned on the line, the coordinates of the current point
1003
are not changed. The execution of G92 results in an axis offset being
1004
calculated and saved for each of the six axes, and the axis offsets
1005
are always used when motion is specified with respect to absolute
1006
distance mode using any of the nine coordinate systems (those designated
1007
by G54 - G59.3). Thus all nine coordinate systems are affected by G92.
1008
1009
Being in incremental distance mode has no effect on the action of G92
1010
in this implementation. [NCMS] is not explicit about this, but it is
1011
implicit in the second sentence of [Fanuc, page 61].
1012
1013
The offset is the amount the origin must be moved so that the
1014
coordinate of the controlled point has the specified value. For
1015
example, if the current point is at X=4 in the currently specified
1016
coordinate system and the current X-axis offset is zero, then "G92 x7"
1017
causes the X-axis offset to be reset to -3.
1018
1019
Since a non-zero offset may be already be in effect when the G92 is
1020
called, that must be taken into account.
1021
1022
In addition to causing the axis offset values in the _setup model to be
1023
set, G52 and G92 set parameters 5211 to 5216 to the x,y,z,a,b,c axis
1024
offsets.
1025
1026
The action of G92.2 is described in [NCMS, page 12]. There is no
1027
equivalent command in [Fanuc]. G92.2 resets axis offsets to zero.
1028
G92.1, also included in [NCMS, page 12] (but the usage here differs
1029
slightly from the spec), is like G92.2, except that it also causes
1030
the axis offset parameters to be set to zero, whereas G92.2 does not
1031
zero out the parameters.
1032
1033
G92.3 is not in [NCMS]. It sets the axis offset values to the values
1034
given in the parameters.
1035
1036
*/
1037
1038
12
int Interp::convert_axis_offsets(int g_code,     //!< g_code being executed (must be in G_92 series)
1039
                                block_pointer block,    //!< pointer to a block of RS274/NGC instructions
1040
                                setup_pointer settings) //!< pointer to machine settings
1041
{
1042
  double *pars;                 /* short name for settings->parameters            */
1043
1044
12
  CHKS((settings->cutter_comp_side),      /* not "== true" */
1045
      NCE_CANNOT_CHANGE_AXIS_OFFSETS_WITH_CUTTER_RADIUS_COMP);
1046
12
  CHKS((block->a_flag && settings->a_axis_wrapped &&
1047
	(block->a_number <= -360.0 || block->a_number >= 360.0)),
1048
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")),
1049
       block->a_number, 'A');
1050
12
  CHKS((block->b_flag && settings->b_axis_wrapped &&
1051
	(block->b_number <= -360.0 || block->b_number >= 360.0)),
1052
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")),
1053
       block->b_number, 'B');
1054
12
  CHKS((block->c_flag && settings->c_axis_wrapped &&
1055
	(block->c_number <= -360.0 || block->c_number >= 360.0)),
1056
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")),
1057
       block->c_number, 'C');
1058
12
  pars = settings->parameters;
1059
12
  if ((g_code == G_52) || (g_code == G_92)) {
1060
5
      pars[5210] = 1.0;
1061
1062
5
      if (g_code == G_52) {
1063
2
	  if (block->x_flag) {
1064
2
	      settings->current_x += settings->axis_offset_x - block->x_number;
1065
2
	      settings->axis_offset_x = block->x_number;
1066
	  }
1067
1068
2
	  if (block->y_flag) {
1069
2
	      settings->current_y += settings->axis_offset_y - block->y_number;
1070
2
	      settings->axis_offset_y = block->y_number;
1071
	  }
1072
1073
2
	  if (block->z_flag) {
1074
	      settings->current_z += settings->axis_offset_z - block->z_number;
1075
	      settings->axis_offset_z = block->z_number;
1076
	  }
1077
2
	  if (block->a_flag) {
1078
	      settings->AA_current += settings->AA_axis_offset - block->a_number;
1079
	      settings->AA_axis_offset = block->a_number;
1080
	  }
1081
2
	  if (block->b_flag) {
1082
	      settings->BB_current += settings->BB_axis_offset - block->b_number;
1083
	      settings->BB_axis_offset = block->b_number;
1084
	  }
1085
2
	  if (block->c_flag) {
1086
	      settings->CC_current += settings->CC_axis_offset - block->c_number;
1087
	      settings->CC_axis_offset = block->c_number;
1088
	  }
1089
2
	  if (block->u_flag) {
1090
	      settings->u_current += settings->u_axis_offset - block->u_number;
1091
	      settings->u_axis_offset = block->u_number;
1092
	  }
1093
2
	  if (block->v_flag) {
1094
	      settings->v_current += settings->v_axis_offset - block->v_number;
1095
	      settings->v_axis_offset = block->v_number;
1096
	  }
1097
2
	  if (block->w_flag) {
1098
	      settings->w_current += settings->w_axis_offset - block->w_number;
1099
	      settings->w_axis_offset = block->w_number;
1100
	  }
1101
1102
      } else {
1103
3
	  if (block->x_flag) {
1104
	      settings->axis_offset_x =
1105
3
		  (settings->current_x + settings->axis_offset_x - block->x_number);
1106
3
	      settings->current_x = block->x_number;
1107
	  }
1108
1109
3
	  if (block->y_flag) {
1110
	      settings->axis_offset_y =
1111
3
		  (settings->current_y + settings->axis_offset_y - block->y_number);
1112
3
	      settings->current_y = block->y_number;
1113
	  }
1114
1115
3
	  if (block->z_flag) {
1116
	      settings->axis_offset_z =
1117
2
		  (settings->current_z + settings->axis_offset_z - block->z_number);
1118
2
	      settings->current_z = block->z_number;
1119
	  }
1120
3
	  if (block->a_flag) {
1121
	      settings->AA_axis_offset = (settings->AA_current +
1122
					  settings->AA_axis_offset - block->a_number);
1123
	      settings->AA_current = block->a_number;
1124
	  }
1125
3
	  if (block->b_flag) {
1126
	      settings->BB_axis_offset = (settings->BB_current +
1127
					  settings->BB_axis_offset - block->b_number);
1128
	      settings->BB_current = block->b_number;
1129
	  }
1130
3
	  if (block->c_flag) {
1131
	      settings->CC_axis_offset = (settings->CC_current +
1132
					  settings->CC_axis_offset - block->c_number);
1133
	      settings->CC_current = block->c_number;
1134
	  }
1135
3
	  if (block->u_flag) {
1136
	      settings->u_axis_offset = (settings->u_current +
1137
					 settings->u_axis_offset - block->u_number);
1138
	      settings->u_current = block->u_number;
1139
	  }
1140
3
	  if (block->v_flag) {
1141
	      settings->v_axis_offset = (settings->v_current +
1142
					 settings->v_axis_offset - block->v_number);
1143
	      settings->v_current = block->v_number;
1144
	  }
1145
3
	  if (block->w_flag) {
1146
	      settings->w_axis_offset = (settings->w_current +
1147
					 settings->w_axis_offset - block->w_number);
1148
	      settings->w_current = block->w_number;
1149
	  }
1150
      }
1151
1152
    SET_G92_OFFSET(settings->axis_offset_x,
1153
                   settings->axis_offset_y,
1154
                   settings->axis_offset_z,
1155
                   settings->AA_axis_offset,
1156
                   settings->BB_axis_offset,
1157
                   settings->CC_axis_offset,
1158
                   settings->u_axis_offset,
1159
                   settings->v_axis_offset,
1160
5
                   settings->w_axis_offset);
1161
1162
5
    pars[5211] = PROGRAM_TO_USER_LEN(settings->axis_offset_x);
1163
5
    pars[5212] = PROGRAM_TO_USER_LEN(settings->axis_offset_y);
1164
5
    pars[5213] = PROGRAM_TO_USER_LEN(settings->axis_offset_z);
1165
5
    pars[5214] = PROGRAM_TO_USER_ANG(settings->AA_axis_offset);
1166
5
    pars[5215] = PROGRAM_TO_USER_ANG(settings->BB_axis_offset);
1167
5
    pars[5216] = PROGRAM_TO_USER_ANG(settings->CC_axis_offset);
1168
5
    pars[5217] = PROGRAM_TO_USER_LEN(settings->u_axis_offset);
1169
5
    pars[5218] = PROGRAM_TO_USER_LEN(settings->v_axis_offset);
1170
5
    pars[5219] = PROGRAM_TO_USER_LEN(settings->w_axis_offset);
1171
1172
7
  } else if ((g_code == G_92_1) || (g_code == G_92_2)) {
1173
5
    pars[5210] = 0.0;
1174
5
    settings->current_x = settings->current_x + settings->axis_offset_x;
1175
5
    settings->current_y = settings->current_y + settings->axis_offset_y;
1176
5
    settings->current_z = settings->current_z + settings->axis_offset_z;
1177
5
    settings->AA_current = (settings->AA_current + settings->AA_axis_offset);
1178
5
    settings->BB_current = (settings->BB_current + settings->BB_axis_offset);
1179
5
    settings->CC_current = (settings->CC_current + settings->CC_axis_offset);
1180
5
    settings->u_current = (settings->u_current + settings->u_axis_offset);
1181
5
    settings->v_current = (settings->v_current + settings->v_axis_offset);
1182
5
    settings->w_current = (settings->w_current + settings->w_axis_offset);
1183
1184
5
    SET_G92_OFFSET(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1185
1186
5
    settings->axis_offset_x = 0.0;
1187
5
    settings->axis_offset_y = 0.0;
1188
5
    settings->axis_offset_z = 0.0;
1189
5
    settings->AA_axis_offset = 0.0;
1190
5
    settings->BB_axis_offset = 0.0;
1191
5
    settings->CC_axis_offset = 0.0;
1192
5
    settings->u_axis_offset = 0.0;
1193
5
    settings->v_axis_offset = 0.0;
1194
5
    settings->w_axis_offset = 0.0;
1195
5
    if (g_code == G_92_1) {
1196
3
      pars[5211] = 0.0;
1197
3
      pars[5212] = 0.0;
1198
3
      pars[5213] = 0.0;
1199
3
      pars[5214] = 0.0;
1200
3
      pars[5215] = 0.0;
1201
3
      pars[5216] = 0.0;
1202
3
      pars[5217] = 0.0;
1203
3
      pars[5218] = 0.0;
1204
3
      pars[5219] = 0.0;
1205
    }
1206
2
  } else if (g_code == G_92_3) {
1207
2
    pars[5210] = 1.0;
1208
    settings->current_x =
1209
2
      settings->current_x + settings->axis_offset_x - USER_TO_PROGRAM_LEN(pars[5211]);
1210
    settings->current_y =
1211
2
      settings->current_y + settings->axis_offset_y - USER_TO_PROGRAM_LEN(pars[5212]);
1212
    settings->current_z =
1213
2
      settings->current_z + settings->axis_offset_z - USER_TO_PROGRAM_LEN(pars[5213]);
1214
    settings->AA_current =
1215
2
      settings->AA_current + settings->AA_axis_offset - USER_TO_PROGRAM_ANG(pars[5214]);
1216
    settings->BB_current =
1217
2
      settings->BB_current + settings->BB_axis_offset - USER_TO_PROGRAM_ANG(pars[5215]);
1218
    settings->CC_current =
1219
2
      settings->CC_current + settings->CC_axis_offset - USER_TO_PROGRAM_ANG(pars[5216]);
1220
    settings->u_current =
1221
2
      settings->u_current + settings->u_axis_offset - USER_TO_PROGRAM_LEN(pars[5217]);
1222
    settings->v_current =
1223
2
      settings->v_current + settings->v_axis_offset - USER_TO_PROGRAM_LEN(pars[5218]);
1224
    settings->w_current =
1225
2
      settings->w_current + settings->w_axis_offset - USER_TO_PROGRAM_LEN(pars[5219]);
1226
1227
2
    settings->axis_offset_x = USER_TO_PROGRAM_LEN(pars[5211]);
1228
2
    settings->axis_offset_y = USER_TO_PROGRAM_LEN(pars[5212]);
1229
2
    settings->axis_offset_z = USER_TO_PROGRAM_LEN(pars[5213]);
1230
2
    settings->AA_axis_offset = USER_TO_PROGRAM_ANG(pars[5214]);
1231
2
    settings->BB_axis_offset = USER_TO_PROGRAM_ANG(pars[5215]);
1232
2
    settings->CC_axis_offset = USER_TO_PROGRAM_ANG(pars[5216]);
1233
2
    settings->u_axis_offset = USER_TO_PROGRAM_LEN(pars[5217]);
1234
2
    settings->v_axis_offset = USER_TO_PROGRAM_LEN(pars[5218]);
1235
2
    settings->w_axis_offset = USER_TO_PROGRAM_LEN(pars[5219]);
1236
1237
    SET_G92_OFFSET(settings->axis_offset_x,
1238
                   settings->axis_offset_y,
1239
                   settings->axis_offset_z,
1240
                   settings->AA_axis_offset,
1241
                   settings->BB_axis_offset,
1242
                   settings->CC_axis_offset,
1243
                   settings->u_axis_offset,
1244
                   settings->v_axis_offset,
1245
2
                   settings->w_axis_offset);
1246
  } else
1247
    ERS(NCE_BUG_CODE_NOT_IN_G52_G92_SERIES);
1248
1249
  return INTERP_OK;
1250
}
1251
1252
#define VAL_LEN 30
1253
1254
123
int Interp::convert_param_comment(char *comment, char *expanded, int len)
1255
{
1256
    int i;
1257
    char param[LINELEN+1];
1258
    int paramNumber;
1259
    int stat;
1260
    double value;
1261
    char valbuf[VAL_LEN]; // max double length + room
1262
    char *v;
1263
    int found;
1264
1265
2887
    while(*comment)
1266
    {
1267
2641
        if(*comment == '#')
1268
        {
1269
315
            found = 0;
1270
315
            logDebug("a parameter");
1271
1272
            // skip over the '#'
1273
315
            comment++;
1274
315
            CHKS((0 == *comment), NCE_NAMED_PARAMETER_NOT_TERMINATED);
1275
1276
315
            if(isdigit(*comment))  // is this numeric param?
1277
            {
1278
288
                logDebug("numeric parameter");
1279
1150
                for(i=0; isdigit(*comment)&& (i<LINELEN); i++)
1280
                {
1281
1150
                    param[i] = *comment++;
1282
                }
1283
288
                param[i] = 0;
1284
288
                paramNumber = atoi(param);
1285
288
                if((paramNumber >= 0) &&
1286
                   (paramNumber < RS274NGC_MAX_PARAMETERS))
1287
                {
1288
288
                    value = _setup.parameters[paramNumber];
1289
288
                    found = 1;
1290
                }
1291
            }
1292
27
            else if(*comment == '<')
1293
            {
1294
27
                logDebug("name parameter");
1295
                // this is a name parameter
1296
                // skip over the '<'
1297
27
                comment++;
1298
27
                CHKS((0 == *comment), NCE_NAMED_PARAMETER_NOT_TERMINATED);
1299
1300
188
                for(i=0; (')' != *comment) &&
1301
188
                        (i<LINELEN) && (0 != *comment);)
1302
                {
1303
188
                    if('>' == *comment)
1304
                    {
1305
                        break;     // done
1306
                    }
1307
161
                    if(isspace(*comment)) // skip space inside the param
1308
                    {
1309
                        comment++;
1310
                        continue;
1311
                    }
1312
                    else
1313
                    {
1314
		        // if tolower is a macro, may need this int
1315
161
		        int c = *comment++;
1316
161
			if (FEATURE(NO_DOWNCASE_OWORD))
1317
35
			    param[i] = c;
1318
			else
1319
126
			    param[i] = tolower(c);
1320
161
                        i++;
1321
                    }
1322
                }
1323
27
                if('>' != *comment)
1324
                {
1325
                    ERS(NCE_NAMED_PARAMETER_NOT_TERMINATED);
1326
                }
1327
                else
1328
                {
1329
27
                    comment++;
1330
                }
1331
1332
                // terminate the name
1333
27
                param[i] = 0;
1334
1335
                // now lookup the name
1336
27
                find_named_param(param, &stat, &value);
1337
27
                if(stat)
1338
                {
1339
26
                    found = 1;
1340
                }
1341
            }
1342
            else
1343
            {
1344
                // neither numeric or name
1345
                logDebug("neither numeric nor name");
1346
                // just store the '#'
1347
                *expanded++ = '#';
1348
1349
                CHKS((*comment == 0), NCE_NAMED_PARAMETER_NOT_TERMINATED);
1350
                continue;
1351
            }
1352
1353
            // we have a parameter -- now insert it
1354
            // we have the value
1355
315
            if(found)
1356
            {
1357
		// avoid -0.0/0.0 issues
1358
628
		double pvalue = equal(value, 0.0) ? 0.0 : value;
1359
314
                int n = snprintf(valbuf, VAL_LEN, "%lf", pvalue);
1360
314
                bool fail = (n >= VAL_LEN || n < 0);
1361
314
                if(fail)
1362
                    strcpy(valbuf, "######");
1363
1364
            }
1365
            else
1366
            {
1367
1
                strcpy(valbuf, "######");
1368
            }
1369
315
            logDebug("found:%d value:|%s|", found, valbuf);
1370
1371
            v = valbuf;
1372
2963
            while(*v)
1373
            {
1374
2648
                *expanded++ = *v++;
1375
            }
1376
        }
1377
        else  // not a '#'
1378
        {
1379
2326
            *expanded++ = *comment++;
1380
        }
1381
    }
1382
123
    *expanded = 0; // the final nul
1383
1384
123
    return INTERP_OK;
1385
}
1386
1387
/****************************************************************************/
1388
1389
/*! convert_comment
1390
1391
Returned Value: int (INTERP_OK)
1392
1393
Side effects:
1394
   The message function is called if the string starts with "MSG,".
1395
   Otherwise, the comment function is called.
1396
1397
Called by: execute_block
1398
1399
To be a message, the first four characters of the comment after the
1400
opening left parenthesis must be "MSG,", ignoring the case of the
1401
letters and allowing spaces or tabs anywhere before the comma (to make
1402
the treatment of case and white space consistent with how it is
1403
handled elsewhere).
1404
1405
Messages are not provided for in [NCMS]. They are implemented here as a
1406
subtype of comment. This is an extension to the rs274NGC language.
1407
1408
*/
1409
1410
1030
static int streq(char *s1, char *s2) {
1411
1030
    return !strcmp(s1, s2);
1412
}
1413
1414
10715
static int startswith(char *haystack, char *needle) {
1415
10715
    return !strncmp(haystack, needle, strlen(needle));
1416
}
1417
1418
1175
int Interp::convert_comment(char *comment, bool enqueue)       //!< string with comment
1419
{
1420
  enum
1421
  { LC_SIZE = 256, EX_SIZE = 2*LC_SIZE};            // 256 from comment[256] in rs274ngc.hh
1422
  char lc[LC_SIZE+1];
1423
  char expanded[EX_SIZE+1];
1424
1175
  char MSG_STR[] = "msg,";
1425
1426
  //!!!KL add two -- debug => same as msg
1427
  //!!!KL         -- print => goes to stderr
1428
1175
  char DEBUG_STR[] = "debug,";
1429
1175
  char PRINT_STR[] = "print,";
1430
1175
  char LOG_STR[] = "log,";
1431
1175
  char LOGOPEN_STR[] = "logopen,";
1432
1175
  char LOGAPPEND_STR[] = "logappend,";
1433
1175
  char LOGCLOSE_STR[] = "logclose";
1434
1175
  char PY_STR[] = "py,";
1435
1175
  char PYRUN_STR[] = "pyrun,";
1436
1175
  char PYRELOAD_STR[] = "pyreload";
1437
1175
  char ABORT_STR[] = "abort,";
1438
  int m, n, start;
1439
1440
  // step over leading white space in comment
1441
1175
  m = 0;
1442
2468
  while (isspace(comment[m]))
1443
118
    m++;
1444
  start = m;
1445
  // copy lowercase comment to lc[]
1446
32625
  for (n = 0; n < LC_SIZE && comment[m] != 0; m++, n++) {
1447
32625
    lc[n] = tolower(comment[m]);
1448
  }
1449
1175
  lc[n] = 0;                    // null terminate
1450
1451
  // compare with MSG, SYSTEM, DEBUG, PRINT
1452
1175
  if (startswith(lc, MSG_STR)) {
1453
3
    MESSAGE(comment + start + strlen(MSG_STR));
1454
    return INTERP_OK;
1455
  }
1456
1172
  else if (startswith(lc, DEBUG_STR))
1457
  {
1458
97
      convert_param_comment(comment+start+strlen(DEBUG_STR), expanded,
1459
97
                            EX_SIZE);
1460
97
      if (_setup.parameters[5599] > 0.0)
1461
97
	  MESSAGE(expanded);
1462
      return INTERP_OK;
1463
  }
1464
1075
  else if (startswith(lc, PRINT_STR))
1465
  {
1466
25
      convert_param_comment(comment+start+strlen(PRINT_STR), expanded,
1467
25
                            EX_SIZE);
1468
25
      fprintf(stdout, "%s\n", expanded);
1469
25
      fflush(stdout);
1470
      return INTERP_OK;
1471
  }
1472
1050
  else if (startswith(lc, LOG_STR))
1473
  {
1474
      convert_param_comment(comment+start+strlen(LOG_STR), expanded,
1475
                            EX_SIZE);
1476
      LOG(expanded);
1477
      return INTERP_OK;
1478
  }
1479
1050
  else if (startswith(lc, LOGOPEN_STR))
1480
  {
1481
      LOGOPEN(comment + start + strlen(LOGOPEN_STR));
1482
      return INTERP_OK;
1483
  }
1484
1050
  else if (startswith(lc, LOGAPPEND_STR))
1485
  {
1486
      LOGAPPEND(comment + start + strlen(LOGAPPEND_STR));
1487
      return INTERP_OK;
1488
  }
1489
1050
  else if (startswith(lc, PY_STR))
1490
  {
1491
19
      return py_execute(comment + start + strlen(PY_STR), false);
1492
  }
1493
1031
  else if (startswith(lc, PYRUN_STR))
1494
  {
1495
      return py_execute(comment + start + strlen(PYRUN_STR), true);
1496
  }
1497
1031
  else if (startswith(lc, PYRELOAD_STR))
1498
  {
1499
      return py_reload();
1500
  }
1501
1031
  else if (startswith(lc, ABORT_STR))
1502
  {
1503
1
      convert_param_comment(comment+start+strlen(ABORT_STR), expanded,
1504
1
                            EX_SIZE);
1505
1
      setSavedError(expanded); // avoid printf interpretation
1506
      return INTERP_ERROR;
1507
  }
1508
1030
  else if (streq(lc, LOGCLOSE_STR))
1509
  {
1510
      LOGCLOSE();
1511
      return INTERP_OK;
1512
  }
1513
  // else it's a real comment
1514
1030
  if (enqueue)
1515
886
      enqueue_COMMENT(comment + start);
1516
  return INTERP_OK;
1517
}
1518
1519
/****************************************************************************/
1520
1521
/*! convert_control_mode
1522
1523
Returned Value: int
1524
   If any of the following errors occur, this returns the error code shown.
1525
   Otherwise, it returns INTERP_OK.
1526
   1. g_code isn't G_61, G_61_1, G_64 : NCE_BUG_CODE_NOT_G61_G61_1_OR_G64
1527
1528
Side effects: See below
1529
1530
Called by: convert_g.
1531
1532
The interpreter switches the machine settings to indicate the
1533
control mode (CANON_EXACT_STOP, CANON_EXACT_PATH or CANON_CONTINUOUS)
1534
1535
A call is made to SET_MOTION_CONTROL_MODE(CANON_XXX), where CANON_XXX is
1536
CANON_EXACT_PATH if g_code is G_61, CANON_EXACT_STOP if g_code is G_61_1,
1537
and CANON_CONTINUOUS if g_code is G_64.
1538
1539
Setting the control mode to CANON_EXACT_STOP on G_61 would correspond
1540
more closely to the meaning of G_61 as given in [NCMS, page 40], but
1541
CANON_EXACT_PATH has the advantage that the tool does not stop if it
1542
does not have to, and no evident disadvantage compared to
1543
CANON_EXACT_STOP, so it is being used for G_61. G_61_1 is not defined
1544
in [NCMS], so it is available and is used here for setting the control
1545
mode to CANON_EXACT_STOP.
1546
1547
It is OK to call SET_MOTION_CONTROL_MODE(CANON_XXX) when CANON_XXX is
1548
already in force.
1549
1550
*/
1551
1552
7
int Interp::convert_control_mode(int g_code,     //!< g_code being executed (G_61, G61_1, || G_64)
1553
				double tolerance,    //tolerance for the path following in G64
1554
				double naivecam_tolerance,    //tolerance for the naivecam
1555
                                setup_pointer settings) //!< pointer to machine settings
1556
{
1557
7
  CHKS((settings->cutter_comp_side),
1558
       (_("Cannot change control mode with cutter radius compensation on")));
1559
7
  if (g_code == G_61) {
1560
1
    SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH, 0);
1561
1
    settings->control_mode = CANON_EXACT_PATH;
1562
6
  } else if (g_code == G_61_1) {
1563
1
    SET_MOTION_CONTROL_MODE(CANON_EXACT_STOP, 0);
1564
1
    settings->control_mode = CANON_EXACT_STOP;
1565
5
  } else if (g_code == G_64) {
1566
5
	if (tolerance >= 0) {
1567
2
	    SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, tolerance);
1568
	} else {
1569
3
	    SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, 0);
1570
	}
1571
5
	if (naivecam_tolerance >= 0) {
1572
1
	    SET_NAIVECAM_TOLERANCE(naivecam_tolerance);
1573
4
	} else if (tolerance >= 0) {
1574
1
	    SET_NAIVECAM_TOLERANCE(tolerance);   // if no naivecam_tolerance specified use same for both
1575
	} else {
1576
3
	    SET_NAIVECAM_TOLERANCE(0);
1577
	}
1578
5
    settings->control_mode = CANON_CONTINUOUS;
1579
  } else
1580
    ERS(NCE_BUG_CODE_NOT_G61_G61_1_OR_G64);
1581
  return INTERP_OK;
1582
}
1583
1584
/****************************************************************************/
1585
1586
/*! convert_coordinate_system
1587
1588
Returned Value: int
1589
   If any of the following errors occur, this returns the error code shown.
1590
   Otherwise, it returns INTERP_OK.
1591
   1. The value of the g_code argument is not 540, 550, 560, 570, 580, 590
1592
      591, 592, or 593:
1593
      NCE_BUG_CODE_NOT_IN_RANGE_G54_TO_G593
1594
1595
Side effects:
1596
   If the coordinate system selected by the g_code is not already in
1597
   use, the canonical program coordinate system axis offset values are
1598
   reset and the coordinate values of the current point are reset.
1599
1600
Called by: convert_g.
1601
1602
COORDINATE SYSTEMS (involves g10, g53, g54 - g59.3, g92)
1603
1604
The canonical machining functions view of coordinate systems is:
1605
1. There are two coordinate systems: absolute and program.
1606
2. All coordinate values are given in terms of the program coordinate system.
1607
3. The offsets of the program coordinate system may be reset.
1608
1609
The RS274/NGC view of coordinate systems, as given in section 3.2
1610
of [NCMS] is:
1611
1. there are ten coordinate systems: absolute and 9 program. The
1612
   program coordinate systems are numbered 1 to 9.
1613
2. you can switch among the 9 but not to the absolute one. G54
1614
   selects coordinate system 1, G55 selects 2, and so on through
1615
   G56, G57, G58, G59, G59.1, G59.2, and G59.3.
1616
3. you can set the offsets of the 9 program coordinate systems
1617
   using G10 L2 Pn (n is the number of the coordinate system) with
1618
   values for the axes in terms of the absolute coordinate system.
1619
4. the first one of the 9 program coordinate systems is the default.
1620
5. data for coordinate systems is stored in parameters [NCMS, pages 59 - 60].
1621
6. g53 means to interpret coordinate values in terms of the absolute
1622
   coordinate system for the one block in which g53 appears.
1623
7. You can offset the current coordinate system using g92. This offset
1624
   will then apply to all nine program coordinate systems.
1625
1626
The approach used in the interpreter mates the canonical and NGC views
1627
of coordinate systems as follows:
1628
1629
During initialization, data from the parameters for the first NGC
1630
coordinate system is used in a SET_ORIGIN_OFFSETS function call and
1631
origin_index in the machine model is set to 1.
1632
1633
If a g_code in the range g54 - g59.3 is encountered in an NC program,
1634
the data from the appropriate NGC coordinate system is copied into the
1635
origin offsets used by the interpreter, a SET_ORIGIN_OFFSETS function
1636
call is made, and the current position is reset.
1637
1638
If a g10 is encountered, the convert_setup function is called to reset
1639
the offsets of the program coordinate system indicated by the P number
1640
given in the same block.
1641
1642
If a g53 is encountered, the axis values given in that block are used
1643
to calculate what the coordinates are of that point in the current
1644
coordinate system, and a STRAIGHT_TRAVERSE or STRAIGHT_FEED function
1645
call to that point using the calculated values is made. No offset
1646
values are changed.
1647
1648
If a g92 is encountered, that is handled by the convert_axis_offsets
1649
function. A g92 results in an axis offset for each axis being calculated
1650
and stored in the machine model. The axis offsets are applied to all
1651
nine coordinate systems. Axis offsets are initialized to zero.
1652
1653
*/
1654
1655
351
void Interp::rotate(double *x, double *y, double theta) {
1656
    double xx, yy;
1657
351
    double t = D2R(theta);
1658
351
    xx = *x * cos(t) - *y * sin(t);
1659
351
    yy = *x * sin(t) + *y * cos(t);
1660
351
    *x = xx;
1661
351
    *y = yy;
1662
351
}
1663
1664
36
int Interp::convert_coordinate_system(int g_code,        //!< g_code called (must be one listed above)
1665
                                     setup_pointer settings)    //!< pointer to machine settings
1666
{
1667
  int origin;
1668
  double *parameters;
1669
1670
36
  CHKS((settings->cutter_comp_side),
1671
       (_("Cannot change coordinate systems with cutter radius compensation on")));
1672
36
  parameters = settings->parameters;
1673
36
  switch (g_code) {
1674
  case G_54:
1675
    origin = 1;
1676
    break;
1677
  case G_55:
1678
5
    origin = 2;
1679
5
    break;
1680
  case G_56:
1681
4
    origin = 3;
1682
4
    break;
1683
  case G_57:
1684
3
    origin = 4;
1685
3
    break;
1686
  case G_58:
1687
3
    origin = 5;
1688
3
    break;
1689
  case G_59:
1690
3
    origin = 6;
1691
3
    break;
1692
  case G_59_1:
1693
3
    origin = 7;
1694
3
    break;
1695
  case G_59_2:
1696
3
    origin = 8;
1697
3
    break;
1698
  case G_59_3:
1699
3
    origin = 9;
1700
3
    break;
1701
  default:
1702
    ERS(NCE_BUG_CODE_NOT_IN_RANGE_G54_TO_G593);
1703
  }
1704
1705
36
  if (origin == settings->origin_index) {       /* already using this origin */
1706
#ifdef DEBUG_EMC
1707
6
    enqueue_COMMENT("interpreter: continuing to use same coordinate system");
1708
#endif
1709
6
    return INTERP_OK;
1710
  }
1711
1712
  // move the current point into the new system
1713
  find_current_in_system(settings, origin,
1714
                         &settings->current_x, &settings->current_y, &settings->current_z,
1715
                         &settings->AA_current, &settings->BB_current, &settings->CC_current,
1716
30
                         &settings->u_current, &settings->v_current, &settings->w_current);
1717
1718
  // remember that this is new system
1719
30
  settings->origin_index = origin;
1720
30
  parameters[5220] = (double) origin;
1721
1722
  // load the origin of the newly-selected system
1723
30
  settings->origin_offset_x = USER_TO_PROGRAM_LEN(parameters[5201 + (origin * 20)]);
1724
30
  settings->origin_offset_y = USER_TO_PROGRAM_LEN(parameters[5202 + (origin * 20)]);
1725
30
  settings->origin_offset_z = USER_TO_PROGRAM_LEN(parameters[5203 + (origin * 20)]);
1726
30
  settings->AA_origin_offset = USER_TO_PROGRAM_ANG(parameters[5204 + (origin * 20)]);
1727
30
  settings->BB_origin_offset = USER_TO_PROGRAM_ANG(parameters[5205 + (origin * 20)]);
1728
30
  settings->CC_origin_offset = USER_TO_PROGRAM_ANG(parameters[5206 + (origin * 20)]);
1729
30
  settings->u_origin_offset = USER_TO_PROGRAM_LEN(parameters[5207 + (origin * 20)]);
1730
30
  settings->v_origin_offset = USER_TO_PROGRAM_LEN(parameters[5208 + (origin * 20)]);
1731
30
  settings->w_origin_offset = USER_TO_PROGRAM_LEN(parameters[5209 + (origin * 20)]);
1732
30
  settings->rotation_xy = parameters[5210 + (origin * 20)];
1733
1734
  SET_G5X_OFFSET(origin,
1735
                 settings->origin_offset_x,
1736
                 settings->origin_offset_y,
1737
                 settings->origin_offset_z,
1738
                 settings->AA_origin_offset,
1739
                 settings->BB_origin_offset,
1740
                 settings->CC_origin_offset,
1741
                 settings->u_origin_offset,
1742
                 settings->v_origin_offset,
1743
30
                 settings->w_origin_offset);
1744
1745
  SET_G92_OFFSET(settings->axis_offset_x,
1746
                 settings->axis_offset_y,
1747
                 settings->axis_offset_z,
1748
                 settings->AA_axis_offset,
1749
                 settings->BB_axis_offset,
1750
                 settings->CC_axis_offset,
1751
                 settings->u_axis_offset,
1752
                 settings->v_axis_offset,
1753
30
                 settings->w_axis_offset);
1754
1755
30
  SET_XY_ROTATION(settings->rotation_xy);
1756
30
  return INTERP_OK;
1757
}
1758
1759
/****************************************************************************/
1760
1761
/*! convert_cutter_compensation
1762
1763
Returned Value: int
1764
   If convert_cutter_compensation_on or convert_cutter_compensation_off
1765
      is called and returns an error code, this returns that code.
1766
   If any of the following errors occur, this returns the error shown.
1767
   Otherwise, it returns INTERP_OK.
1768
   1. g_code is not G_40, G_41, or G_42:
1769
      NCE_BUG_CODE_NOT_G40_G41_OR_G42
1770
1771
Side effects:
1772
   The value of cutter_comp_side in the machine model mode is
1773
   set to RIGHT, LEFT, or false. The currently active tool table index in
1774
   the machine model (which is the index of the slot whose diameter
1775
   value is used in cutter radius compensation) is updated.
1776
1777
Since cutter radius compensation is performed in the interpreter, no
1778
call is made to any canonical function regarding cutter radius compensation.
1779
1780
Called by: convert_g
1781
1782
*/
1783
1784
149
int Interp::convert_cutter_compensation(int g_code,      //!< must be G_40, G_41, or G_42
1785
                                       block_pointer block,     //!< pointer to a block of RS274 instructions
1786
                                       setup_pointer settings)  //!< pointer to machine settings
1787
{
1788
1789
149
  if (g_code == G_40) {
1790
87
    CHP(convert_cutter_compensation_off(settings));
1791
62
  } else if (g_code == G_41) {
1792
    CHP(convert_cutter_compensation_on(LEFT, block, settings));
1793
62
  } else if (g_code == G_42) {
1794
1
    CHP(convert_cutter_compensation_on(RIGHT, block, settings));
1795
61
  } else if (g_code == G_41_1) {
1796
58
    CHP(convert_cutter_compensation_on(LEFT, block, settings));
1797
3
  } else if (g_code == G_42_1) {
1798
3
    CHP(convert_cutter_compensation_on(RIGHT, block, settings));
1799
  } else
1800
    ERS("BUG: Code not G40, G41, G41.1, G42, G42.2");
1801
1802
  return INTERP_OK;
1803
}
1804
1805
/****************************************************************************/
1806
1807
/*! convert_cutter_compensation_off
1808
1809
Returned Value: int (INTERP_OK)
1810
1811
Side effects:
1812
   A comment is made that cutter radius compensation is turned off.
1813
   The machine model of the cutter radius compensation mode is set to false.
1814
   The value of cutter_comp_firstmove in the machine model is set to true.
1815
     This serves as a flag when cutter radius compensation is
1816
     turned on again.
1817
1818
Called by: convert_cutter_compensation
1819
1820
*/
1821
1822
87
int Interp::convert_cutter_compensation_off(setup_pointer settings)      //!< pointer to machine settings
1823
{
1824
#ifdef DEBUG_EMC
1825
87
  enqueue_COMMENT("interpreter: cutter radius compensation off");
1826
#endif
1827
87
  if(settings->cutter_comp_side && settings->cutter_comp_radius > 0.0) {
1828
      double cx, cy, cz;
1829
58
      comp_get_current(settings, &cx, &cy, &cz);
1830
58
      CHP(move_endpoint_and_flush(settings, cx, cy));
1831
58
      dequeue_canons(settings);
1832
58
      settings->current_x = settings->program_x;
1833
58
      settings->current_y = settings->program_y;
1834
58
      settings->current_z = settings->program_z;
1835
58
      settings->arc_not_allowed = true;
1836
  }
1837
87
  settings->cutter_comp_side = false;
1838
87
  settings->cutter_comp_firstmove = true;
1839
87
  return INTERP_OK;
1840
}
1841
1842
/****************************************************************************/
1843
1844
/*! convert_cutter_compensation_on
1845
1846
Returned Value: int
1847
   If any of the following errors occur, this returns the error code shown.
1848
   Otherwise, it returns INTERP_OK.
1849
   1. The selected plane is not the XY plane:
1850
      NCE_CANNOT_TURN_CUTTER_RADIUS_COMP_ON_OUT_OF_XY_PLANE
1851
   2. Cutter radius compensation is already on:
1852
      NCE_CANNOT_TURN_CUTTER_RADIUS_COMP_ON_WHEN_ON
1853
1854
Side effects:
1855
   A COMMENT function call is made (conditionally) saying that the
1856
   interpreter is switching mode so that cutter radius compensation is on.
1857
   The value of cutter_comp_radius in the machine model mode is
1858
   set to the absolute value of the radius given in the tool table.
1859
   The value of cutter_comp_side in the machine model mode is
1860
   set to RIGHT or LEFT. The currently active tool table index in
1861
   the machine model is updated.
1862
1863
Called by: convert_cutter_compensation
1864
1865
check_other_codes checks that a d word occurs only in a block with g41
1866
or g42.
1867
1868
Cutter radius compensation is carried out in the interpreter, so no
1869
call is made to a canonical function (although there is a canonical
1870
function, START_CUTTER_RADIUS_COMPENSATION, that could be called if
1871
the primitive level could execute it).
1872
1873
This version uses a D word if there is one in the block, but it does
1874
not require a D word, since the sample programs which the interpreter
1875
is supposed to handle do not have them.  Logically, the D word is
1876
optional, since the D word is always (except in cases we have never
1877
heard of) the slot number of the tool in the spindle. Not requiring a
1878
D word is contrary to [Fanuc, page 116] and [NCMS, page 79], however.
1879
Both manuals require the use of the D-word with G41 and G42.
1880
1881
This version handles a negative offset radius, which may be
1882
encountered if the programmed tool path is a center line path for
1883
cutting a profile and the path was constructed using a nominal tool
1884
diameter. Then the value in the tool table for the diameter is set to
1885
be the difference between the actual diameter and the nominal
1886
diameter. If the actual diameter is less than the nominal, the value
1887
in the table is negative. The method of handling a negative radius is
1888
to switch the side of the offset and use a positive radius. This
1889
requires that the profile use arcs (not straight lines) to go around
1890
convex corners.
1891
1892
*/
1893
1894
/* Set *result to the integer nearest to value; return TRUE if value is
1895
 * within .0001 of an integer
1896
 */
1897
27
static int is_near_int(int *result, double value) {
1898
27
    *result = (int)(value + .5);
1899
27
    return fabs(*result - value) < .0001;
1900
}
1901
1902
62
int Interp::convert_cutter_compensation_on(int side,     //!< side of path cutter is on (LEFT or RIGHT)
1903
                                          block_pointer block,  //!< pointer to a block of RS274 instructions
1904
                                          setup_pointer settings)       //!< pointer to machine settings
1905
{
1906
  double radius;
1907
  int pocket_number, orientation;
1908
1909
62
  CHKS((settings->plane != CANON_PLANE_XY && settings->plane != CANON_PLANE_XZ),
1910
      NCE_RADIUS_COMP_ONLY_IN_XY_OR_XZ);
1911
62
  CHKS((settings->cutter_comp_side),
1912
      NCE_CANNOT_TURN_CUTTER_RADIUS_COMP_ON_WHEN_ON);
1913
62
  if(block->g_modes[7] == G_41_1 || block->g_modes[7] == G_42_1) {
1914
61
      CHKS((!block->d_flag),
1915
              _("G%d.1 with no D word"), block->g_modes[7]/10 );
1916
61
      radius = block->d_number_float / 2;
1917
61
      if(block->l_number != -1) {
1918
          CHKS((settings->plane != CANON_PLANE_XZ), _("G%d.1 with L word, but plane is not G18"), block->g_modes[7]/10);
1919
          orientation = block->l_number;
1920
      } else {
1921
          orientation = 0;
1922
      }
1923
  } else {
1924
1
      if(!block->d_flag) {
1925
          pocket_number = 0;
1926
      } else {
1927
          int tool;
1928
1
          CHKS(!is_near_int(&tool, block->d_number_float),
1929
                  _("G%d requires D word to be a whole number"),
1930
                   block->g_modes[7]/10);
1931
1
          CHKS((tool < 0), NCE_NEGATIVE_D_WORD_TOOL_RADIUS_INDEX_USED);
1932
1
          CHP((find_tool_pocket(settings, tool, &pocket_number)));
1933
      }
1934
1
      radius = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].diameter) / 2.0;
1935
1
      orientation = settings->tool_table[pocket_number].orientation;
1936
1
      CHKS((settings->plane != CANON_PLANE_XZ && orientation != 0 && orientation != 9), _("G%d with lathe tool, but plane is not G18"), block->g_modes[7]/10);
1937
  }
1938
62
  if (radius < 0.0) { /* switch side & make radius positive if radius negative */
1939
28
    radius = -radius;
1940
28
    if (side == RIGHT)
1941
      side = LEFT;
1942
    else
1943
27
      side = RIGHT;
1944
  }
1945
#ifdef DEBUG_EMC
1946
62
  if (side == RIGHT)
1947
30
    enqueue_COMMENT("interpreter: cutter radius compensation on right");
1948
  else
1949
32
    enqueue_COMMENT("interpreter: cutter radius compensation on left");
1950
#endif
1951
1952
62
  settings->cutter_comp_radius = radius;
1953
62
  settings->cutter_comp_orientation = orientation;
1954
62
  settings->cutter_comp_side = side;
1955
62
  return INTERP_OK;
1956
}
1957
1958
/****************************************************************************/
1959
1960
/*! convert_distance_mode
1961
1962
Returned Value: int
1963
   If any of the following errors occur, this returns the error shown.
1964
   Otherwise, it returns INTERP_OK.
1965
   1. g_code isn't G_90 or G_91: NCE_BUG_CODE_NOT_G90_OR_G91
1966
1967
Side effects:
1968
   The interpreter switches the machine settings to indicate the current
1969
   distance mode (absolute or incremental).
1970
1971
   The canonical machine to which commands are being sent does not have
1972
   an incremental mode, so no command setting the distance mode is
1973
   generated in this function. A comment function call explaining the
1974
   change of mode is made (conditionally), however, if there is a change.
1975
1976
Called by: convert_g.
1977
1978
*/
1979
1980
// OK to call this in a concave corner with a deferred move, since it
1981
// doesn't issue any CANONs
1982
1983
176
int Interp::convert_distance_mode(int g_code,    //!< g_code being executed (must be G_90 or G_91)
1984
                                 setup_pointer settings)        //!< pointer to machine settings
1985
{
1986
176
  if (g_code == G_90) {
1987
129
    if (settings->distance_mode != MODE_ABSOLUTE) {
1988
#ifdef DEBUG_EMC
1989
40
      enqueue_COMMENT("interpreter: distance mode changed to absolute");
1990
#endif
1991
40
      settings->distance_mode = MODE_ABSOLUTE;
1992
    }
1993
47
  } else if (g_code == G_91) {
1994
47
    if (settings->distance_mode != MODE_INCREMENTAL) {
1995
#ifdef DEBUG_EMC
1996
47
      enqueue_COMMENT("interpreter: distance mode changed to incremental");
1997
#endif
1998
47
      settings->distance_mode = MODE_INCREMENTAL;
1999
    }
2000
  } else
2001
    ERS(NCE_BUG_CODE_NOT_G90_OR_G91);
2002
  return INTERP_OK;
2003
}
2004
2005
/****************************************************************************/
2006
2007
/*! convert_ijk_distance_mode
2008
2009
Returned Value: int
2010
   If any of the following errors occur, this returns the error shown.
2011
   Otherwise, it returns INTERP_OK.
2012
   1. g_code isn't G_90.1 or G_91.1: NCE_BUG_CODE_NOT_G90_OR_G91
2013
2014
Side effects:
2015
   The interpreter switches the machine settings to indicate the current
2016
   distance mode for arc centers (absolute or incremental).
2017
2018
   The canonical machine to which commands are being sent does not have
2019
   an incremental mode, so no command setting the distance mode is
2020
   generated in this function. A comment function call explaining the
2021
   change of mode is made (conditionally), however, if there is a change.
2022
2023
Called by: convert_g.
2024
2025
*/
2026
2027
// OK to call this in a concave corner with a deferred move, since it
2028
// doesn't issue any CANONs except comments (and who cares where the comments are)
2029
2030
int Interp::convert_ijk_distance_mode(int g_code,    //!< g_code being executed (must be G_90_1 or G_91_1)
2031
                                 setup_pointer settings)        //!< pointer to machine settings
2032
{
2033
  if (g_code == G_90_1) {
2034
    if (settings->ijk_distance_mode != MODE_ABSOLUTE) {
2035
#ifdef DEBUG_EMC
2036
      enqueue_COMMENT("interpreter: IJK distance mode changed to absolute");
2037
#endif
2038
      settings->ijk_distance_mode = MODE_ABSOLUTE;
2039
    }
2040
  } else if (g_code == G_91_1) {
2041
    if (settings->ijk_distance_mode != MODE_INCREMENTAL) {
2042
#ifdef DEBUG_EMC
2043
      enqueue_COMMENT("interpreter: IJK distance mode changed to incremental");
2044
#endif
2045
      settings->ijk_distance_mode = MODE_INCREMENTAL;
2046
    }
2047
  } else
2048
    ERS(NCE_BUG_CODE_NOT_G90_OR_G91);
2049
  return INTERP_OK;
2050
}
2051
2052
/****************************************************************************/
2053
2054
/*! convert_lathe_diameter_mode
2055
2056
Returned Value: int
2057
   If any of the following errors occur, this returns the error shown.
2058
   Otherwise, it returns INTERP_OK.
2059
   1. g_code isn't G_07 or G_08: NCE_BUG_CODE_NOT_G07_OR_G08
2060
2061
Side effects:
2062
   The interpreter switches the machine settings to indicate the current
2063
   distance mode for arc centers (absolute or incremental).
2064
2065
   The canonical machine to which commands are being sent does not have
2066
   an incremental mode, so no command setting the distance mode is
2067
   generated in this function. A comment function call explaining the
2068
   change of mode is made (conditionally), however, if there is a change.
2069
2070
Called by: convert_g.
2071
2072
*/
2073
2074
int Interp::convert_lathe_diameter_mode(int g_code,    //!< g_code being executed (must be G_90_1 or G_91_1)
2075
                  block_pointer block,           //!< pointer to current block
2076
                  setup_pointer settings)        //!< pointer to machine settings
2077
{
2078
  if (g_code == G_7) {
2079
    if (!settings->lathe_diameter_mode) {
2080
      if(block->x_flag)
2081
      {
2082
        block->x_number /= 2; //Apply scaling now
2083
      }
2084
      if(block->motion_to_be == G_76) {
2085
          block->i_number /= 2;
2086
          block->j_number /= 2;
2087
          block->k_number /= 2;
2088
      }
2089
#ifdef DEBUG_EMC
2090
      COMMENT("interpreter: Lathe diameter mode changed to diameter");
2091
#endif
2092
      settings->lathe_diameter_mode = true;
2093
    }
2094
  } else if (g_code == G_8) {
2095
    if (settings->lathe_diameter_mode) {
2096
      if(block->x_flag)
2097
      {
2098
        block->x_number *= 2; //Remove any existing scaling
2099
      }
2100
      if(block->motion_to_be == G_76) {
2101
          block->i_number *= 2;
2102
          block->j_number *= 2;
2103
          block->k_number *= 2;
2104
      }
2105
#ifdef DEBUG_EMC
2106
      COMMENT("interpreter: Lathe diameter mode changed to radius");
2107
#endif
2108
      settings->lathe_diameter_mode = false;
2109
    }
2110
  } else
2111
    ERS("BUG: Code not G7 or G8");
2112
  return INTERP_OK;
2113
}
2114
2115
2116
/****************************************************************************/
2117
2118
/*! convert_dwell
2119
2120
Returned Value: int (INTERP_OK)
2121
2122
Side effects:
2123
   A dwell command is executed.
2124
2125
Called by: convert_g.
2126
2127
*/
2128
2129
3
int Interp::convert_dwell(setup_pointer settings, double time)   //!< time in seconds to dwell  */
2130
{
2131
3
  enqueue_DWELL(time);
2132
3
  return INTERP_OK;
2133
}
2134
2135
/****************************************************************************/
2136
2137
/*! convert_feed_mode
2138
2139
Returned Value: int
2140
   If any of the following errors occur, this returns an error code.
2141
   Otherwise, it returns INTERP_OK.
2142
   1.  g_code isn't G_93, G_94 or G_95
2143
2144
Side effects:
2145
   The interpreter switches the machine settings to indicate the current
2146
   feed mode (UNITS_PER_MINUTE or INVERSE_TIME).
2147
2148
   The canonical machine to which commands are being sent does not have
2149
   a feed mode, so no command setting the distance mode is generated in
2150
   this function. A comment function call is made (conditionally)
2151
   explaining the change in mode, however.
2152
2153
Called by: execute_block.
2154
2155
*/
2156
2157
13
int Interp::convert_feed_mode(int g_code,        //!< g_code being executed (must be G_93, G_94 or G_95)
2158
                             setup_pointer settings)    //!< pointer to machine settings
2159
{
2160
13
  if (g_code == G_93) {
2161
#ifdef DEBUG_EMC
2162
6
    enqueue_COMMENT("interpreter: feed mode set to inverse time");
2163
#endif
2164
6
    settings->feed_mode = INVERSE_TIME;
2165
6
    enqueue_SET_FEED_MODE(0);
2166
7
  } else if (g_code == G_94) {
2167
#ifdef DEBUG_EMC
2168
7
    enqueue_COMMENT("interpreter: feed mode set to units per minute");
2169
#endif
2170
7
    settings->feed_mode = UNITS_PER_MINUTE;
2171
7
    enqueue_SET_FEED_MODE(0);
2172
7
    settings->feed_rate = 0.0;
2173
7
    enqueue_SET_FEED_RATE(0);
2174
  } else if(g_code == G_95) {
2175
#ifdef DEBUG_EMC
2176
    enqueue_COMMENT("interpreter: feed mode set to units per revolution");
2177
#endif
2178
    settings->feed_mode = UNITS_PER_REVOLUTION;
2179
    enqueue_SET_FEED_MODE(1);
2180
    settings->feed_rate = 0.0;
2181
    enqueue_SET_FEED_RATE(0);
2182
  } else
2183
    ERS("BUG: Code not G93, G94, or G95");
2184
  return INTERP_OK;
2185
}
2186
2187
/****************************************************************************/
2188
2189
/*! convert_feed_rate
2190
2191
Returned Value: int (INTERP_OK)
2192
2193
Side effects:
2194
   The machine feed_rate is set to the value of f_number in the
2195
     block by function call.
2196
   The machine model feed_rate is set to that value.
2197
2198
Called by: execute_block
2199
2200
This is called only if the feed mode is UNITS_PER_MINUTE or UNITS_PER_REVOLUTION.
2201
2202
*/
2203
2204
569
int Interp::convert_feed_rate(block_pointer block,       //!< pointer to a block of RS274 instructions
2205
                             setup_pointer settings)    //!< pointer to machine settings
2206
{
2207
569
  settings->feed_rate = block->f_number;
2208
569
  enqueue_SET_FEED_RATE(block->f_number);
2209
569
  return INTERP_OK;
2210
}
2211
2212
/****************************************************************************/
2213
2214
/*! convert_g
2215
2216
Returned Value: int
2217
   If one of the following functions is called and returns an error code,
2218
   this returns that code.
2219
      convert_control_mode
2220
      convert_coordinate_system
2221
      convert_cutter_compensation
2222
      convert_distance_mode
2223
      convert_ijk_distance_mode
2224
      convert_lathe_diameter_mode
2225
      convert_dwell
2226
      convert_length_units
2227
      convert_modal_0
2228
      convert_motion
2229
      convert_retract_mode
2230
      convert_set_plane
2231
      convert_tool_length_offset
2232
   Otherwise, it returns INTERP_OK.
2233
2234
Side effects:
2235
   Any g_codes in the block (excluding g93 and 94) and any implicit
2236
   motion g_code are executed.
2237
2238
Called by: execute_block.
2239
2240
This takes a pointer to a block of RS274/NGC instructions (already
2241
read in) and creates the appropriate output commands corresponding to
2242
any "g" codes in the block.
2243
2244
Codes g93 and g94, which set the feed mode, are executed earlier by
2245
execute_block before reading the feed rate.
2246
2247
G codes are are executed in the following order.
2248
1.  mode 0, G4 only - dwell. Left here from earlier versions.
2249
2.  mode 2, one of (G17, G18, G19) - plane selection.
2250
3.  mode 6, one of (G20, G21) - length units.
2251
4.  mode 15 one of (G07,G08) - lathe diameter mode
2252
5.  mode 7, one of (G40, G41, G42) - cutter radius compensation.
2253
6.  mode 8, one of (G43, G49) - tool length offset
2254
7.  mode 12, one of (G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3)
2255
    - coordinate system selection.
2256
8.  mode 13, one of (G61, G61.1, G64, G50, G51) - control mode
2257
9.  mode 3, one of (G90, G91) - distance mode.
2258
10.  mode 4, one of (G90.1, G91.1) - arc i,j,k mode.
2259
11. mode 10, one of (G98, G99) - retract mode.
2260
12. mode 0, one of (G10, G28, G30, G92, G92.1, G92.2, G92.3) -
2261
13. mode 1, one of (G0, G1, G2, G3, G38.2, G80, G81 to G89, G33, G33.1, G76) - motion or cancel.
2262
    G53 from mode 0 is also handled here, if present.
2263
2264
Some mode 0 and most mode 1 G codes must be executed after the length units
2265
are set, since they use coordinate values. Mode 1 codes also must wait
2266
until most of the other modes are set.
2267
2268
*/
2269
2270
4509
int Interp::convert_g(block_pointer block,       //!< pointer to a block of RS274/NGC instructions
2271
		      setup_pointer settings)    //!< pointer to machine settings
2272
{
2273
    int status;
2274
2275
4521
    if ((block->g_modes[GM_MODAL_0] == G_4) && ONCE(STEP_DWELL)) {
2276
3
      status = convert_dwell(settings, block->p_number);
2277
3
      CHP(status);
2278
    }
2279
4553
    if ((block->g_modes[GM_SET_PLANE] != -1) && ONCE(STEP_SET_PLANE)) {
2280
11
	status = convert_set_plane(block->g_modes[GM_SET_PLANE], settings);
2281
11
	CHP(status);
2282
    }
2283
4725
    if ((block->g_modes[GM_LENGTH_UNITS] != -1) && ONCE(STEP_LENGTH_UNITS)) {
2284
54
	status = convert_length_units(block->g_modes[GM_LENGTH_UNITS], settings);
2285
54
	CHP(status);
2286
    }
2287
4509
    if ((block->g_modes[GM_LATHE_DIAMETER_MODE] != -1) && ONCE(STEP_LATHE_DIAMETER_MODE)) {
2288
	status = convert_lathe_diameter_mode(block->g_modes[GM_LATHE_DIAMETER_MODE], block, settings);
2289
	CHP(status);
2290
    }
2291
5105
    if ((block->g_modes[GM_CUTTER_COMP] != -1) && ONCE(STEP_CUTTER_COMP)) {
2292
149
	status = convert_cutter_compensation(block->g_modes[GM_CUTTER_COMP], block, settings);
2293
149
	CHP(status);
2294
    }
2295
4577
    if ((block->g_modes[GM_TOOL_LENGTH_OFFSET] != -1) && ONCE(STEP_TOOL_LENGTH_OFFSET)){
2296
17
	status = convert_tool_length_offset(block->g_modes[GM_TOOL_LENGTH_OFFSET], block, settings);
2297
17
	CHP(status);
2298
    }
2299
4653
    if ((block->g_modes[GM_COORD_SYSTEM] != -1) && ONCE(STEP_COORD_SYSTEM)){
2300
36
	status = convert_coordinate_system(block->g_modes[GM_COORD_SYSTEM], settings);
2301
36
	CHP(status);
2302
    }
2303
4537
    if ((block->g_modes[GM_CONTROL_MODE] != -1) && ONCE(STEP_CONTROL_MODE)) {
2304
	status = convert_control_mode(block->g_modes[GM_CONTROL_MODE],
2305
7
				      block->p_number, block->q_number, settings);
2306
7
	CHP(status);
2307
    }
2308
5213
    if ((block->g_modes[GM_DISTANCE_MODE] != -1) && ONCE(STEP_DISTANCE_MODE)) {
2309
176
      status = convert_distance_mode(block->g_modes[GM_DISTANCE_MODE], settings);
2310
176
      CHP(status);
2311
    }
2312
4509
    if ((block->g_modes[GM_IJK_DISTANCE_MODE] != -1) && ONCE(STEP_IJK_DISTANCE_MODE)){
2313
	status = convert_ijk_distance_mode(block->g_modes[GM_IJK_DISTANCE_MODE], settings);
2314
	CHP(status);
2315
    }
2316
4533
    if ((block->g_modes[GM_RETRACT_MODE] != -1)  && ONCE(STEP_RETRACT_MODE)){
2317
6
	status = convert_retract_mode(block->g_modes[GM_RETRACT_MODE], settings);
2318
6
	CHP(status);
2319
    }
2320
4837
    if ((block->g_modes[GM_MODAL_0] != -1) && ONCE(STEP_MODAL_0)) {
2321
82
      status = convert_modal_0(block->g_modes[GM_MODAL_0], block, settings);
2322
82
      CHP(status);
2323
  }
2324
10345
    if ((block->motion_to_be != -1)  && ONCE(STEP_MOTION)){
2325
1459
      status = convert_motion(block->motion_to_be, block, settings);
2326
1459
      CHP(status);
2327
  }
2328
  return INTERP_OK;
2329
}
2330
2331
int Interp::convert_savehome(int code, block_pointer block, setup_pointer s) {
2332
    double *p = s->parameters;
2333
2334
    if(s->cutter_comp_side) {
2335
        ERS(_("Cannot set reference point with cutter compensation in effect"));
2336
    }
2337
2338
    double x = PROGRAM_TO_USER_LEN(s->current_x + s->tool_offset.tran.x + s->origin_offset_x + s->axis_offset_x);
2339
    double y = PROGRAM_TO_USER_LEN(s->current_y + s->tool_offset.tran.y + s->origin_offset_y + s->axis_offset_y);
2340
    double z = PROGRAM_TO_USER_LEN(s->current_z + s->tool_offset.tran.z + s->origin_offset_z + s->axis_offset_z);
2341
    double a = PROGRAM_TO_USER_ANG(s->AA_current + s->tool_offset.a + s->AA_origin_offset + s->AA_axis_offset);
2342
    double b = PROGRAM_TO_USER_ANG(s->BB_current + s->tool_offset.b + s->BB_origin_offset + s->BB_axis_offset);
2343
    double c = PROGRAM_TO_USER_ANG(s->CC_current + s->tool_offset.c + s->CC_origin_offset + s->CC_axis_offset);
2344
    double u = PROGRAM_TO_USER_LEN(s->u_current + s->tool_offset.u + s->u_origin_offset + s->u_axis_offset);
2345
    double v = PROGRAM_TO_USER_LEN(s->v_current + s->tool_offset.v + s->v_origin_offset + s->v_axis_offset);
2346
    double w = PROGRAM_TO_USER_LEN(s->w_current + s->tool_offset.w + s->w_origin_offset + s->w_axis_offset);
2347
2348
    if(s->a_axis_wrapped) {
2349
        a = fmod(a, 360.0);
2350
        if(a<0) a += 360.0;
2351
    }
2352
2353
    if(s->b_axis_wrapped) {
2354
        b = fmod(b, 360.0);
2355
        if(b<0) b += 360.0;
2356
    }
2357
2358
    if(s->c_axis_wrapped) {
2359
        c = fmod(c, 360.0);
2360
        if(c<0) c += 360.0;
2361
    }
2362
2363
    if(code == G_28_1) {
2364
        p[5161] = x;
2365
        p[5162] = y;
2366
        p[5163] = z;
2367
        p[5164] = a;
2368
        p[5165] = b;
2369
        p[5166] = c;
2370
        p[5167] = u;
2371
        p[5168] = v;
2372
        p[5169] = w;
2373
    } else if(code == G_30_1) {
2374
        p[5181] = x;
2375
        p[5182] = y;
2376
        p[5183] = z;
2377
        p[5184] = a;
2378
        p[5185] = b;
2379
        p[5186] = c;
2380
        p[5187] = u;
2381
        p[5188] = v;
2382
        p[5189] = w;
2383
    } else {
2384
        ERS("BUG: Code not G28.1 or G38.1");
2385
    }
2386
    return INTERP_OK;
2387
}
2388
2389
2390
/****************************************************************************/
2391
2392
/*! convert_home
2393
2394
Returned Value: int
2395
   If any of the following errors occur, this returns the error code shown.
2396
   Otherwise, it returns INTERP_OK.
2397
   1. cutter radius compensation is on:
2398
      NCE_CANNOT_USE_G28_OR_G30_WITH_CUTTER_RADIUS_COMP
2399
   2. The code is not G28 or G30: NCE_BUG_CODE_NOT_G28_OR_G30
2400
2401
Side effects:
2402
   This executes a straight traverse to the programmed point, using
2403
   the current coordinate system, tool length offset, and motion mode
2404
   to interpret the coordinate values. Then it executes a straight
2405
   traverse to move one or more axes to the location of reference
2406
   point 1 (if G28) or reference point 2 (if G30).  If any axis words
2407
   are specified in this block, only those axes are moved to the
2408
   reference point.  If none are specified, all axes are moved.  It
2409
   also updates the setting of the position of the tool point to the
2410
   end point of the move.
2411
2412
   If either move would affect the position of one or more locking
2413
   rotaries, the rotaries are unlocked and indexed one at a time,
2414
   in the order A,B,C and then the other axes are moved.
2415
2416
   N.B. Many gcode programmers call the reference point a home
2417
   position, and that is exactly what it is if the parameters are
2418
   zero.  Do not confuse this with homing the axis (searching for
2419
   a switch or index pulse).
2420
2421
Called by: convert_modal_0.
2422
2423
*/
2424
2425
int Interp::convert_home(int move,       //!< G code, must be G_28 or G_30
2426
                        block_pointer block,    //!< pointer to a block of RS274 instructions
2427
                        setup_pointer settings) //!< pointer to machine settings
2428
{
2429
  double end_x;
2430
  double end_y;
2431
  double end_z;
2432
  double AA_end;
2433
  double BB_end;
2434
  double CC_end;
2435
  double u_end;
2436
  double v_end;
2437
  double w_end;
2438
  double end_x_home;
2439
  double end_y_home;
2440
  double end_z_home;
2441
  double AA_end_home;
2442
  double BB_end_home;
2443
  double CC_end_home;
2444
  double u_end_home;
2445
  double v_end_home;
2446
  double w_end_home;
2447
  double *parameters;
2448
2449
  parameters = settings->parameters;
2450
  CHP(find_ends(block, settings, &end_x, &end_y, &end_z,
2451
                &AA_end, &BB_end, &CC_end,
2452
                &u_end, &v_end, &w_end));
2453
2454
  CHKS((settings->cutter_comp_side),
2455
      NCE_CANNOT_USE_G28_OR_G30_WITH_CUTTER_RADIUS_COMP);
2456
2457
  // waypoint is in currently active coordinate system
2458
2459
  // move indexers first, one at a time
2460
  // JOINTS_AXES settings->*_indexer_jnum == -1 means notused
2461
  if (AA_end != settings->AA_current && (-1 != settings->a_indexer_jnum) )
2462
      issue_straight_index(3,settings->a_indexer_jnum, AA_end, block->line_number, settings);
2463
  if (BB_end != settings->BB_current && (-1 != settings->b_indexer_jnum) )
2464
      issue_straight_index(4,settings->b_indexer_jnum, BB_end, block->line_number, settings);
2465
  if (CC_end != settings->CC_current && (-1 != settings->c_indexer_jnum) )
2466
      issue_straight_index(5,settings->c_indexer_jnum, CC_end, block->line_number, settings);
2467
2468
  STRAIGHT_TRAVERSE(block->line_number, end_x, end_y, end_z,
2469
                    AA_end, BB_end, CC_end,
2470
                    u_end, v_end, w_end);
2471
2472
  settings->current_x = end_x;
2473
  settings->current_y = end_y;
2474
  settings->current_z = end_z;
2475
  settings->AA_current = AA_end;
2476
  settings->BB_current = BB_end;
2477
  settings->CC_current = CC_end;
2478
  settings->u_current = u_end;
2479
  settings->v_current = v_end;
2480
  settings->w_current = w_end;
2481
2482
  if (move == G_28) {
2483
      find_relative(USER_TO_PROGRAM_LEN(parameters[5161]),
2484
                    USER_TO_PROGRAM_LEN(parameters[5162]),
2485
                    USER_TO_PROGRAM_LEN(parameters[5163]),
2486
                    USER_TO_PROGRAM_ANG(parameters[5164]),
2487
                    USER_TO_PROGRAM_ANG(parameters[5165]),
2488
                    USER_TO_PROGRAM_ANG(parameters[5166]),
2489
                    USER_TO_PROGRAM_LEN(parameters[5167]),
2490
                    USER_TO_PROGRAM_LEN(parameters[5168]),
2491
                    USER_TO_PROGRAM_LEN(parameters[5169]),
2492
                    &end_x_home, &end_y_home, &end_z_home,
2493
                    &AA_end_home, &BB_end_home, &CC_end_home,
2494
                    &u_end_home, &v_end_home, &w_end_home, settings);
2495
  } else if (move == G_30) {
2496
      find_relative(USER_TO_PROGRAM_LEN(parameters[5181]),
2497
                    USER_TO_PROGRAM_LEN(parameters[5182]),
2498
                    USER_TO_PROGRAM_LEN(parameters[5183]),
2499
                    USER_TO_PROGRAM_ANG(parameters[5184]),
2500
                    USER_TO_PROGRAM_ANG(parameters[5185]),
2501
                    USER_TO_PROGRAM_ANG(parameters[5186]),
2502
                    USER_TO_PROGRAM_LEN(parameters[5187]),
2503
                    USER_TO_PROGRAM_LEN(parameters[5188]),
2504
                    USER_TO_PROGRAM_LEN(parameters[5189]),
2505
                    &end_x_home, &end_y_home, &end_z_home,
2506
                    &AA_end_home, &BB_end_home, &CC_end_home,
2507
                    &u_end_home, &v_end_home, &w_end_home, settings);
2508
  } else
2509
    ERS(NCE_BUG_CODE_NOT_G28_OR_G30);
2510
2511
  // if any axes are specified, home only those axes after the waypoint
2512
  // (both fanuc & haas, contrary to emc historical operation)
2513
2514
  if (block->x_flag) end_x = end_x_home;
2515
  if (block->y_flag) end_y = end_y_home;
2516
  if (block->z_flag) end_z = end_z_home;
2517
  if (block->a_flag) AA_end = AA_end_home;
2518
  if (block->b_flag) BB_end = BB_end_home;
2519
  if (block->c_flag) CC_end = CC_end_home;
2520
  if (block->u_flag) u_end = u_end_home;
2521
  if (block->v_flag) v_end = v_end_home;
2522
  if (block->w_flag) w_end = w_end_home;
2523
2524
  // but, if no axes are specified, home all of them
2525
  // (haas does this, emc historical did, throws an error in fanuc)
2526
2527
  if (!block->x_flag && !block->y_flag && !block->z_flag &&
2528
      !block->a_flag && !block->b_flag && !block->c_flag &&
2529
      !block->u_flag && !block->v_flag && !block->w_flag) {
2530
      end_x = end_x_home;
2531
      end_y = end_y_home;
2532
      end_z = end_z_home;
2533
      AA_end = AA_end_home;
2534
      BB_end = BB_end_home;
2535
      CC_end = CC_end_home;
2536
      u_end = u_end_home;
2537
      v_end = v_end_home;
2538
      w_end = w_end_home;
2539
  }
2540
2541
  // move indexers first, one at a time
2542
  // JOINTS_AXES settings->*_indexer_jnum == -1 means notused
2543
  if (AA_end != settings->AA_current && (-1 != settings->a_indexer_jnum) )
2544
      issue_straight_index(3,settings->a_indexer_jnum, AA_end, block->line_number, settings);
2545
  if (BB_end != settings->BB_current && (-1 != settings->b_indexer_jnum) )
2546
      issue_straight_index(4,settings->b_indexer_jnum, BB_end, block->line_number, settings);
2547
  if (CC_end != settings->CC_current && (-1 != settings->c_indexer_jnum) )
2548
      issue_straight_index(5,settings->c_indexer_jnum, CC_end, block->line_number, settings);
2549
2550
  STRAIGHT_TRAVERSE(block->line_number, end_x, end_y, end_z,
2551
                    AA_end, BB_end, CC_end,
2552
                    u_end, v_end, w_end);
2553
  settings->current_x = end_x;
2554
  settings->current_y = end_y;
2555
  settings->current_z = end_z;
2556
  settings->AA_current = AA_end;
2557
  settings->BB_current = BB_end;
2558
  settings->CC_current = CC_end;
2559
  settings->u_current = u_end;
2560
  settings->v_current = v_end;
2561
  settings->w_current = w_end;
2562
2563
  return INTERP_OK;
2564
}
2565
2566
/****************************************************************************/
2567
2568
/*! convert_length_units
2569
2570
Returned Value: int
2571
   If any of the following errors occur, this returns the error shown.
2572
   Otherwise, it returns INTERP_OK.
2573
   1. The g_code argument isnt G_20 or G_21:
2574
      NCE_BUG_CODE_NOT_G20_OR_G21
2575
   2. Cutter radius compensation is on:
2576
      NCE_CANNOT_CHANGE_UNITS_WITH_CUTTER_RADIUS_COMP
2577
2578
Side effects:
2579
   A command setting the length units is executed. The machine
2580
   settings are reset regarding length units and current position.
2581
2582
Called by: convert_g.
2583
2584
Tool length offset and diameter, work coordinate systems, feed rate,
2585
g28/g30 home positions, and g53 motion in absolute coordinates all work
2586
properly after switching units.  Historically these had problems but the
2587
intention is that they work properly now.
2588
2589
The tool table in settings is not converted here; it is always in
2590
inifile units and the conversion happens when reading an entry.  Tool
2591
offsets and feed rate that are in effect are converted by rereading them
2592
from the canon level.
2593
2594
Cutter diameter is not converted because radius comp is not in effect
2595
when we are changing units.
2596
2597
XXX Other distance items in the settings (such as the various parameters
2598
for cycles) need testing.
2599
2600
*/
2601
2602
54
int Interp::convert_length_units(int g_code,     //!< g_code being executed (must be G_20 or G_21)
2603
                                setup_pointer settings) //!< pointer to machine settings
2604
{
2605
54
  if (g_code == G_20) {
2606
20
    USE_LENGTH_UNITS(CANON_UNITS_INCHES);
2607
20
    if (settings->length_units != CANON_UNITS_INCHES) {
2608
20
      settings->length_units = CANON_UNITS_INCHES;
2609
20
      settings->current_x = (settings->current_x * INCH_PER_MM);
2610
20
      settings->current_y = (settings->current_y * INCH_PER_MM);
2611
20
      settings->current_z = (settings->current_z * INCH_PER_MM);
2612
20
      settings->program_x = (settings->program_x * INCH_PER_MM);
2613
20
      settings->program_y = (settings->program_y * INCH_PER_MM);
2614
20
      settings->program_z = (settings->program_z * INCH_PER_MM);
2615
20
      qc_scale(INCH_PER_MM);
2616
20
      settings->cutter_comp_radius *= INCH_PER_MM;
2617
20
      settings->axis_offset_x = (settings->axis_offset_x * INCH_PER_MM);
2618
20
      settings->axis_offset_y = (settings->axis_offset_y * INCH_PER_MM);
2619
20
      settings->axis_offset_z = (settings->axis_offset_z * INCH_PER_MM);
2620
20
      settings->origin_offset_x = (settings->origin_offset_x * INCH_PER_MM);
2621
20
      settings->origin_offset_y = (settings->origin_offset_y * INCH_PER_MM);
2622
20
      settings->origin_offset_z = (settings->origin_offset_z * INCH_PER_MM);
2623
2624
20
      settings->u_current = (settings->u_current * INCH_PER_MM);
2625
20
      settings->v_current = (settings->v_current * INCH_PER_MM);
2626
20
      settings->w_current = (settings->w_current * INCH_PER_MM);
2627
20
      settings->u_axis_offset = (settings->u_axis_offset * INCH_PER_MM);
2628
20
      settings->v_axis_offset = (settings->v_axis_offset * INCH_PER_MM);
2629
20
      settings->w_axis_offset = (settings->w_axis_offset * INCH_PER_MM);
2630
20
      settings->u_origin_offset = (settings->u_origin_offset * INCH_PER_MM);
2631
20
      settings->v_origin_offset = (settings->v_origin_offset * INCH_PER_MM);
2632
20
      settings->w_origin_offset = (settings->w_origin_offset * INCH_PER_MM);
2633
2634
20
      settings->tool_offset.tran.x = GET_EXTERNAL_TOOL_LENGTH_XOFFSET();
2635
20
      settings->tool_offset.tran.y = GET_EXTERNAL_TOOL_LENGTH_YOFFSET();
2636
20
      settings->tool_offset.tran.z = GET_EXTERNAL_TOOL_LENGTH_ZOFFSET();
2637
20
      settings->tool_offset.a = GET_EXTERNAL_TOOL_LENGTH_AOFFSET();
2638
20
      settings->tool_offset.b = GET_EXTERNAL_TOOL_LENGTH_BOFFSET();
2639
20
      settings->tool_offset.c = GET_EXTERNAL_TOOL_LENGTH_COFFSET();
2640
20
      settings->tool_offset.u = GET_EXTERNAL_TOOL_LENGTH_UOFFSET();
2641
20
      settings->tool_offset.v = GET_EXTERNAL_TOOL_LENGTH_VOFFSET();
2642
20
      settings->tool_offset.w = GET_EXTERNAL_TOOL_LENGTH_WOFFSET();
2643
20
      settings->feed_rate = GET_EXTERNAL_FEED_RATE();
2644
    }
2645
34
  } else if (g_code == G_21) {
2646
34
    USE_LENGTH_UNITS(CANON_UNITS_MM);
2647
34
    if (settings->length_units != CANON_UNITS_MM) {
2648
      settings->length_units = CANON_UNITS_MM;
2649
      settings->current_x = (settings->current_x * MM_PER_INCH);
2650
      settings->current_y = (settings->current_y * MM_PER_INCH);
2651
      settings->current_z = (settings->current_z * MM_PER_INCH);
2652
      settings->program_x = (settings->program_x * MM_PER_INCH);
2653
      settings->program_y = (settings->program_y * MM_PER_INCH);
2654
      settings->program_z = (settings->program_z * MM_PER_INCH);
2655
      qc_scale(MM_PER_INCH);
2656
      settings->cutter_comp_radius *= MM_PER_INCH;
2657
      settings->axis_offset_x = (settings->axis_offset_x * MM_PER_INCH);
2658
      settings->axis_offset_y = (settings->axis_offset_y * MM_PER_INCH);
2659
      settings->axis_offset_z = (settings->axis_offset_z * MM_PER_INCH);
2660
      settings->origin_offset_x = (settings->origin_offset_x * MM_PER_INCH);
2661
      settings->origin_offset_y = (settings->origin_offset_y * MM_PER_INCH);
2662
      settings->origin_offset_z = (settings->origin_offset_z * MM_PER_INCH);
2663
2664
      settings->u_current = (settings->u_current * MM_PER_INCH);
2665
      settings->v_current = (settings->v_current * MM_PER_INCH);
2666
      settings->w_current = (settings->w_current * MM_PER_INCH);
2667
      settings->u_axis_offset = (settings->u_axis_offset * MM_PER_INCH);
2668
      settings->v_axis_offset = (settings->v_axis_offset * MM_PER_INCH);
2669
      settings->w_axis_offset = (settings->w_axis_offset * MM_PER_INCH);
2670
      settings->u_origin_offset = (settings->u_origin_offset * MM_PER_INCH);
2671
      settings->v_origin_offset = (settings->v_origin_offset * MM_PER_INCH);
2672
      settings->w_origin_offset = (settings->w_origin_offset * MM_PER_INCH);
2673
2674
      settings->tool_offset.tran.x = GET_EXTERNAL_TOOL_LENGTH_XOFFSET();
2675
      settings->tool_offset.tran.y = GET_EXTERNAL_TOOL_LENGTH_YOFFSET();
2676
      settings->tool_offset.tran.z = GET_EXTERNAL_TOOL_LENGTH_ZOFFSET();
2677
      settings->tool_offset.a = GET_EXTERNAL_TOOL_LENGTH_AOFFSET();
2678
      settings->tool_offset.b = GET_EXTERNAL_TOOL_LENGTH_BOFFSET();
2679
      settings->tool_offset.c = GET_EXTERNAL_TOOL_LENGTH_COFFSET();
2680
      settings->tool_offset.u = GET_EXTERNAL_TOOL_LENGTH_UOFFSET();
2681
      settings->tool_offset.v = GET_EXTERNAL_TOOL_LENGTH_VOFFSET();
2682
      settings->tool_offset.w = GET_EXTERNAL_TOOL_LENGTH_WOFFSET();
2683
      settings->feed_rate = GET_EXTERNAL_FEED_RATE();
2684
    }
2685
  } else
2686
    ERS(NCE_BUG_CODE_NOT_G20_OR_G21);
2687
  return INTERP_OK;
2688
}
2689
2690
2691
/*
2692
 * given two double arrays representing interpreter settings as stored in
2693
 * _setup.active_settings, construct a G-code sequence to synchronize their state.
2694
 */
2695
int Interp::gen_settings(double *current, double *saved, std::string &cmd)
2696
{
2697
    int i;
2698
    char buf[LINELEN];
2699
    for (i = 0; i < ACTIVE_SETTINGS; i++) {
2700
	if (saved[i] != current[i]) {
2701
	    switch (i) {
2702
	    case 0: break; // sequence_number - no point in restoring
2703
	    case 1:
2704
		snprintf(buf,sizeof(buf)," F%.1f", saved[i]);
2705
                cmd += buf;
2706
		break;
2707
	    case 2:
2708
		snprintf(buf,sizeof(buf)," S%.0f", saved[i]);
2709
                cmd += buf;
2710
		break;
2711
	    }
2712
	}
2713
    }
2714
    return INTERP_OK;
2715
}
2716
2717
2718
/*
2719
 * given two int arrays representing interpreter settings as stored in
2720
 * _setup.active_g_codes, construct a G-code sequence to synchronize their state.
2721
 */
2722
int Interp::gen_g_codes(int *current, int *saved, std::string &cmd)
2723
{
2724
    int i, val;
2725
    char buf[LINELEN];
2726
    for (i = 0; i < ACTIVE_G_CODES; i++) {
2727
	val = saved[i];
2728
	if (val != current[i]) {
2729
2730
	    switch (i) {
2731
	    case 0:
2732
		// // sequence_number - no point in restoring
2733
		break;
2734
	    case 2: // FIXME - I dont understand this:
2735
		//   gez[2] = ((block == NULL) ? -1 : block->g_modes[0]);
2736
		break;
2737
	    case 12:  // mystery slot
2738
		break;
2739
	    case 5: // - length units
2740
		// this is treated before all others - see convert_m()
2741
		break;
2742
	    case 1: // - motion_mode
2743
		// restoring the motion mode is a real bad idea to start with
2744
		break;
2745
	    case 3: // - plane
2746
	    case 4: // - cutter compensation
2747
	    case 6: // - distance mode
2748
	    case 7: // - feed mode
2749
	    case 8: // - coordinate system
2750
	    case 9: // - tool offset (G43/G49)
2751
	    case 10: // - retract mode
2752
	    case 11: // - control mode
2753
	    case 13: // - spindle mode
2754
	    case 14: // - ijk distance mode
2755
	    case 15: // - lathe diameter mode
2756
2757
		if (val != -1) { // FIXME not sure if this is correct!
2758
		    // if this was set in sub, and unset in caller, it will
2759
		    // not be reset
2760
		    if (val % 10) {
2761
			snprintf(buf,sizeof(buf)," G%d.%d", val / 10, val % 10);
2762
		    } else {
2763
			snprintf(buf,sizeof(buf)," G%d", val / 10);
2764
		    }
2765
                    cmd += buf;
2766
		} else {
2767
		    // so complain rather loudly
2768
		    MSG("------ gen_g_codes BUG: index %d = -1!!\n",i);
2769
		}
2770
		break;
2771
	    }
2772
	}
2773
    }
2774
    return INTERP_OK;
2775
}
2776
2777
/*
2778
 * given two int arrays representing interpreter settings as stored in
2779
 * _setup.active_m_codes, construct a M-code sequence to synchronize their state.
2780
 *
2781
 * use multiple lines here because M7 and M8 may not be on the same line since
2782
 * they are in the same modal group.
2783
 */
2784
int Interp::gen_m_codes(int *current, int *saved, std::string &cmd)
2785
{
2786
    int i,val;
2787
    char buf[LINELEN];
2788
    for (i = 0; i < ACTIVE_M_CODES; i++) {
2789
	val = saved[i];
2790
	if (val != current[i]) {
2791
	    switch (i) {
2792
	    case 0: /* 0 seq number  */
2793
		break;
2794
	    case 1: /* 1 stopping    */
2795
		// FIXME - is the next line needed at all?
2796
		// emz[1] = (block == NULL) ? -1 : block->m_modes[4];
2797
		break;
2798
	    case 3: /* 3 tool change */
2799
		// FIXME - dont know how to handle this.
2800
		// emz[3] =
2801
		//  (block == NULL) ? -1 : block->m_modes[6];
2802
		break;
2803
	    case 2: // spindle
2804
	    case 4: // mist
2805
	    case 5: // flood
2806
	    case 6: // speed/feed override
2807
	    case 7: // adaptive feed
2808
	    case 8: // feed hold
2809
		if (val != -1) {  // unsure..
2810
		    snprintf(buf,sizeof(buf),"M%d\n", val);
2811
		    cmd += buf;
2812
		} else {
2813
		    MSG("------ gen_m_codes: index %d = -1!!\n",i);
2814
		}
2815
		break;
2816
	    }
2817
	}
2818
    }
2819
    return INTERP_OK;
2820
}
2821
2822
int Interp::save_settings(setup_pointer settings)
2823
{
2824
      // the state is sprinkled all over _setup
2825
      // collate state in _setup.active_* arrays
2826
      write_g_codes((block_pointer) NULL, settings);
2827
      write_m_codes((block_pointer) NULL, settings);
2828
      write_settings(settings);
2829
2830
      // save in the current call frame
2831
      active_g_codes((int *)settings->sub_context[settings->call_level].saved_g_codes);
2832
      active_m_codes((int *)settings->sub_context[settings->call_level].saved_m_codes);
2833
      active_settings((double *)settings->sub_context[settings->call_level].saved_settings);
2834
2835
      // TBD: any other state deemed important to save/restore should be added here
2836
      // context_struct might need to be extended too.
2837
2838
      return INTERP_OK;
2839
}
2840
2841
/* restore global settings/gcodes/mcodes to current call level from a valid context
2842
 * used by:
2843
 *   M72 - restores context from same level
2844
 *       example: restore_settings(settings->call_level)
2845
 *
2846
 *   an o-word return/endsub if auto-restore (M73) was issued
2847
 *       issue this like so - after call_level has been decremented:
2848
 *       restore_settings(settings->call_level + 1)
2849
 */
2850
int Interp::restore_settings(setup_pointer settings,
2851
			    int from_level)    //!< call level of context to restore from
2852
{
2853
2854
    CHKS((from_level < settings->call_level),
2855
	 (_("BUG: cannot restore from a lower call level (%d) to a higher call level (%d)")),from_level,settings->call_level);
2856
    CHKS((from_level < 0), (_("BUG: restore from level %d !?")),from_level);
2857
    CHKS((settings->call_level < 0), (_("BUG: restore to level %d !?")),settings->call_level);
2858
2859
    // linearize state
2860
    write_g_codes((block_pointer) NULL, settings);
2861
    write_m_codes((block_pointer) NULL, settings);
2862
    write_settings(settings);
2863
2864
    std::string cmd;
2865
2866
    // construct gcode from the state difference and execute
2867
    // this assures appropriate canon commands are generated if needed -
2868
    // just restoring interp variables is not enough
2869
2870
    // G20/G21 switching is special - it is executed beforehand
2871
    // so restoring feed lateron is interpreted in the correct context
2872
2873
    if (settings->active_g_codes[5] != settings->sub_context[from_level].saved_g_codes[5]) {
2874
        char buf[LINELEN];
2875
	snprintf(buf,sizeof(buf), "G%d",settings->sub_context[from_level].saved_g_codes[5]/10);
2876
	CHKS(execute(buf) != INTERP_OK, _("M7x: restore_settings G20/G21 failed: '%s'"), cmd.c_str());
2877
    }
2878
    gen_settings((double *)settings->active_settings, (double *)settings->sub_context[from_level].saved_settings,cmd);
2879
    gen_m_codes((int *) settings->active_m_codes, (int *)settings->sub_context[from_level].saved_m_codes,cmd);
2880
    gen_g_codes((int *)settings->active_g_codes, (int *)settings->sub_context[from_level].saved_g_codes,cmd);
2881
2882
    if (!cmd.empty()) {
2883
	// the sequence can be multiline, separated by nl
2884
	// so split and execute each line
2885
        char buf[cmd.size() + 1];
2886
        strncpy(buf, cmd.c_str(), sizeof(buf));
2887
	char *last = buf;
2888
	char *s;
2889
	while ((s = strtok_r(last, "\n", &last)) != NULL) {
2890
	    int status = execute(s);
2891
	    if (status != INTERP_OK) {
2892
		char currentError[LINELEN+1];
2893
		strcpy(currentError,getSavedError());
2894
		CHKS(status, _("M7x: restore_settings failed executing: '%s': %s"), s, currentError);
2895
	    }
2896
	}
2897
	write_g_codes((block_pointer) NULL, settings);
2898
	write_m_codes((block_pointer) NULL, settings);
2899
	write_settings(settings);
2900
    }
2901
2902
    // TBD: any state deemed important to restore should be restored here
2903
    // NB: some state changes might generate canon commands so do that here
2904
    // if needed
2905
2906
    return INTERP_OK;
2907
}
2908
2909
2910
2911
/****************************************************************************/
2912
2913
/*! convert_m
2914
2915
Returned Value: int
2916
   If convert_tool_change returns an error code, this returns that code.
2917
   If input-related stuff is needed, it sets the flag input_flag = true.
2918
   Otherwise, it returns INTERP_OK.
2919
2920
Side effects:
2921
   m_codes in the block are executed. For each m_code
2922
   this consists of making a function call(s) to a canonical machining
2923
   function(s) and setting the machine model.
2924
2925
Called by: execute_block.
2926
2927
This handles four separate types of activity in order:
2928
1. changing the tool (m6) - which also retracts and stops the spindle.
2929
2. Turning the spindle on or off (m3, m4, and m5)
2930
3. Turning coolant on and off (m7, m8, and m9)
2931
4. turning a-axis clamping on and off (m26, m27) - commented out.
2932
5. enabling or disabling feed and speed overrides (m49, m49).
2933
6. changing the loaded toolnumber (m61).
2934
Within each group, only the first code encountered will be executed.
2935
2936
This does nothing with m0, m1, m2, m30, or m60 (which are handled in
2937
convert_stop).
2938
2939
*/
2940
2941
4509
int Interp::convert_m(block_pointer block,       //!< pointer to a block of RS274/NGC instructions
2942
		      setup_pointer settings)    //!< pointer to machine settings
2943
{
2944
  int type;
2945
  double timeout;               // timeout for M66
2946
2947
  /* The M62-65 commands are used for DIO */
2948
  /* M62 sets a DIO synched with motion
2949
     M63 clears a DIO synched with motion
2950
     M64 sets a DIO imediately
2951
     M65 clears a DIO imediately
2952
     M66 waits for an input
2953
     M67 reads a digital input
2954
     M68 reads an analog input*/
2955
2956
4509
  if (IS_USER_MCODE(block,settings,5) && ONCE_M(5))  {
2957
      return convert_remapped_code(block, settings, STEP_M_5, 'm',
2958
				   block->m_modes[5]);
2959
4509
  } else if ((block->m_modes[5] == 62) && ONCE_M(5)) {
2960
      CHKS((settings->cutter_comp_side),
2961
           (_("Cannot set motion output with cutter radius compensation on")));  // XXX
2962
      CHKS((!block->p_flag), _("No valid P word with M62"));
2963
      SET_MOTION_OUTPUT_BIT(round_to_int(block->p_number));
2964
4509
  } else if ((block->m_modes[5] == 63) && ONCE_M(5)) {
2965
      CHKS((settings->cutter_comp_side),
2966
           (_("Cannot set motion digital output with cutter radius compensation on")));  // XXX
2967
      CHKS((!block->p_flag), _("No valid P word with M63"));
2968
      CLEAR_MOTION_OUTPUT_BIT(round_to_int(block->p_number));
2969
4509
  } else if ((block->m_modes[5] == 64) && ONCE_M(5)){
2970
      CHKS((settings->cutter_comp_side),
2971
           (_("Cannot set auxiliary digital output with cutter radius compensation on")));  // XXX
2972
      CHKS((!block->p_flag), _("No valid P word with M64"));
2973
      SET_AUX_OUTPUT_BIT(round_to_int(block->p_number));
2974
4509
  } else if ((block->m_modes[5] == 65) && ONCE_M(5)) {
2975
      CHKS((settings->cutter_comp_side),
2976
           (_("Cannot set auxiliary digital output with cutter radius compensation on")));  // XXX
2977
      CHKS((!block->p_flag), _("No valid P word with M65"));
2978
      CLEAR_AUX_OUTPUT_BIT(round_to_int(block->p_number));
2979
4513
  } else if ((block->m_modes[5] == 66) && ONCE_M(5)){
2980
2981
    //P-word = digital channel
2982
    //E-word = analog channel
2983
    //L-word = wait type (immediate, rise, fall, high, low)
2984
    //Q-word = timeout
2985
    // it is an error if:
2986
2987
    // P and E word are specified together
2988
1
    CHKS(((block->p_flag) && (block->e_flag)),
2989
	NCE_BOTH_DIGITAL_AND_ANALOG_INPUT_SELECTED);
2990
2991
    // L-word not 0, and timeout <= 0
2992
1
    CHKS(((block->q_number <= 0) && (block->l_flag) && (round_to_int(block->l_number) > 0)),
2993
	NCE_ZERO_TIMEOUT_WITH_WAIT_NOT_IMMEDIATE);
2994
2995
    // E-word specified (analog input) and wait type not immediate
2996
1
    CHKS(((block->e_flag) && (block->l_flag) && (round_to_int(block->l_number) != 0)),
2997
	NCE_ANALOG_INPUT_WITH_WAIT_NOT_IMMEDIATE);
2998
2999
    // missing P or E (or invalid = negative)
3000
1
    CHKS( ((block->p_flag) && (round_to_int(block->p_number) < 0)) ||
3001
         ((block->e_flag) && (round_to_int(block->e_number) < 0)) ||
3002
	 ((!block->p_flag) && (!block->e_flag)) ,
3003
	NCE_INVALID_OR_MISSING_P_AND_E_WORDS_FOR_WAIT_INPUT);
3004
3005
1
    if (block->p_flag) { // got a digital input
3006
1
	if (round_to_int(block->p_number) < 0) // safety check for negative words
3007
	    ERS(_("invalid P-word with M66"));
3008
3009
1
	if (block->l_flag) {
3010
1
	    type = round_to_int(block->l_number);
3011
	} else {
3012
	    type = WAIT_MODE_IMMEDIATE;
3013
        }
3014
3015
1
	if (block->q_number > 0) {
3016
	    timeout = block->q_number;
3017
	} else {
3018
	    timeout = 0;
3019
        }
3020
3021
1
        CHKS((settings->cutter_comp_side),
3022
             (_("Cannot wait for digital input with cutter radius compensation on")));
3023
3024
1
	int ret = WAIT(round_to_int(block->p_number), DIGITAL_INPUT, type, timeout);
3025
	//WAIT returns 0 on success, -1 for out of bounds
3026
1
	CHKS((ret == -1), NCE_DIGITAL_INPUT_INVALID_ON_M66);
3027
1
	if (ret == 0) {
3028
1
	    settings->input_flag = true;
3029
1
	    settings->input_index = round_to_int(block->p_number);
3030
1
	    settings->input_digital = true;
3031
	}
3032
    } else if (round_to_int(block->e_number) >= 0) { // got an analog input
3033
        CHKS((settings->cutter_comp_side),
3034
             (_("Cannot wait for analog input with cutter radius compensation on")));
3035
3036
	int ret = WAIT(round_to_int(block->e_number), ANALOG_INPUT, 0, 0); //WAIT returns 0 on success, -1 for out of bounds
3037
	CHKS((ret == -1), NCE_ANALOG_INPUT_INVALID_ON_M66);
3038
	if (ret == 0) {
3039
	    settings->input_flag = true;
3040
	    settings->input_index = round_to_int(block->e_number);
3041
	    settings->input_digital = false;
3042
	}
3043
    }
3044
4508
  } else if ((block->m_modes[5] == 67) && ONCE_M(5)) {
3045
3046
    //E-word = analog channel
3047
    //Q-word = analog value
3048
      CHKS((settings->cutter_comp_side),
3049
           (_("Cannot set motion analog output with cutter radius compensation on")));  // XXX
3050
      CHKS((!block->e_flag) || (round_to_int(block->e_number) < 0), (_("Invalid analog index with M67")));
3051
      SET_MOTION_OUTPUT_VALUE(round_to_int(block->e_number), block->q_number);
3052
4508
  } else if ((block->m_modes[5] == 68)  && ONCE_M(5)) {
3053
    //E-word = analog channel
3054
    //Q-word = analog value
3055
      CHKS((settings->cutter_comp_side),
3056
           (_("Cannot set auxiliary analog output with cutter radius compensation on")));  // XXX
3057
      CHKS((!block->e_flag) || (round_to_int(block->e_number) < 0), (_("Invalid analog index with M68")));
3058
      SET_AUX_OUTPUT_VALUE(round_to_int(block->e_number), block->q_number);
3059
  }
3060
3061
4545
  if ((block->m_modes[6] != -1)  && ONCE_M(6)){
3062
      int toolno;
3063
27
      bool remapped_in_block = STEP_REMAPPED_IN_BLOCK(block, STEP_M_6);
3064
3065
9
      switch (block->m_modes[6]) {
3066
      case 6:
3067
9
      	  if (IS_USER_MCODE(block,settings,6) && remapped_in_block) {
3068
      		  return convert_remapped_code(block,settings,
3069
      					       STEP_M_6,
3070
      					       'm',
3071
      					       block->m_modes[6]);
3072
	  } else {
3073
	      // the code was used in its very remap procedure -
3074
	      // the 'recursion case'; record the fact
3075
9
	      CONTROLLING_BLOCK(*settings).builtin_used = !remapped_in_block;
3076
9
	      CHP(convert_tool_change(settings));
3077
	  }
3078
      	  break;
3079
3080
      case 61:
3081
	  if (IS_USER_MCODE(block,settings,6) && remapped_in_block) {
3082
	      return convert_remapped_code(block, settings, STEP_M_6,'m',
3083
					   block->m_modes[6]);
3084
	  } else {
3085
	      CONTROLLING_BLOCK(*settings).builtin_used = !remapped_in_block;
3086
	      toolno = round_to_int(block->q_number);
3087
	      // now also accept M61 Q0 - unload tool
3088
	      CHKS((toolno < 0), (_("Need non-negative Q-word to specify tool number with M61")));
3089
3090
	      int pocket;
3091
3092
	      // make sure selected tool exists
3093
	      CHP((find_tool_pocket(settings, toolno, &pocket)));
3094
	      settings->current_pocket = pocket;
3095
	      settings->toolchange_flag = true;
3096
	      CHANGE_TOOL_NUMBER(settings->current_pocket);
3097
	      set_tool_parameters();
3098
	  }
3099
	  break;
3100
3101
      default:
3102
	  if (IS_USER_MCODE(block,settings,6)) {
3103
	      return convert_remapped_code(block, settings, STEP_M_6,'m',
3104
					   block->m_modes[6]);
3105
	  }
3106
      }
3107
  }
3108
3109
  // needs more testing.. once? test tool_change_flag!
3110
  //#ifdef DEBATABLE
3111
4509
  if (FEATURE(RETAIN_G43)) {
3112
    // I consider this useful, so make it configurable.
3113
    // Turn it on optionally . -mah
3114
3115
    // I would like this, but it's a big change.  It changes the
3116
    // operation of legal ngc programs, but it could be argued that
3117
    // those programs are buggy or likely to be not what the author
3118
    // intended.
3119
3120
    // It would allow you to turn on G43 after loading the first tool,
3121
    // and then not worry about it through the program.  When you
3122
    // finally unload the last tool, G43 mode is canceled.
3123
3124
10
      if ((settings->active_g_codes[9] == G_43) && ONCE(STEP_RETAIN_G43)) {
3125
        if(settings->selected_pocket > 0) {
3126
            struct block_struct g43;
3127
            init_block(&g43);
3128
            block->g_modes[_gees[G_43]] = G_43;
3129
            CHP(convert_tool_length_offset(G_43, &g43, settings));
3130
        } else {
3131
            struct block_struct g49;
3132
            init_block(&g49);
3133
            block->g_modes[_gees[G_49]] = G_49;
3134
            CHP(convert_tool_length_offset(G_49, &g49, settings));
3135
        }
3136
    }
3137
  }
3138
  //#endif
3139
3140
4509
 if (IS_USER_MCODE(block,settings,7) && ONCE_M(7)) {
3141
    return convert_remapped_code(block, settings, STEP_M_7, 'm',
3142
				   block->m_modes[7]);
3143
4525
 } else if ((block->m_modes[7] == 3)  && ONCE_M(7)) {
3144
4
    enqueue_START_SPINDLE_CLOCKWISE();
3145
4
    settings->spindle_turning = CANON_CLOCKWISE;
3146
4505
 } else if ((block->m_modes[7] == 4) && ONCE_M(7)) {
3147
    enqueue_START_SPINDLE_COUNTERCLOCKWISE();
3148
    settings->spindle_turning = CANON_COUNTERCLOCKWISE;
3149
4521
 } else if ((block->m_modes[7] == 5) && ONCE_M(7)){
3150
4
    enqueue_STOP_SPINDLE_TURNING();
3151
4
    settings->spindle_turning = CANON_STOPPED;
3152
4525
  } else if ((block->m_modes[7] == 19) && ONCE_M(7)) {
3153
6
      settings->spindle_turning = CANON_STOPPED;
3154
6
      if (block->r_flag || block->p_flag)
3155
3
      enqueue_ORIENT_SPINDLE(block->r_flag ? (block->r_number + settings->orient_offset) : settings->orient_offset,
3156
7
			     block->p_flag ? block->p_number : 0);
3157
6
      if (block->q_flag) {
3158
2
	  CHKS((block->q_number <= 0.0),(_("Q word with M19 requires a value > 0")));
3159
2
	  enqueue_WAIT_ORIENT_SPINDLE_COMPLETE(block->q_number);
3160
      }
3161
4495
  } else if ((block->m_modes[7] == 70) || (block->m_modes[7] == 73)) {
3162
3163
     // save state in current stack frame. We borrow the o-word call stack
3164
     // and extend it to hold modes & settings.
3165
     save_settings(&_setup);
3166
3167
     // flag this frame as containing a valid context
3168
     _setup.sub_context[_setup.call_level].context_status |= CONTEXT_VALID;
3169
3170
     // mark as auto-restore context
3171
     if (block->m_modes[7] == 73) {
3172
	 if (_setup.call_level == 0) {
3173
	     MSG("Warning - M73 at top level: nothing to return to; storing context anyway\n");
3174
	 } else {
3175
	     _setup.sub_context[_setup.call_level].context_status |= CONTEXT_RESTORE_ON_RETURN;
3176
	 }
3177
     }
3178
4495
 } else if ((block->m_modes[7] == 71) && ONCE_M(7))  {
3179
      // M72 - invalidate context at current level
3180
      _setup.sub_context[_setup.call_level].context_status &= ~CONTEXT_VALID;
3181
3182
4495
 } else if ((block->m_modes[7] == 72)  && ONCE_M(7)) {
3183
3184
      // restore state from current stack frame.
3185
      CHKS((!(_setup.sub_context[_setup.call_level].context_status & CONTEXT_VALID)),
3186
           (_("Cannot restore context from invalid stack frame - missing M70/M73?")));
3187
      CHP(restore_settings(&_setup, _setup.call_level));
3188
  }
3189
3190
4509
  if (IS_USER_MCODE(block,settings,8) && ONCE_M(8)) {
3191
     return convert_remapped_code(block, settings, STEP_M_8, 'm',
3192
				   block->m_modes[8]);
3193
4509
  } else if ((block->m_modes[8] == 7) && ONCE_M(8)){
3194
      enqueue_MIST_ON();
3195
      settings->mist = true;
3196
4509
  } else if ((block->m_modes[8] == 8) && ONCE_M(8)) {
3197
      enqueue_FLOOD_ON();
3198
      settings->flood = true;
3199
4509
  } else if ((block->m_modes[8] == 9) && ONCE_M(8)) {
3200
      enqueue_MIST_OFF();
3201
      settings->mist = false;
3202
      enqueue_FLOOD_OFF();
3203
      settings->flood = false;
3204
  }
3205
3206
/* No axis clamps in this version
3207
  if (block->m_modes[2] == 26)
3208
    {
3209
#ifdef DEBUG_EMC
3210
      COMMENT("interpreter: automatic A-axis clamping turned on");
3211
#endif
3212
      settings->a_axis_clamping = true;
3213
    }
3214
  else if (block->m_modes[2] == 27)
3215
    {
3216
#ifdef DEBUG_EMC
3217
      COMMENT("interpreter: automatic A-axis clamping turned off");
3218
#endif
3219
      settings->a_axis_clamping = false;
3220
    }
3221
*/
3222
4509
if (IS_USER_MCODE(block,settings,9) && ONCE_M(9)) {
3223
     return convert_remapped_code(block, settings, STEP_M_9, 'm',
3224
				   block->m_modes[9]);
3225
4509
 } else if ((block->m_modes[9] == 48)  && ONCE_M(9)){
3226
    CHKS((settings->cutter_comp_side),
3227
         (_("Cannot enable overrides with cutter radius compensation on")));  // XXX
3228
    ENABLE_FEED_OVERRIDE();
3229
    ENABLE_SPEED_OVERRIDE();
3230
    settings->feed_override = true;
3231
    settings->speed_override = true;
3232
4509
 } else if ((block->m_modes[9] == 49)  && ONCE_M(9)){
3233
    CHKS((settings->cutter_comp_side),
3234
         (_("Cannot disable overrides with cutter radius compensation on")));  // XXX
3235
    DISABLE_FEED_OVERRIDE();
3236
    DISABLE_SPEED_OVERRIDE();
3237
    settings->feed_override = false;
3238
    settings->speed_override = false;
3239
  }
3240
3241
4509
if ((block->m_modes[9] == 50)  && ONCE_M(9)){
3242
    if (block->p_number != 0) {
3243
        CHKS((settings->cutter_comp_side),
3244
             (_("Cannot enable overrides with cutter radius compensation on")));  // XXX
3245
	ENABLE_FEED_OVERRIDE();
3246
	settings->feed_override = true;
3247
    } else {
3248
        CHKS((settings->cutter_comp_side),
3249
             (_("Cannot disable overrides with cutter radius compensation on")));  // XXX
3250
        DISABLE_FEED_OVERRIDE();
3251
	settings->feed_override = false;
3252
    }
3253
  }
3254
3255
4509
if ((block->m_modes[9] == 51)  && ONCE_M(9)){
3256
3257
    if (block->p_number != 0) {
3258
        CHKS((settings->cutter_comp_side),
3259
             (_("Cannot enable overrides with cutter radius compensation on")));  // XXX
3260
	ENABLE_SPEED_OVERRIDE();
3261
	settings->speed_override = true;
3262
    } else {
3263
        CHKS((settings->cutter_comp_side),
3264
             (_("Cannot disable overrides with cutter radius compensation on")));  // XXX
3265
	DISABLE_SPEED_OVERRIDE();
3266
	settings->speed_override = false;
3267
    }
3268
  }
3269
3270
4509
if ((block->m_modes[9] == 52)  && ONCE_M(9)){
3271
    if (block->p_number != 0) {
3272
        CHKS((settings->cutter_comp_side),
3273
             (_("Cannot enable overrides with cutter radius compensation on")));  // XXX
3274
	ENABLE_ADAPTIVE_FEED();
3275
	settings->adaptive_feed = true;
3276
    } else {
3277
        CHKS((settings->cutter_comp_side),
3278
             (_("Cannot disable overrides with cutter radius compensation on")));  // XXX
3279
	DISABLE_ADAPTIVE_FEED();
3280
	settings->adaptive_feed = false;
3281
    }
3282
  }
3283
3284
4509
if ((block->m_modes[9] == 53)  && ONCE_M(9)){
3285
    if (block->p_number != 0) {
3286
        CHKS((settings->cutter_comp_side),
3287
             (_("Cannot enable overrides with cutter radius compensation on")));  // XXX
3288
	ENABLE_FEED_HOLD();
3289
	settings->feed_hold = true;
3290
    } else {
3291
        CHKS((settings->cutter_comp_side),
3292
             (_("Cannot disable overrides with cutter radius compensation on")));  // XXX
3293
	DISABLE_FEED_HOLD();
3294
	settings->feed_hold = false;
3295
    }
3296
  }
3297
3298
4509
if (IS_USER_MCODE(block,settings,10) && ONCE_M(10)) {
3299
    return convert_remapped_code(block,settings,STEP_M_10,'m',
3300
				   block->m_modes[10]);
3301
3302
4509
 } else if ((block->m_modes[10] != -1)  && ONCE_M(10)){
3303
     /* user-defined M codes */
3304
    int index = block->m_modes[10];
3305
    if (USER_DEFINED_FUNCTION[index - 100] == 0) {
3306
      CHKS(1, NCE_UNKNOWN_M_CODE_USED,index);
3307
    }
3308
    enqueue_M_USER_COMMAND(index,block->p_number,block->q_number);
3309
  }
3310
  return INTERP_OK;
3311
}
3312
3313
/****************************************************************************/
3314
3315
/*! convert_modal_0
3316
3317
Returned Value: int
3318
   If one of the following functions is called and returns an error code,
3319
   this returns that code.
3320
      convert_axis_offsets
3321
      convert_home
3322
      convert_setup
3323
   If any of the following errors occur, this returns the error code shown.
3324
   Otherwise, it returns INTERP_OK.
3325
   1. code is not G_4, G_10, G_28, G_30, G_52, G_53, G_92, G_92_1, G_92_2,
3326
          or G_92_3:
3327
      NCE_BUG_CODE_NOT_G4_G10_G28_G30_G52_G53_OR_G92_SERIES
3328
3329
Side effects: See below
3330
3331
Called by: convert_g
3332
3333
If the g_code is g10, g28, g30, g52, g92, g92.1, g92.2, or g92.3 (all are in
3334
modal group 0), it is executed. The other two in modal group 0 (G4 and
3335
G53) are executed elsewhere.
3336
3337
*/
3338
3339
82
int Interp::convert_modal_0(int code,    //!< G code, must be from group 0
3340
                           block_pointer block, //!< pointer to a block of RS274/NGC instructions
3341
                           setup_pointer settings)      //!< pointer to machine settings
3342
{
3343
3344
82
  if (code == G_10) {
3345
58
      if(block->l_number == 1 || block->l_number == 10 || block->l_number == 11)
3346
26
          CHP(convert_setup_tool(block, settings));
3347
      else
3348
32
          CHP(convert_setup(block, settings));
3349
24
  } else if ((code == G_28) || (code == G_30)) {
3350
    CHP(convert_home(code, block, settings));
3351
24
  } else if ((code == G_28_1) || (code == G_30_1)) {
3352
    CHP(convert_savehome(code, block, settings));
3353
48
  } else if ((code == G_52) ||
3354
43
	     (code == G_92) || (code == G_92_1) ||
3355
14
             (code == G_92_2) || (code == G_92_3)) {
3356
12
    CHP(convert_axis_offsets(code, block, settings));
3357
12
  } else if (code == G_5_3) {
3358
1
    CHP(convert_nurbs(code, block, settings));
3359
11
  } else if ((code == G_4) || (code == G_53));  /* handled elsewhere */
3360
  else
3361
    ERS(NCE_BUG_CODE_NOT_G4_G10_G28_G30_G52_G53_OR_G92_SERIES);
3362
  return INTERP_OK;
3363
}
3364
3365
/****************************************************************************/
3366
3367
/*! convert_motion
3368
3369
Returned Value: int
3370
   If one of the following functions is called and returns an error code,
3371
   this returns that code.
3372
      convert_arc
3373
      convert_cycle
3374
      convert_probe
3375
      convert_straight
3376
   If any of the following errors occur, this returns the error shown.
3377
   Otherwise, it returns INTERP_OK.
3378
   1. The motion code is not 0,1,2,3,38.2,80,81,82,83,84,85,86,87, 88, or 89:
3379
      NCE_BUG_UNKNOWN_MOTION_CODE
3380
3381
Side effects:
3382
   A g_code from the group causing motion (mode 1) is executed.
3383
3384
Called by: convert_g.
3385
3386
*/
3387
3388
1459
int Interp::convert_motion(int motion,   //!< g_code for a line, arc, canned cycle
3389
                          block_pointer block,  //!< pointer to a block of RS274 instructions
3390
                          setup_pointer settings)       //!< pointer to machine settings
3391
{
3392
1459
  int ai = block->a_flag && (-1 != settings->a_indexer_jnum);
3393
1459
  int bi = block->b_flag && (-1 != settings->b_indexer_jnum);
3394
1459
  int ci = block->c_flag && (-1 != settings->c_indexer_jnum);
3395
3396
3397
1459
  if (motion != G_0) {
3398
1300
      CHKS((ai), (_("Indexing axis %c can only be moved with G0")), 'A');
3399
1300
      CHKS((bi), (_("Indexing axis %c can only be moved with G0")), 'B');
3400
1300
      CHKS((ci), (_("Indexing axis %c can only be moved with G0")), 'C');
3401
  }
3402
3403
251
  int xyzuvw_flag = (block->x_flag || block->y_flag || block->z_flag ||
3404
1461
                     block->u_flag || block->v_flag || block->w_flag);
3405
3406
1459
  CHKS((ai && (xyzuvw_flag || block->b_flag || block->c_flag)),
3407
       (_("Indexing axis %c can only be moved alone")), 'A');
3408
1459
  CHKS((bi && (xyzuvw_flag || block->a_flag || block->c_flag)),
3409
       (_("Indexing axis %c can only be moved alone")), 'B');
3410
1459
  CHKS((ci && (xyzuvw_flag || block->a_flag || block->b_flag)),
3411
       (_("Indexing axis %c can only be moved alone")), 'C');
3412
3413
1459
  if (!is_a_cycle(motion))
3414
1422
    settings->cycle_il_flag = false;
3415
3416
1459
  if (ai || bi || ci) {
3417
    int anum=-1,jnum=-1;
3418
    if (     ai) {anum = 3; jnum = settings->a_indexer_jnum;}
3419
    else if (bi) {anum = 4; jnum = settings->b_indexer_jnum;}
3420
    else if (ci) {anum = 5; jnum = settings->c_indexer_jnum;}
3421
    CHP(convert_straight_indexer(anum, jnum, block, settings));
3422
1459
  } else if ((motion == G_0) || (motion == G_1) || (motion == G_33) || (motion == G_33_1) || (motion == G_76)) {
3423
880
    CHP(convert_straight(motion, block, settings));
3424
579
  } else if ((motion == G_3) || (motion == G_2)) {
3425
537
    CHP(convert_arc(motion, block, settings));
3426
42
  } else if (motion == G_38_2 || motion == G_38_3 ||
3427
42
             motion == G_38_4 || motion == G_38_5) {
3428
    CHP(convert_probe(block, motion, settings));
3429
42
  } else if (motion == G_80) {
3430
#ifdef DEBUG_EMC
3431
1
    enqueue_COMMENT("interpreter: motion mode set to none");
3432
#endif
3433
1
    settings->motion_mode = G_80;
3434
41
  } else if (IS_USER_GCODE(motion)) {
3435
      CHP(convert_remapped_code(block, settings, STEP_MOTION, 'g', motion));
3436
41
  } else if (is_a_cycle(motion)) {
3437
37
    CHP(convert_cycle(motion, block, settings));
3438
4
  } else if ((motion == G_5) || (motion == G_5_1)) {
3439
    CHP(convert_spline(motion, block, settings));
3440
4
  } else if (motion == G_5_2) {
3441
4
    CHP(convert_nurbs(motion, block, settings));
3442
  } else {
3443
    ERS(NCE_BUG_UNKNOWN_MOTION_CODE);
3444
  }
3445
3446
  return INTERP_OK;
3447
}
3448
3449
/****************************************************************************/
3450
3451
/*! convert_probe
3452
3453
Returned Value: int
3454
   If any of the following errors occur, this returns the error code shown.
3455
   Otherwise, it returns INTERP_OK.
3456
   1. No value is given in the block for any of X, Y, or Z:
3457
      NCE_X_Y_AND_Z_WORDS_ALL_MISSING_WITH_G38_2
3458
   3. cutter radius comp is on: NCE_CANNOT_PROBE_WITH_CUTTER_RADIUS_COMP_ON
3459
   4. Feed rate is zero: NCE_CANNOT_PROBE_WITH_ZERO_FEED_RATE
3460
   5. The move is degenerate (already at the specified point)
3461
      NCE_START_POINT_TOO_CLOSE_TO_PROBE_POINT
3462
3463
Side effects:
3464
   This executes a straight_probe command.
3465
   The probe_flag in the settings is set to true.
3466
   The motion mode in the settings is set to G_38_2.
3467
3468
Called by: convert_motion.
3469
3470
The approach to operating in incremental distance mode (g91) is to
3471
put the the absolute position values into the block before using the
3472
block to generate a move.
3473
3474
After probing is performed, the location of the probe cannot be
3475
predicted. This differs from every other command, all of which have
3476
predictable results. The next call to the interpreter (with either
3477
Interp::read or Interp::execute) will result in updating the
3478
current position by calls to get_external_position_x, etc.
3479
3480
*/
3481
3482
int Interp::convert_probe(block_pointer block,   //!< pointer to a block of RS274 instructions
3483
                          int g_code,
3484
                          setup_pointer settings)        //!< pointer to machine settings
3485
{
3486
  double end_x;
3487
  double end_y;
3488
  double end_z;
3489
  double AA_end;
3490
  double BB_end;
3491
  double CC_end;
3492
  double u_end;
3493
  double v_end;
3494
  double w_end;
3495
3496
  /* probe_type:
3497
     ~1 = error if probe operation is unsuccessful (ngc default)
3498
     |1 = suppress error, report in # instead
3499
     ~2 = move until probe trips (ngc default)
3500
     |2 = move until probe clears */
3501
3502
  unsigned char probe_type = g_code - G_38_2;
3503
3504
  CHKS((settings->cutter_comp_side),
3505
      NCE_CANNOT_PROBE_WITH_CUTTER_RADIUS_COMP_ON);
3506
  CHKS((settings->feed_rate == 0.0), NCE_CANNOT_PROBE_WITH_ZERO_FEED_RATE);
3507
  CHKS(settings->feed_mode == UNITS_PER_REVOLUTION,
3508
	  _("Cannot probe with feed per rev mode"));
3509
  CHP(find_ends(block, settings, &end_x, &end_y, &end_z,
3510
                &AA_end, &BB_end, &CC_end,
3511
                &u_end, &v_end, &w_end));
3512
  CHKS(((!(probe_type & 1)) &&
3513
        settings->current_x == end_x && settings->current_y == end_y &&
3514
        settings->current_z == end_z && settings->AA_current == AA_end &&
3515
        settings->BB_current == BB_end && settings->CC_current == CC_end &&
3516
        settings->u_current == u_end && settings->v_current == v_end &&
3517
        settings->w_current == w_end),
3518
       NCE_START_POINT_TOO_CLOSE_TO_PROBE_POINT);
3519
3520
  TURN_PROBE_ON();
3521
  STRAIGHT_PROBE(block->line_number, end_x, end_y, end_z,
3522
                 AA_end, BB_end, CC_end,
3523
                 u_end, v_end, w_end, probe_type);
3524
3525
  TURN_PROBE_OFF();
3526
  settings->motion_mode = g_code;
3527
  settings->probe_flag = true;
3528
  return INTERP_OK;
3529
}
3530
3531
/****************************************************************************/
3532
3533
/*! convert_retract_mode
3534
3535
Returned Value: int
3536
   If any of the following errors occur, this returns the error code shown.
3537
   Otherwise, it returns INTERP_OK.
3538
   1. g_code isn't G_98 or G_99: NCE_BUG_CODE_NOT_G98_OR_G99
3539
3540
Side effects:
3541
   The interpreter switches the machine settings to indicate the current
3542
   retract mode for canned cycles (OLD_Z or R_PLANE).
3543
3544
Called by: convert_g.
3545
3546
The canonical machine to which commands are being sent does not have a
3547
retract mode, so no command setting the retract mode is generated in
3548
this function.
3549
3550
*/
3551
3552
6
int Interp::convert_retract_mode(int g_code,     //!< g_code being executed (must be G_98 or G_99)
3553
                                setup_pointer settings) //!< pointer to machine settings
3554
{
3555
6
  CHKS((settings->cutter_comp_side),
3556
       (_("Cannot change retract mode with cutter radius compensation on")));
3557
6
  if (g_code == G_98) {
3558
#ifdef DEBUG_EMC
3559
3
    enqueue_COMMENT("interpreter: retract mode set to old_z");
3560
#endif
3561
3
    settings->retract_mode = OLD_Z;
3562
3
  } else if (g_code == G_99) {
3563
#ifdef DEBUG_EMC
3564
3
    enqueue_COMMENT("interpreter: retract mode set to r_plane");
3565
#endif
3566
3
    settings->retract_mode = R_PLANE;
3567
  } else
3568
    ERS(NCE_BUG_CODE_NOT_G98_OR_G99);
3569
  return INTERP_OK;
3570
}
3571
3572
// G10 L1  P[tool number] R[radius] X[x offset] Z[z offset] Q[orientation]
3573
// G10 L10 P[tool number] R[radius] X[x offset] Z[z offset] Q[orientation]
3574
3575
26
int Interp::convert_setup_tool(block_pointer block, setup_pointer settings) {
3576
26
    int pocket = -1, toolno;
3577
    int q;
3578
    double tx, ty, tz, ta, tb, tc, tu, tv, tw;
3579
26
    int direct = block->l_number == 1;
3580
3581
26
    is_near_int(&toolno, block->p_number);
3582
3583
26
    CHP((find_tool_pocket(settings, toolno, &pocket)));
3584
3585
26
    CHKS(!(block->x_flag || block->y_flag || block->z_flag ||
3586
	   block->a_flag || block->b_flag || block->c_flag ||
3587
	   block->u_flag || block->v_flag || block->w_flag ||
3588
	   block->r_flag || block->q_flag || block->i_flag ||
3589
           block->j_flag),
3590
	 _("G10 L1 without offsets has no effect"));
3591
3592
26
    if(direct) {
3593
9
        if(block->x_flag)
3594
5
            settings->tool_table[pocket].offset.tran.x = PROGRAM_TO_USER_LEN(block->x_number);
3595
9
        if(block->y_flag)
3596
5
            settings->tool_table[pocket].offset.tran.y = PROGRAM_TO_USER_LEN(block->y_number);
3597
9
        if(block->z_flag)
3598
5
            settings->tool_table[pocket].offset.tran.z = PROGRAM_TO_USER_LEN(block->z_number);
3599
9
        if(block->a_flag)
3600
            settings->tool_table[pocket].offset.a = PROGRAM_TO_USER_ANG(block->a_number);
3601
9
        if(block->b_flag)
3602
            settings->tool_table[pocket].offset.b = PROGRAM_TO_USER_ANG(block->b_number);
3603
9
        if(block->c_flag)
3604
            settings->tool_table[pocket].offset.c = PROGRAM_TO_USER_ANG(block->c_number);
3605
9
        if(block->u_flag)
3606
            settings->tool_table[pocket].offset.u = PROGRAM_TO_USER_LEN(block->u_number);
3607
9
        if(block->v_flag)
3608
            settings->tool_table[pocket].offset.v = PROGRAM_TO_USER_LEN(block->v_number);
3609
9
        if(block->w_flag)
3610
            settings->tool_table[pocket].offset.w = PROGRAM_TO_USER_LEN(block->w_number);
3611
    } else {
3612
17
        int to_fixture = block->l_number == 11;
3613
17
        int destination_system = to_fixture? 9 : settings->origin_index; // maybe 9 (g59.3) should be user configurable?
3614
3615
        find_current_in_system_without_tlo(settings, destination_system,
3616
                                           &tx, &ty, &tz,
3617
                                           &ta, &tb, &tc,
3618
17
                                           &tu, &tv, &tw);
3619
3620
17
        if ( to_fixture && settings->parameters[5210]) {
3621
            // For G10L11, we don't want to move the origin of the
3622
            // fixture according to G92.  Since find_current_in_system
3623
            // did this for us already, undo it.
3624
2
            tx += USER_TO_PROGRAM_LEN(settings->parameters[5211]);
3625
2
            ty += USER_TO_PROGRAM_LEN(settings->parameters[5212]);
3626
2
            tz += USER_TO_PROGRAM_LEN(settings->parameters[5213]);
3627
2
            ta += USER_TO_PROGRAM_ANG(settings->parameters[5214]);
3628
2
            tb += USER_TO_PROGRAM_ANG(settings->parameters[5215]);
3629
2
            tc += USER_TO_PROGRAM_ANG(settings->parameters[5216]);
3630
2
            tu += USER_TO_PROGRAM_LEN(settings->parameters[5217]);
3631
2
            tv += USER_TO_PROGRAM_LEN(settings->parameters[5218]);
3632
2
            tw += USER_TO_PROGRAM_LEN(settings->parameters[5219]);
3633
        }
3634
3635
3636
17
        if(block->x_flag && block->y_flag) {
3637
3
            tx -= block->x_number;
3638
3
            ty -= block->y_number;
3639
3
            rotate(&tx, &ty, settings->parameters[5210 + destination_system * 20]);
3640
3
            settings->tool_table[pocket].offset.tran.x = PROGRAM_TO_USER_LEN(tx);
3641
3
            settings->tool_table[pocket].offset.tran.y = PROGRAM_TO_USER_LEN(ty);
3642
14
        } else if(block->x_flag) {
3643
            // keep the component of the tool table's current setting that points
3644
            // along our possibly-rotated Y axis
3645
            double ox, oy;
3646
4
            ox = USER_TO_PROGRAM_LEN(settings->tool_table[pocket].offset.tran.x);
3647
4
            oy = USER_TO_PROGRAM_LEN(settings->tool_table[pocket].offset.tran.y);
3648
4
            rotate(&ox, &oy, -settings->parameters[5210 + destination_system * 20]);
3649
4
            ox = 0;
3650
4
            rotate(&ox, &oy, settings->parameters[5210 + destination_system * 20]);
3651
3652
3653
4
            tx -= block->x_number;
3654
4
            ty = 0;
3655
4
            rotate(&tx, &ty, settings->parameters[5210 + destination_system * 20]);
3656
3657
4
            settings->tool_table[pocket].offset.tran.x = PROGRAM_TO_USER_LEN(tx + ox);
3658
4
            settings->tool_table[pocket].offset.tran.y = PROGRAM_TO_USER_LEN(ty + oy);
3659
10
        } else if(block->y_flag) {
3660
            double ox, oy;
3661
5
            ox = USER_TO_PROGRAM_LEN(settings->tool_table[pocket].offset.tran.x);
3662
5
            oy = USER_TO_PROGRAM_LEN(settings->tool_table[pocket].offset.tran.y);
3663
5
            rotate(&ox, &oy, -settings->parameters[5210 + destination_system * 20]);
3664
5
            oy = 0;
3665
5
            rotate(&ox, &oy, settings->parameters[5210 + destination_system * 20]);
3666
3667
5
            ty -= block->y_number;
3668
5
            tx = 0;
3669
3670
5
            rotate(&tx, &ty, settings->parameters[5210 + destination_system * 20]);
3671
5
            settings->tool_table[pocket].offset.tran.x = PROGRAM_TO_USER_LEN(tx + ox);
3672
5
            settings->tool_table[pocket].offset.tran.y = PROGRAM_TO_USER_LEN(ty + oy);
3673
        }
3674
3675
3676
17
        if(block->z_flag)
3677
9
            settings->tool_table[pocket].offset.tran.z = PROGRAM_TO_USER_LEN(tz - block->z_number);
3678
17
        if(block->a_flag)
3679
            settings->tool_table[pocket].offset.a = PROGRAM_TO_USER_ANG(ta - block->a_number);
3680
17
        if(block->b_flag)
3681
            settings->tool_table[pocket].offset.b = PROGRAM_TO_USER_ANG(tb - block->b_number);
3682
17
        if(block->c_flag)
3683
            settings->tool_table[pocket].offset.c = PROGRAM_TO_USER_ANG(tc - block->c_number);
3684
17
        if(block->u_flag)
3685
            settings->tool_table[pocket].offset.u = PROGRAM_TO_USER_LEN(tu - block->u_number);
3686
17
        if(block->v_flag)
3687
            settings->tool_table[pocket].offset.v = PROGRAM_TO_USER_LEN(tv - block->v_number);
3688
17
        if(block->w_flag)
3689
            settings->tool_table[pocket].offset.w = PROGRAM_TO_USER_LEN(tw - block->w_number);
3690
    }
3691
3692
26
    if(block->r_flag) settings->tool_table[pocket].diameter = PROGRAM_TO_USER_LEN(block->r_number) * 2.;
3693
26
    if(block->i_flag) settings->tool_table[pocket].frontangle = block->i_number;
3694
26
    if(block->j_flag) settings->tool_table[pocket].backangle = block->j_number;
3695
26
    if(block->q_number != -1.0) {
3696
        CHKS((!is_near_int(&q, block->q_number)), _("Q number in G10 is not an integer"));
3697
        CHKS((q > 9), _("Invalid tool orientation"));
3698
        settings->tool_table[pocket].orientation = q;
3699
    }
3700
3701
    SET_TOOL_TABLE_ENTRY(pocket,
3702
                             settings->tool_table[pocket].toolno,
3703
                             settings->tool_table[pocket].offset,
3704
                             settings->tool_table[pocket].diameter,
3705
                             settings->tool_table[pocket].frontangle,
3706
                             settings->tool_table[pocket].backangle,
3707
26
                             settings->tool_table[pocket].orientation);
3708
3709
    //
3710
    // On non-random tool changers we just updated the tool's "home pocket"
3711
    // in the tool changer carousel, so now, if the tool is currently
3712
    // loaded, we need to copy the new tool information to the spindle
3713
    // (pocket 0).  This is never needed on random tool changers because
3714
    // there tools don't have a home pocket, and instead we updated pocket
3715
    // 0 (the spindle) directly when modifying the loaded tool.
3716
    //
3717
26
    if ((!settings->random_toolchanger) && (settings->current_pocket == pocket)) {
3718
26
       settings->tool_table[0] = settings->tool_table[pocket];
3719
    }
3720
3721
    //
3722
    // Update parameter #5400 with the tool currently in the spindle, or a
3723
    // special "invalid tool number" marker if no tool is in the spindle.
3724
    // Unfortunately, random and nonrandom toolchangers use a different
3725
    // number for "invalid tool number":  nonrandom uses 0, random uses -1.
3726
    //
3727
26
    if (settings->random_toolchanger)
3728
        if (settings->tool_table[0].toolno >= 0) {
3729
            settings->parameters[5400] = settings->tool_table[0].toolno;
3730
        } else {
3731
            settings->parameters[5400] = -1;
3732
    } else {
3733
26
        if (settings->tool_table[0].toolno > 0) {
3734
26
            settings->parameters[5400] = settings->tool_table[0].toolno;
3735
        } else {
3736
            settings->parameters[5400] = 0;
3737
        }
3738
    }
3739
3740
26
    settings->parameters[5401] = settings->tool_table[0].offset.tran.x;
3741
26
    settings->parameters[5402] = settings->tool_table[0].offset.tran.y;
3742
26
    settings->parameters[5403] = settings->tool_table[0].offset.tran.z;
3743
26
    settings->parameters[5404] = settings->tool_table[0].offset.a;
3744
26
    settings->parameters[5405] = settings->tool_table[0].offset.b;
3745
26
    settings->parameters[5406] = settings->tool_table[0].offset.c;
3746
26
    settings->parameters[5407] = settings->tool_table[0].offset.u;
3747
26
    settings->parameters[5408] = settings->tool_table[0].offset.v;
3748
26
    settings->parameters[5409] = settings->tool_table[0].offset.w;
3749
26
    settings->parameters[5410] = settings->tool_table[0].diameter;
3750
26
    settings->parameters[5411] = settings->tool_table[0].frontangle;
3751
26
    settings->parameters[5412] = settings->tool_table[0].backangle;
3752
26
    settings->parameters[5413] = settings->tool_table[0].orientation;
3753
3754
    // if the modified tool is currently in the spindle, then copy its
3755
    // information to pocket 0 of the tool table (which signifies the
3756
    // spindle)
3757
26
    if (   !_setup.random_toolchanger
3758
26
        && pocket == settings->current_pocket) {
3759
        SET_TOOL_TABLE_ENTRY(0,
3760
                             settings->tool_table[pocket].toolno,
3761
                             settings->tool_table[pocket].offset,
3762
                             settings->tool_table[pocket].diameter,
3763
                             settings->tool_table[pocket].frontangle,
3764
                             settings->tool_table[pocket].backangle,
3765
26
                             settings->tool_table[pocket].orientation);
3766
    }
3767
3768
    return INTERP_OK;
3769
}
3770
3771
/****************************************************************************/
3772
3773
/*! convert_setup
3774
3775
Returned Value: int (INTERP_OK)
3776
3777
Side effects:
3778
   SET_G5X_OFFSET is called, and the coordinate
3779
   values for the program origin are reset.
3780
   If the program origin is currently in use, the values of the
3781
   the coordinates of the current point are updated.
3782
3783
Called by: convert_modal_0.
3784
3785
This is called only if g10 is called. g10 L2 may be used to alter the
3786
location of coordinate systems as described in [NCMS, pages 9 - 10] and
3787
[Fanuc, page 65]. [Fanuc] has only six coordinate systems, while
3788
[NCMS] has nine (the first six of which are the same as the six [Fanuc]
3789
has). All nine are implemented here.
3790
3791
Being in incremental distance mode has no effect on the action of G10
3792
in this implementation. The manual is not explicit about what is
3793
intended.
3794
3795
If L is 20 instead of 2, the coordinates are relative to the current
3796
position instead of the origin.  Like how G92 offsets are programmed,
3797
the meaning is "set the coordinate system origin such that my current
3798
position becomes the specified value".
3799
3800
See documentation of convert_coordinate_system for more information.
3801
3802
*/
3803
3804
32
int Interp::convert_setup(block_pointer block,   //!< pointer to a block of RS274/NGC instructions
3805
                         setup_pointer settings)        //!< pointer to machine settings
3806
{
3807
  double x;
3808
  double y;
3809
  double z;
3810
  double a;
3811
  double b;
3812
  double c;
3813
  double u, v, w;
3814
  double r;
3815
  double *parameters;
3816
  int p_int;
3817
3818
  double cx, cy, cz, ca, cb, cc, cu, cv, cw;
3819
3820
32
  CHKS((block->i_flag || block->j_flag), _("I J words not allowed with G10 L2"));
3821
3822
32
  parameters = settings->parameters;
3823
32
  p_int = (int) (block->p_number + 0.0001);
3824
3825
  // if P = 0 then use whatever coordinate system that is currently active
3826
32
  if (p_int == 0) {
3827
    p_int = settings->origin_index;
3828
  }
3829
3830
32
  CHKS((block->l_number == 20 && block->a_flag && settings->a_axis_wrapped &&
3831
        (block->a_number <= -360.0 || block->a_number >= 360.0)),
3832
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")), block->a_number, 'A');
3833
32
  CHKS((block->l_number == 20 && block->b_flag && settings->b_axis_wrapped &&
3834
        (block->b_number <= -360.0 || block->b_number >= 360.0)),
3835
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")), block->b_number, 'B');
3836
32
  CHKS((block->l_number == 20 && block->c_flag && settings->c_axis_wrapped &&
3837
        (block->c_number <= -360.0 || block->c_number >= 360.0)),
3838
       (_("Invalid absolute position %5.2f for wrapped rotary axis %c")), block->c_number, 'C');
3839
3840
32
  CHKS((settings->cutter_comp_side && p_int == settings->origin_index),
3841
       (_("Cannot change the active coordinate system with cutter radius compensation on")));
3842
3843
  find_current_in_system(settings, p_int,
3844
                         &cx, &cy, &cz,
3845
                         &ca, &cb, &cc,
3846
32
                         &cu, &cv, &cw);
3847
3848
32
  if (block->r_flag) {
3849
26
    CHKS((block->l_number == 20), _("R not allowed in G10 L20"));
3850
26
    r = block->r_number;
3851
26
    parameters[5210 + (p_int * 20)] = r;
3852
  } else
3853
6
    r = parameters[5210 + (p_int * 20)];
3854
3855
32
  if (block->l_number == 20) {
3856
      // old position in rotated system
3857
3
      double oldx = cx, oldy = cy;
3858
3859
      // find new desired position in rotated system
3860
3
      x = cx;
3861
3
      y = cy;
3862
3863
3
      if (block->x_flag) {
3864
2
          x = block->x_number;
3865
      }
3866
3
      if (block->y_flag) {
3867
2
          y = block->y_number;
3868
      }
3869
3870
      // move old current position into the unrotated system
3871
3
      rotate(&oldx, &oldy, r);
3872
      // move desired position into the unrotated system
3873
3
      rotate(&x, &y, r);
3874
3875
      // find new offset
3876
3
      x = oldx + USER_TO_PROGRAM_LEN(parameters[5201 + (p_int * 20)]) - x;
3877
3
      y = oldy + USER_TO_PROGRAM_LEN(parameters[5202 + (p_int * 20)]) - y;
3878
3879
      // parameters are not rotated
3880
3
      parameters[5201 + (p_int * 20)] = PROGRAM_TO_USER_LEN(x);
3881
3
      parameters[5202 + (p_int * 20)] = PROGRAM_TO_USER_LEN(y);
3882
3883
3
      if (p_int == settings->origin_index) {
3884
          // let the code below fix up the current coordinates correctly
3885
3
          rotate(&settings->current_x, &settings->current_y, settings->rotation_xy);
3886
3
          settings->rotation_xy = 0;
3887
      }
3888
  } else {
3889
29
      if (block->x_flag) {
3890
24
          x = block->x_number;
3891
24
          parameters[5201 + (p_int * 20)] = PROGRAM_TO_USER_LEN(x);
3892
      } else {
3893
5
          x = USER_TO_PROGRAM_LEN(parameters[5201 + (p_int * 20)]);
3894
      }
3895
29
      if (block->y_flag) {
3896
23
          y = block->y_number;
3897
23
          parameters[5202 + (p_int * 20)] = PROGRAM_TO_USER_LEN(y);
3898
      } else {
3899
6
          y = USER_TO_PROGRAM_LEN(parameters[5202 + (p_int * 20)]);
3900
      }
3901
  }
3902
3903
32
  if (block->z_flag) {
3904
21
    z = block->z_number;
3905
21
    if (block->l_number == 20) z = cz + USER_TO_PROGRAM_LEN(parameters[5203 + (p_int * 20)]) - z;
3906
21
    parameters[5203 + (p_int * 20)] = PROGRAM_TO_USER_LEN(z);
3907
  } else
3908
11
    z = USER_TO_PROGRAM_LEN(parameters[5203 + (p_int * 20)]);
3909
3910
32
  if (block->a_flag) {
3911
    a = block->a_number;
3912
    if (block->l_number == 20) a = ca + USER_TO_PROGRAM_ANG(parameters[5204 + (p_int * 20)]) - a;
3913
    parameters[5204 + (p_int * 20)] = PROGRAM_TO_USER_ANG(a);
3914
  } else
3915
32
    a = USER_TO_PROGRAM_ANG(parameters[5204 + (p_int * 20)]);
3916
3917
32
  if (block->b_flag) {
3918
    b = block->b_number;
3919
    if (block->l_number == 20) b = cb + USER_TO_PROGRAM_ANG(parameters[5205 + (p_int * 20)]) - b;
3920
    parameters[5205 + (p_int * 20)] = PROGRAM_TO_USER_ANG(b);
3921
  } else
3922
32
    b = USER_TO_PROGRAM_ANG(parameters[5205 + (p_int * 20)]);
3923
3924
32
  if (block->c_flag) {
3925
    c = block->c_number;
3926
    if (block->l_number == 20) c = cc + USER_TO_PROGRAM_ANG(parameters[5206 + (p_int * 20)]) - c;
3927
    parameters[5206 + (p_int * 20)] = PROGRAM_TO_USER_ANG(c);
3928
  } else
3929
32
    c = USER_TO_PROGRAM_ANG(parameters[5206 + (p_int * 20)]);
3930
3931
32
  if (block->u_flag) {
3932
    u = block->u_number;
3933
    if (block->l_number == 20) u = cu + USER_TO_PROGRAM_LEN(parameters[5207 + (p_int * 20)]) - u;
3934
    parameters[5207 + (p_int * 20)] = PROGRAM_TO_USER_LEN(u);
3935
  } else
3936
32
    u = USER_TO_PROGRAM_LEN(parameters[5207 + (p_int * 20)]);
3937
3938
32
  if (block->v_flag) {
3939
    v = block->v_number;
3940
    if (block->l_number == 20) v = cv + USER_TO_PROGRAM_LEN(parameters[5208 + (p_int * 20)]) - v;
3941
    parameters[5208 + (p_int * 20)] = PROGRAM_TO_USER_LEN(v);
3942
  } else
3943
32
    v = USER_TO_PROGRAM_LEN(parameters[5208 + (p_int * 20)]);
3944
3945
32
  if (block->w_flag) {
3946
    w = block->w_number;
3947
    if (block->l_number == 20) w = cw + USER_TO_PROGRAM_LEN(parameters[5209 + (p_int * 20)]) - w;
3948
    parameters[5209 + (p_int * 20)] = PROGRAM_TO_USER_LEN(w);
3949
  } else
3950
32
    w = USER_TO_PROGRAM_LEN(parameters[5209 + (p_int * 20)]);
3951
3952
32
  if (p_int == settings->origin_index) {        /* system is currently used */
3953
3954
16
    rotate(&settings->current_x, &settings->current_y, settings->rotation_xy);
3955
3956
16
    settings->current_x += settings->origin_offset_x;
3957
16
    settings->current_y += settings->origin_offset_y;
3958
16
    settings->current_z += settings->origin_offset_z;
3959
16
    settings->AA_current += settings->AA_origin_offset;
3960
16
    settings->BB_current += settings->BB_origin_offset;
3961
16
    settings->CC_current += settings->CC_origin_offset;
3962
16
    settings->u_current += settings->u_origin_offset;
3963
16
    settings->v_current += settings->v_origin_offset;
3964
16
    settings->w_current += settings->w_origin_offset;
3965
3966
16
    settings->origin_offset_x = x;
3967
16
    settings->origin_offset_y = y;
3968
16
    settings->origin_offset_z = z;
3969
16
    settings->AA_origin_offset = a;
3970
16
    settings->BB_origin_offset = b;
3971
16
    settings->CC_origin_offset = c;
3972
16
    settings->u_origin_offset = u;
3973
16
    settings->v_origin_offset = v;
3974
16
    settings->w_origin_offset = w;
3975
3976
16
    settings->current_x -= x;
3977
16
    settings->current_y -= y;
3978
16
    settings->current_z -= z;
3979
16
    settings->AA_current -= a;
3980
16
    settings->BB_current -= b;
3981
16
    settings->CC_current -= c;
3982
16
    settings->u_current -= u;
3983
16
    settings->v_current -= v;
3984
16
    settings->w_current -= w;
3985
3986
16
    SET_G5X_OFFSET(p_int, x, y, z, a, b, c, u, v, w);
3987
3988
16
    rotate(&settings->current_x, &settings->current_y, - r);
3989
16
    settings->rotation_xy = r;
3990
16
    SET_XY_ROTATION(settings->rotation_xy);
3991
3992
  }
3993
#ifdef DEBUG_EMC
3994
  else
3995
16
    enqueue_COMMENT("interpreter: setting coordinate system origin");
3996
#endif
3997
  return INTERP_OK;
3998
}
3999
4000
/****************************************************************************/
4001
4002
/*! convert_set_plane
4003
4004
Returned Value: int
4005
   If any of the following errors occur, this returns the error code shown.
4006
   Otherwise, it returns INTERP_OK.
4007
   1. The user tries to change to a different plane while comp is on:
4008
      NCE_CANNOT_CHANGE_PLANES_WITH_CUTTER_RADIUS_COMP_ON);
4009
   2. The g_code is not G_17, G_18, or G_19:
4010
      NCE_BUG_CODE_NOT_G17_G18_OR_G19
4011
4012
Side effects:
4013
   A canonical command setting the current plane is executed.
4014
4015
Called by: convert_g.
4016
4017
*/
4018
4019
11
int Interp::convert_set_plane(int g_code,        //!< must be G_17, G_18, or G_19
4020
                             setup_pointer settings)    //!< pointer to machine settings
4021
{
4022
11
  CHKS((settings->cutter_comp_side && g_code == G_17 && settings->plane != CANON_PLANE_XY),
4023
        NCE_CANNOT_CHANGE_PLANES_WITH_CUTTER_RADIUS_COMP_ON);
4024
11
  CHKS((settings->cutter_comp_side && g_code == G_18 && settings->plane != CANON_PLANE_XZ),
4025
        NCE_CANNOT_CHANGE_PLANES_WITH_CUTTER_RADIUS_COMP_ON);
4026
11
  CHKS((settings->cutter_comp_side && g_code == G_19 && settings->plane != CANON_PLANE_YZ),
4027
        NCE_CANNOT_CHANGE_PLANES_WITH_CUTTER_RADIUS_COMP_ON);
4028
4029
11
  CHKS((settings->cutter_comp_side && g_code == G_19),
4030
          NCE_RADIUS_COMP_ONLY_IN_XY_OR_XZ);
4031
4032
11
  if (g_code == G_17) {
4033
3
    SELECT_PLANE(CANON_PLANE_XY);
4034
3
    settings->plane = CANON_PLANE_XY;
4035
8
  } else if (g_code == G_18) {
4036
6
    SELECT_PLANE(CANON_PLANE_XZ);
4037
6
    settings->plane = CANON_PLANE_XZ;
4038
2
  } else if (g_code == G_19) {
4039
2
    SELECT_PLANE(CANON_PLANE_YZ);
4040
2
    settings->plane = CANON_PLANE_YZ;
4041
  } else if (g_code == G_17_1) {
4042
    SELECT_PLANE(CANON_PLANE_UV);
4043
    settings->plane = CANON_PLANE_UV;
4044
  } else if (g_code == G_18_1) {
4045
    SELECT_PLANE(CANON_PLANE_UW);
4046
    settings->plane = CANON_PLANE_UW;
4047
  } else if (g_code == G_19_1) {
4048
    SELECT_PLANE(CANON_PLANE_VW);
4049
    settings->plane = CANON_PLANE_VW;
4050
  } else
4051
    ERS(NCE_BUG_CODE_NOT_G17_G18_OR_G19);
4052
  return INTERP_OK;
4053
}
4054
4055
/****************************************************************************/
4056
4057
/*! convert_speed
4058
4059
Returned Value: int (INTERP_OK)
4060
4061
Side effects:
4062
  The machine spindle speed is set to the value of s_number in the
4063
  block by a call to SET_SPINDLE_SPEED.
4064
  The machine model for spindle speed is set to that value.
4065
4066
Called by: execute_block.
4067
4068
*/
4069
4070
7
int Interp::convert_speed(block_pointer block,   //!< pointer to a block of RS274 instructions
4071
                         setup_pointer settings)        //!< pointer to machine settings
4072
{
4073
7
  enqueue_SET_SPINDLE_SPEED(block->s_number);
4074
7
  settings->speed = block->s_number;
4075
7
  return INTERP_OK;
4076
}
4077
4078
int Interp::convert_spindle_mode(block_pointer block, setup_pointer settings)
4079
{
4080
    if(block->g_modes[14] == G_97) {
4081
        settings->spindle_mode = CONSTANT_RPM;
4082
	enqueue_SET_SPINDLE_MODE(0);
4083
    } else { /* G_96 */
4084
        settings->spindle_mode = CONSTANT_SURFACE;
4085
	if(block->d_flag)
4086
	    enqueue_SET_SPINDLE_MODE(fabs(block->d_number_float));
4087
	else
4088
	    enqueue_SET_SPINDLE_MODE(1e30);
4089
    }
4090
    return INTERP_OK;
4091
}
4092
/****************************************************************************/
4093
4094
/*! convert_stop
4095
4096
Returned Value: int
4097
   When an m2 or m30 (program_end) is encountered, this returns INTERP_EXIT.
4098
   If the code is not m0, m1, m2, m30, or m60, this returns
4099
   NCE_BUG_CODE_NOT_M0_M1_M2_M30_M60
4100
   Otherwise, it returns INTERP_OK.
4101
4102
Side effects:
4103
   An m0, m1, m2, m30, or m60 in the block is executed.
4104
4105
   For m0, m1, and m60, this makes a function call to the PROGRAM_STOP
4106
   canonical machining function (which stops program execution).
4107
   In addition, m60 calls PALLET_SHUTTLE.
4108
4109
   For m2 and m30, this resets the machine and then calls PROGRAM_END.
4110
   In addition, m30 calls PALLET_SHUTTLE.
4111
   Clear g92 offset if DISABLE_G92_PERSISTENCE is set in the .ini file.
4112
4113
Called by: execute_block.
4114
4115
This handles stopping or ending the program (m0, m1, m2, m30, m60)
4116
4117
[NCMS] specifies how the following modes should be reset at m2 or
4118
m30. The descriptions are not collected in one place, so this list
4119
may be incomplete.
4120
4121
G52 offsetting coordinate zero points [NCMS, page 10]
4122
G92 coordinate offset using tool position [NCMS, page 10]
4123
4124
The following should have reset values, but no description of reset
4125
behavior could be found in [NCMS].
4126
G17, G18, G19 selected plane [NCMS, pages 14, 20]
4127
G90, G91 distance mode [NCMS, page 15]
4128
G93, G94 feed mode [NCMS, pages 35 - 37]
4129
M48, M49 overrides enabled, disabled [NCMS, pages 37 - 38]
4130
M3, M4, M5 spindle turning [NCMS, page 7]
4131
4132
The following should be set to some value at machine start-up but
4133
not automatically reset by any of the stopping codes.
4134
1. G20, G21 length units [NCMS, page 15]. This is up to the installer.
4135
2. motion_control_mode. This is set in Interp::init but not reset here.
4136
   Might add it here.
4137
4138
The following resets have been added by calling the appropriate
4139
canonical machining command and/or by resetting interpreter
4140
settings. They occur on M2 or M30.
4141
4142
1. origin offsets are set to the default (like G54)
4143
2. Selected plane is set to CANON_PLANE_XY (like G17) - SELECT_PLANE
4144
3. Distance mode is set to MODE_ABSOLUTE (like G90)   - no canonical call
4145
4. Feed mode is set to UNITS_PER_MINUTE (like G94)    - no canonical call
4146
5. Feed and speed overrides are set to true (like M48)  - ENABLE_FEED_OVERRIDE
4147
                                                      - ENABLE_SPEED_OVERRIDE
4148
6. Cutter compensation is turned off (like G40)       - no canonical call
4149
7. The spindle is stopped (like M5)                   - STOP_SPINDLE_TURNING
4150
8. The motion mode is set to G_1 (like G1)            - no canonical call
4151
9. Coolant is turned off (like M9)                    - FLOOD_OFF & MIST_OFF
4152
10. G52/G92 is cleared if DISABLE_G92_PERSISTENCE is set in the .ini file
4153
4154
*/
4155
4156
43
int Interp::convert_stop(block_pointer block,    //!< pointer to a block of RS274/NGC instructions
4157
                        setup_pointer settings) //!< pointer to machine settings
4158
{
4159
  int index;
4160
  char *line;
4161
  int length;
4162
4163
  double cx, cy, cz;
4164
43
  comp_get_current(settings, &cx, &cy, &cz);
4165
43
  CHP(move_endpoint_and_flush(settings, cx, cy));
4166
43
  dequeue_canons(settings);
4167
4168
43
  if (block->m_modes[4] == 0) {
4169
2
    PROGRAM_STOP();
4170
41
  } else if (block->m_modes[4] == 60) {
4171
    PALLET_SHUTTLE();
4172
    PROGRAM_STOP();
4173
41
  } else if (block->m_modes[4] == 1) {
4174
    OPTIONAL_PROGRAM_STOP();
4175
41
  } else if ((block->m_modes[4] == 2) || (block->m_modes[4] == 30)) {   /* reset stuff here */
4176
41
    program_end_cleanup(settings);
4177
41
    if (block->m_modes[4] == 30)
4178
5
      PALLET_SHUTTLE();
4179
41
    PROGRAM_END();
4180
41
    if (_setup.percent_flag && _setup.file_pointer) {
4181
2
      line = _setup.linetext;
4182
      for (;;) {                /* check for ending percent sign and comment if missing */
4183
6
        if (fgets(line, LINELEN, _setup.file_pointer) == NULL) {
4184
          enqueue_COMMENT("interpreter: percent sign missing from end of file");
4185
          break;
4186
        }
4187
6
        length = strlen(line);
4188
6
        if (length == (LINELEN - 1)) {       // line is too long. need to finish reading the line
4189
          for (; fgetc(_setup.file_pointer) != '\n';);
4190
          continue;
4191
        }
4192
6
        for (index = (length - 1);      // index set on last char
4193
11
             (index >= 0) && (isspace(line[index])); index--);
4194
6
        if (line[index] == '%') // found line with % at end
4195
        {
4196
2
          for (index--; (index >= 0) && (isspace(line[index])); index--);
4197
2
          if (index == -1)      // found line with only percent sign
4198
            break;
4199
        }
4200
      }
4201
    }
4202
41
    unwind_call(INTERP_EXIT, __FILE__,__LINE__,__FUNCTION__);
4203
    return INTERP_EXIT;
4204
  } else
4205
    ERS(NCE_BUG_CODE_NOT_M0_M1_M2_M30_M60);
4206
  return INTERP_OK;
4207
}
4208
4209
/*************************************************************************** */
4210
4211
/*! convert_straight
4212
4213
Returned Value: int
4214
   If convert_straight_comp1 or convert_straight_comp2 is called
4215
   and returns an error code, this returns that code.
4216
   If any of the following errors occur, this returns the error shown.
4217
   Otherwise, it returns INTERP_OK.
4218
   1. The value of move is not G_0 or G_1:
4219
      NCE_BUG_CODE_NOT_G0_OR_G1
4220
   2. A straight feed (g1) move is called with feed rate set to 0:
4221
      NCE_CANNOT_DO_G1_WITH_ZERO_FEED_RATE
4222
   3. A straight feed (g1) move is called with inverse time feed in effect
4223
      but no f word (feed time) is provided:
4224
      NCE_F_WORD_MISSING_WITH_INVERSE_TIME_G1_MOVE
4225
   4. A move is called with G53 and cutter radius compensation on:
4226
      NCE_CANNOT_USE_G53_WITH_CUTTER_RADIUS_COMP
4227
   5. A G33 move is called without the necessary support compiled in:
4228
      NCE_G33_NOT_SUPPORTED
4229
4230
Side effects:
4231
   This executes a STRAIGHT_FEED command at cutting feed rate
4232
   (if move is G_1) or a STRAIGHT_TRAVERSE command (if move is G_0).
4233
   It also updates the setting of the position of the tool point to the
4234
   end point of the move. If cutter radius compensation is on, it may
4235
   also generate an arc before the straight move. Also, in INVERSE_TIME
4236
   feed mode, SET_FEED_RATE will be called the feed rate setting changed.
4237
4238
Called by: convert_motion.
4239
4240
The approach to operating in incremental distance mode (g91) is to
4241
put the the absolute position values into the block before using the
4242
block to generate a move.
4243
4244
In inverse time feed mode, a lower bound of 0.1 is placed on the feed
4245
rate so that the feed rate is never set to zero. If the destination
4246
point is the same as the current point, the feed rate would be
4247
calculated as zero otherwise.
4248
4249
If cutter compensation is in use, the path's length may increase or
4250
decrease.  Also an arc may be added, to go around a corner, before the
4251
straight move.  For the purpose of calculating the feed rate when in
4252
inverse time mode, this length increase or decrease is ignored.  The
4253
feed is still set to the original programmed straight length divided by
4254
the F number (with the above lower bound).  The new arc (if needed) and
4255
the new longer or shorter straight move are taken at this feed.
4256
4257
*/
4258
4259
880
int Interp::convert_straight(int move,   //!< either G_0 or G_1
4260
                            block_pointer block,        //!< pointer to a block of RS274 instructions
4261
                            setup_pointer settings)     //!< pointer to machine settings
4262
{
4263
  double end_x;
4264
  double end_y;
4265
  double end_z;
4266
  double AA_end;
4267
  double BB_end;
4268
  double CC_end;
4269
  double u_end, v_end, w_end;
4270
  int status;
4271
4272
880
  settings->arc_not_allowed = false;
4273
4274
880
  if (move == G_1) {
4275
720
    if (settings->feed_mode == UNITS_PER_MINUTE) {
4276
708
      CHKS((settings->feed_rate == 0.0), NCE_CANNOT_DO_G1_WITH_ZERO_FEED_RATE);
4277
12
    } else if (settings->feed_mode == UNITS_PER_REVOLUTION) {
4278
      CHKS((settings->feed_rate == 0.0), NCE_CANNOT_DO_G1_WITH_ZERO_FEED_RATE);
4279
      CHKS((settings->speed == 0.0), _("Cannot feed with zero spindle speed in feed per rev mode"));
4280
12
    } else if (settings->feed_mode == INVERSE_TIME) {
4281
12
      CHKS((!block->f_flag),
4282
          NCE_F_WORD_MISSING_WITH_INVERSE_TIME_G1_MOVE);
4283
    }
4284
  }
4285
4286
879
  settings->motion_mode = move;
4287
879
  CHP(find_ends(block, settings, &end_x, &end_y, &end_z,
4288
                &AA_end, &BB_end, &CC_end, &u_end, &v_end, &w_end));
4289
4290
879
  if (move == G_1) {
4291
      inverse_time_rate_straight(end_x, end_y, end_z,
4292
                                 AA_end, BB_end, CC_end,
4293
                                 u_end, v_end, w_end,
4294
719
                                 block, settings);
4295
  }
4296
4297
1229
  if ((settings->cutter_comp_side) &&    /* ! "== true" */
4298
350
      (settings->cutter_comp_radius > 0.0)) {   /* radius always is >= 0 */
4299
4300
310
    CHKS((block->g_modes[0] == G_53),
4301
        NCE_CANNOT_USE_G53_WITH_CUTTER_RADIUS_COMP);
4302
4303
310
    if(settings->plane == CANON_PLANE_XZ) {
4304
32
        if (settings->cutter_comp_firstmove)
4305
            status = convert_straight_comp1(move, block, settings, end_z, end_x, end_y,
4306
2
                                            AA_end, BB_end, CC_end, u_end, v_end, w_end);
4307
        else
4308
            status = convert_straight_comp2(move, block, settings, end_z, end_x, end_y,
4309
30
                                            AA_end, BB_end, CC_end, u_end, v_end, w_end);
4310
278
    } else if(settings->plane == CANON_PLANE_XY) {
4311
278
        if (settings->cutter_comp_firstmove)
4312
            status = convert_straight_comp1(move, block, settings, end_x, end_y, end_z,
4313
57
                                            AA_end, BB_end, CC_end, u_end, v_end, w_end);
4314
        else
4315
            status = convert_straight_comp2(move, block, settings, end_x, end_y, end_z,
4316
221
                                            AA_end, BB_end, CC_end, u_end, v_end, w_end);
4317
    } else ERS("BUG: Invalid plane for cutter compensation");
4318
310
    CHP(status);
4319
569
  } else if (move == G_0) {
4320
    STRAIGHT_TRAVERSE(block->line_number, end_x, end_y, end_z,
4321
                      AA_end, BB_end, CC_end,
4322
156
                      u_end, v_end, w_end);
4323
156
    settings->current_x = end_x;
4324
156
    settings->current_y = end_y;
4325
156
    settings->current_z = end_z;
4326
413
  } else if (move == G_1) {
4327
    STRAIGHT_FEED(block->line_number, end_x, end_y, end_z,
4328
                  AA_end, BB_end, CC_end,
4329
412
                  u_end, v_end, w_end);
4330
412
    settings->current_x = end_x;
4331
412
    settings->current_y = end_y;
4332
412
    settings->current_z = end_z;
4333
1
  } else if (move == G_33) {
4334
    CHKS(((settings->spindle_turning != CANON_CLOCKWISE) &&
4335
           (settings->spindle_turning != CANON_COUNTERCLOCKWISE)),
4336
          _("Spindle not turning in G33"));
4337
    START_SPEED_FEED_SYNCH(block->k_number, 0);
4338
    STRAIGHT_FEED(block->line_number, end_x, end_y, end_z, AA_end, BB_end, CC_end, u_end, v_end, w_end);
4339
    STOP_SPEED_FEED_SYNCH();
4340
    settings->current_x = end_x;
4341
    settings->current_y = end_y;
4342
    settings->current_z = end_z;
4343
1
  } else if (move == G_33_1) {
4344
    CHKS(((settings->spindle_turning != CANON_CLOCKWISE) &&
4345
           (settings->spindle_turning != CANON_COUNTERCLOCKWISE)),
4346
          _("Spindle not turning in G33.1"));
4347
    START_SPEED_FEED_SYNCH(block->k_number, 0);
4348
    RIGID_TAP(block->line_number, end_x, end_y, end_z);
4349
    STOP_SPEED_FEED_SYNCH();
4350
    // after the RIGID_TAP cycle we'll be in the same spot
4351
1
  } else if (move == G_76) {
4352
1
    CHKS(((settings->spindle_turning != CANON_CLOCKWISE) &&
4353
           (settings->spindle_turning != CANON_COUNTERCLOCKWISE)),
4354
          _("Spindle not turning in G76"));
4355
1
    CHKS((settings->AA_current != AA_end ||
4356
         settings->BB_current != BB_end ||
4357
         settings->CC_current != CC_end ||
4358
         settings->u_current != u_end ||
4359
         settings->v_current != v_end ||
4360
         settings->w_current != w_end), NCE_CANNOT_MOVE_ROTARY_AXES_WITH_G76);
4361
1
    int result = convert_threading_cycle(block, settings, end_x, end_y, end_z);
4362
1
    if(result != INTERP_OK) return result;
4363
  } else
4364
    ERS(NCE_BUG_CODE_NOT_G0_OR_G1);
4365
4366
878
  settings->AA_current = AA_end;
4367
878
  settings->BB_current = BB_end;
4368
878
  settings->CC_current = CC_end;
4369
878
  settings->u_current = u_end;
4370
878
  settings->v_current = v_end;
4371
878
  settings->w_current = w_end;
4372
878
  return INTERP_OK;
4373
}
4374
4375
int Interp::convert_straight_indexer(int axis, int jnum, block_pointer block, setup_pointer settings) {
4376
    double end_x, end_y, end_z;
4377
    double AA_end, BB_end, CC_end;
4378
    double u_end, v_end, w_end;
4379
4380
    find_ends(block, settings, &end_x, &end_y, &end_z,
4381
              &AA_end, &BB_end, &CC_end, &u_end, &v_end, &w_end);
4382
4383
    CHKS((end_x != settings->current_x ||
4384
          end_y != settings->current_y ||
4385
          end_z != settings->current_z ||
4386
          u_end != settings->u_current ||
4387
          v_end != settings->v_current ||
4388
          w_end != settings->w_current ||
4389
          (axis != 3 && AA_end != settings->AA_current) ||
4390
          (axis != 4 && BB_end != settings->BB_current) ||
4391
          (axis != 5 && CC_end != settings->CC_current)),
4392
         _("BUG: An axis incorrectly moved along with an indexer"));
4393
4394
    switch(axis) {
4395
    case 3:
4396
        issue_straight_index(axis, jnum, AA_end, block->line_number, settings);
4397
        break;
4398
    case 4:
4399
        issue_straight_index(axis, jnum, BB_end, block->line_number, settings);
4400
        break;
4401
    case 5:
4402
        issue_straight_index(axis, jnum, CC_end, block->line_number, settings);
4403
        break;
4404
    default:
4405
        ERS((_("BUG: trying to index incorrect axis")));
4406
    }
4407
    return INTERP_OK;
4408
}
4409
4410
int Interp::issue_straight_index(int axis, int jnum, double target, int lineno, setup_pointer settings) {
4411
    CANON_MOTION_MODE save_mode;
4412
    double save_tolerance;
4413
    // temporarily switch to exact stop mode for indexing move
4414
    save_mode = GET_EXTERNAL_MOTION_CONTROL_MODE();
4415
    save_tolerance = GET_EXTERNAL_MOTION_CONTROL_TOLERANCE();
4416
    if (save_mode != CANON_EXACT_PATH)
4417
        SET_MOTION_CONTROL_MODE(CANON_EXACT_PATH, 0);
4418
4419
    double AA_end = axis == 3? target: settings->AA_current;
4420
    double BB_end = axis == 4? target: settings->BB_current;
4421
    double CC_end = axis == 5? target: settings->CC_current;
4422
4423
    // tell canon that this is a special indexing move
4424
    UNLOCK_ROTARY(lineno, jnum);
4425
    STRAIGHT_TRAVERSE(lineno, settings->current_x, settings->current_y, settings->current_z,
4426
                      AA_end, BB_end, CC_end,
4427
                      settings->u_current, settings->v_current, settings->w_current);
4428
    LOCK_ROTARY(lineno, jnum);
4429
4430
    // restore path mode
4431
    if(save_mode != CANON_EXACT_PATH)
4432
        SET_MOTION_CONTROL_MODE(save_mode, save_tolerance);
4433
4434
    settings->AA_current = AA_end;
4435
    settings->BB_current = BB_end;
4436
    settings->CC_current = CC_end;
4437
    return INTERP_OK;
4438
}
4439
4440
4441
#define AABBCC settings->AA_current, settings->BB_current, settings->CC_current, settings->u_current, settings->v_current, settings->w_current
4442
4443
// make one threading pass.  only called from convert_threading_cycle.
4444
static void
4445
35
threading_pass(setup_pointer settings, block_pointer block,
4446
	       int boring, double safe_x, double depth, double end_depth,
4447
	       double start_y, double start_z, double zoff, double taper_dist,
4448
	       int entry_taper, int exit_taper, double taper_pitch,
4449
	       double pitch, double full_threadheight, double target_z) {
4450
    STRAIGHT_TRAVERSE(block->line_number, boring?
4451
		      safe_x + depth - end_depth:
4452
35
		      safe_x - depth + end_depth,
4453
70
		      start_y, start_z - zoff, AABBCC); //back
4454
35
    if(taper_dist && entry_taper) {
4455
	DISABLE_FEED_OVERRIDE();
4456
	START_SPEED_FEED_SYNCH(taper_pitch, 0);
4457
	STRAIGHT_FEED(block->line_number, boring?
4458
		      safe_x + depth - full_threadheight:
4459
		      safe_x - depth + full_threadheight,
4460
		      start_y, start_z - zoff, AABBCC); //in
4461
	STRAIGHT_FEED(block->line_number, boring? safe_x + depth: safe_x - depth, //angled in
4462
		      start_y, start_z - zoff - taper_dist, AABBCC);
4463
	START_SPEED_FEED_SYNCH(pitch, 0);
4464
    } else {
4465
	STRAIGHT_TRAVERSE(block->line_number, boring? safe_x + depth: safe_x - depth,
4466
35
			  start_y, start_z - zoff, AABBCC); //in
4467
35
	DISABLE_FEED_OVERRIDE();
4468
35
	START_SPEED_FEED_SYNCH(pitch, 0);
4469
    }
4470
4471
35
    if(taper_dist && exit_taper) {
4472
	STRAIGHT_FEED(block->line_number, boring? safe_x + depth: safe_x - depth,  //over
4473
35
		      start_y, target_z - zoff + taper_dist, AABBCC);
4474
35
	START_SPEED_FEED_SYNCH(taper_pitch, 0);
4475
	STRAIGHT_FEED(block->line_number, boring?
4476
		      safe_x + depth - full_threadheight:
4477
35
		      safe_x - depth + full_threadheight,
4478
70
		      start_y, target_z - zoff, AABBCC); //angled out
4479
    } else {
4480
	STRAIGHT_FEED(block->line_number, boring? safe_x + depth: safe_x - depth,
4481
		      start_y, target_z - zoff, AABBCC); //over
4482
    }
4483
35
    STOP_SPEED_FEED_SYNCH();
4484
    STRAIGHT_TRAVERSE(block->line_number, boring?
4485
		      safe_x + depth - end_depth:
4486
35
		      safe_x - depth + end_depth,
4487
70
		      start_y, target_z - zoff, AABBCC); //out
4488
35
    ENABLE_FEED_OVERRIDE();
4489
35
}
4490
4491
1
int Interp::convert_threading_cycle(block_pointer block,
4492
				    setup_pointer settings,
4493
				    double end_x, double end_y, double end_z) {
4494
4495
4496
1
    CHKS((settings->cutter_comp_side),
4497
         (_("Cannot use G76 threading cycle with cutter radius compensation on")));
4498
4499
1
    CHKS((block->i_number == 0),
4500
        (_("In G76, I must not be 0")));
4501
1
    CHKS((block->j_number <= 0),
4502
        (_("In G76, J must be greater than 0")));
4503
1
    CHKS((block->k_number <= block->j_number),
4504
        (_("In G76, K must be greater than J")));
4505
4506
1
    double start_x = settings->current_x;
4507
1
    double start_y = settings->current_y;
4508
1
    double start_z = settings->current_z;
4509
4510
1
    double i_number = block->i_number;
4511
1
    double j_number = block->j_number;
4512
1
    double k_number = block->k_number;
4513
4514
1
    if(_setup.lathe_diameter_mode){
4515
      i_number /= 2;
4516
      j_number /= 2;
4517
      k_number /= 2;
4518
    }
4519
4520
4521
1
    int boring = 0;
4522
4523
1
    if (i_number > 0.0)
4524
	boring = 1;
4525
4526
1
    double safe_x = start_x;
4527
1
    double full_dia_depth = fabs(i_number);
4528
1
    double start_depth = fabs(i_number) + fabs(j_number);
4529
1
    double cut_increment = fabs(j_number);
4530
1
    double full_threadheight = fabs(k_number);
4531
1
    double end_depth = fabs(k_number) + fabs(i_number);
4532
4533
1
    double pitch = block->p_number;
4534
1
    double compound_angle = block->q_number;
4535
1
    if(compound_angle == -1) compound_angle = 0;
4536
1
    compound_angle *= M_PIl/180.0;
4537
1
    if(end_z > start_z) compound_angle = -compound_angle;
4538
4539
1
    int spring_cuts = block->h_flag ? block->h_number: 0;
4540
4541
1
    double degression = block->r_number;
4542
1
    if(degression < 1.0 || !block->r_flag) degression = 1.0;
4543
4544
1
    double taper_dist = block->e_flag? block->e_number: 0.0;
4545
1
    if(taper_dist < 0.0) taper_dist = 0.0;
4546
    double taper_pitch = taper_dist > 0.0?
4547
1
	pitch * hypot(taper_dist, full_threadheight)/taper_dist: pitch;
4548
4549
1
    if(end_z > start_z) taper_dist = -taper_dist;
4550
4551
1
    int taper_flags = block->l_number;
4552
1
    if(taper_flags < 0) taper_flags = 0;
4553
4554
1
    int entry_taper = taper_flags & 1;
4555
1
    int exit_taper = taper_flags & 2;
4556
4557
    double depth, zoff;
4558
1
    int pass = 1;
4559
4560
1
    double target_z = end_z + fabs(k_number) * tan(compound_angle);
4561
4562
1
    depth = start_depth;
4563
1
    zoff = (depth - full_dia_depth) * tan(compound_angle);
4564
33
    while (depth < end_depth) {
4565
	threading_pass(settings, block, boring, safe_x, depth, end_depth, start_y,
4566
		       start_z, zoff, taper_dist, entry_taper, exit_taper,
4567
31
		       taper_pitch, pitch, full_threadheight, target_z);
4568
31
        depth = full_dia_depth + cut_increment * pow(++pass, 1.0/degression);
4569
31
        zoff = (depth - full_dia_depth) * tan(compound_angle);
4570
    }
4571
    // full specified depth now
4572
1
    depth = end_depth;
4573
1
    zoff = (depth - full_dia_depth) * tan(compound_angle);
4574
    // cut at least once -- more if spring cuts.
4575
5
    for(int i = 0; i<spring_cuts+1; i++) {
4576
	threading_pass(settings, block, boring, safe_x, depth, end_depth, start_y,
4577
		       start_z, zoff, taper_dist, entry_taper, exit_taper,
4578
4
		       taper_pitch, pitch, full_threadheight, target_z);
4579
    }
4580
1
    STRAIGHT_TRAVERSE(block->line_number, end_x, end_y, end_z, AABBCC);
4581
1
    settings->current_x = end_x;
4582
1
    settings->current_y = end_y;
4583
1
    settings->current_z = end_z;
4584
#undef AABBC
4585
1
    return INTERP_OK;
4586
}
4587
4588
4589
/****************************************************************************/
4590
4591
/*! convert_straight_comp1
4592
4593
Returned Value: int
4594
   If any of the following errors occur, this returns the error shown.
4595
   Otherwise, it returns INTERP_OK.
4596
   1. The side is not RIGHT or LEFT:
4597
      NCE_BUG_SIDE_NOT_RIGHT_OR_LEFT
4598
   2. The destination tangent point is not more than a tool radius
4599
      away (indicating gouging): NCE_CUTTER_GOUGING_WITH_CUTTER_RADIUS_COMP
4600
   3. The value of move is not G_0 or G_1
4601
      NCE_BUG_CODE_NOT_G0_OR_G1
4602
4603
Side effects:
4604
   This executes a STRAIGHT_MOVE command at cutting feed rate
4605
   or a STRAIGHT_TRAVERSE command.
4606
   It also updates the setting of the position of the tool point
4607
   to the end point of the move and updates the programmed point.
4608
4609
Called by: convert_straight.
4610
4611
This is called if cutter radius compensation is on and
4612
settings->cutter_comp_firstmove is true, indicating that this is the
4613
first move after cutter radius compensation is turned on.
4614
4615
The algorithm used here for determining the path is to draw a straight
4616
line from the destination point which is tangent to a circle whose
4617
center is at the current point and whose radius is the radius of the
4618
cutter. The destination point of the cutter tip is then found as the
4619
center of a circle of the same radius tangent to the tangent line at
4620
the destination point.
4621
4622
*/
4623
4624
59
int Interp::convert_straight_comp1(int move,     //!< either G_0 or G_1
4625
                                   block_pointer block,  //!< pointer to a block of RS274 instructions
4626
                                   setup_pointer settings,       //!< pointer to machine settings
4627
                                   double px,    //!< X coordinate of end point
4628
                                   double py,    //!< Y coordinate of end point
4629
                                   double pz,    //!< Z coordinate of end point
4630
                                   double AA_end,        //!< A coordinate of end point
4631
                                   double BB_end,        //!< B coordinate of end point
4632
                                   double CC_end,        //!< C coordinate of end point
4633
                                   double u_end, double v_end, double w_end)
4634
{
4635
    double alpha;
4636
    double distance;
4637
59
    double radius = settings->cutter_comp_radius; /* always will be positive */
4638
    double end_x, end_y;
4639
4640
59
    int side = settings->cutter_comp_side;
4641
    double cx, cy, cz;
4642
4643
59
    comp_get_current(settings, &cx, &cy, &cz);
4644
59
    distance = hypot((px - cx), (py - cy));
4645
4646
59
    CHKS(((side != LEFT) && (side != RIGHT)), NCE_BUG_SIDE_NOT_RIGHT_OR_LEFT);
4647
59
    CHKS((distance <= radius), _("Length of cutter compensation entry move is not greater than the tool radius"));
4648
4649
59
    alpha = atan2(py - cy, px - cx) + (side == LEFT ? M_PIl/2. : -M_PIl/2.);
4650
4651
59
    end_x = (px + (radius * cos(alpha)));
4652
59
    end_y = (py + (radius * sin(alpha)));
4653
4654
    // with these moves we don't need to record the direction vector.
4655
    // they cannot get reversed because they are guaranteed to be long
4656
    // enough.
4657
4658
59
    set_endpoint(cx, cy);
4659
4660
59
    if (move == G_0) {
4661
        enqueue_STRAIGHT_TRAVERSE(settings, block->line_number,
4662
                                  cos(alpha), sin(alpha), 0,
4663
                                  end_x, end_y, pz,
4664
                                  AA_end, BB_end, CC_end, u_end, v_end, w_end);
4665
    }
4666
59
    else if (move == G_1) {
4667
        enqueue_STRAIGHT_FEED(settings, block->line_number,
4668
                              cos(alpha), sin(alpha), 0,
4669
                              end_x, end_y, pz,
4670
59
                              AA_end, BB_end, CC_end, u_end, v_end, w_end);
4671
    } else
4672
        ERS(NCE_BUG_CODE_NOT_G0_OR_G1);
4673
4674
59
    settings->cutter_comp_firstmove = false;
4675
4676
59
    comp_set_current(settings, end_x, end_y, pz);
4677
59
    settings->AA_current = AA_end;
4678
59
    settings->BB_current = BB_end;
4679
59
    settings->CC_current = CC_end;
4680
59
    settings->u_current = u_end;
4681
59
    settings->v_current = v_end;
4682
59
    settings->w_current = w_end;
4683
59
    comp_set_programmed(settings, px, py, pz);
4684
    return INTERP_OK;
4685
}
4686
/****************************************************************************/
4687
4688
/*! convert_straight_comp2
4689
4690
Returned Value: int
4691
   If any of the following errors occur, this returns the error shown.
4692
   Otherwise, it returns INTERP_OK.
4693
   1. The compensation side is not RIGHT or LEFT:
4694
      NCE_BUG_SIDE_NOT_RIGHT_OR_LEFT
4695
   2. A concave corner is found:
4696
      NCE_CONCAVE_CORNER_WITH_CUTTER_RADIUS_COMP
4697
4698
Side effects:
4699
   This executes a STRAIGHT_FEED command at cutting feed rate
4700
   or a STRAIGHT_TRAVERSE command.
4701
   It also generates an ARC_FEED to go around a corner, if necessary.
4702
   It also updates the setting of the position of the tool point to
4703
   the end point of the move and updates the programmed point.
4704
4705
Called by: convert_straight.
4706
4707
This is called if cutter radius compensation is on and
4708
settings->cutter_comp_firstmove is not true, indicating that this is not
4709
the first move after cutter radius compensation is turned on.
4710
4711
The algorithm used here is:
4712
1. Determine the direction of the last motion. This is done by finding
4713
   the direction of the line from the last programmed point to the
4714
   current tool tip location. This line is a radius of the tool and is
4715
   perpendicular to the direction of motion since the cutter is tangent
4716
   to that direction.
4717
2. Determine the direction of the programmed motion.
4718
3. If there is a convex corner, insert an arc to go around the corner.
4719
4. Find the destination point for the tool tip. The tool will be
4720
   tangent to the line from the last programmed point to the present
4721
   programmed point at the present programmed point.
4722
5. Go in a straight line from the current tool tip location to the
4723
   destination tool tip location.
4724
4725
This uses an angle tolerance of TOLERANCE_CONCAVE_CORNER (0.01 radian)
4726
to determine if:
4727
1) an illegal concave corner exists (tool will not fit into corner),
4728
2) no arc is required to go around the corner (i.e. the current line
4729
   is in the same direction as the end of the previous move), or
4730
3) an arc is required to go around a convex corner and start off in
4731
   a new direction.
4732
4733
If a rotary axis is moved in this block and an extra arc is required
4734
to go around a sharp corner, all the rotary axis motion occurs on the
4735
arc.  An alternative might be to distribute the rotary axis motion
4736
over the arc and the straight move in proportion to their lengths.
4737
4738
If the Z-axis is moved in this block and an extra arc is required to
4739
go around a sharp corner, all the Z-axis motion occurs on the straight
4740
line and none on the extra arc.  An alternative might be to distribute
4741
the Z-axis motion over the extra arc and the straight line in
4742
proportion to their lengths.
4743
4744
This handles the case of there being no XY motion.
4745
4746
This handles G0 moves. Where an arc is inserted to round a corner in a
4747
G1 move, no arc is inserted for a G0 move; a STRAIGHT_TRAVERSE is made
4748
from the current point to the end point. The end point for a G0
4749
move is the same as the end point for a G1 move, however.
4750
4751
*/
4752
4753
251
int Interp::convert_straight_comp2(int move,     //!< either G_0 or G_1
4754
                                   block_pointer block,  //!< pointer to a block of RS274 instructions
4755
                                   setup_pointer settings,       //!< pointer to machine settings
4756
                                   double px,    //!< X coordinate of programmed end point
4757
                                   double py,    //!< Y coordinate of programmed end point
4758
                                   double pz,    //!< Z coordinate of end point
4759
                                   double AA_end,        //!< A coordinate of end point
4760
                                   double BB_end,        //!< B coordinate of end point
4761
                                   double CC_end,        //!< C coordinate of end point
4762
                                   double u_end, double v_end, double w_end)
4763
{
4764
    double alpha;
4765
    double beta;
4766
    double end_x, end_y, end_z;                 /* x-coordinate of actual end point */
4767
    double gamma;
4768
    double mid_x, mid_y;                 /* x-coordinate of end of added arc, if needed */
4769
    double radius;
4770
    int side;
4771
251
    double small = TOLERANCE_CONCAVE_CORNER;      /* radians, testing corners */
4772
    double opx, opy, opz;      /* old programmed beginning point */
4773
    double theta;
4774
    double cx, cy, cz;
4775
    int concave;
4776
4777
251
    comp_get_current(settings, &cx, &cy, &cz);
4778
251
    comp_get_current(settings, &end_x, &end_y, &end_z);
4779
251
    comp_get_programmed(settings, &opx, &opy, &opz);
4780
4781
251
    if ((py == opy) && (px == opx)) {     /* no XY motion */
4782
17
        if (move == G_0) {
4783
            enqueue_STRAIGHT_TRAVERSE(settings, block->line_number,
4784
                                      px - opx, py - opy, pz - opz,
4785
                                      cx, cy, pz,
4786
3
                                      AA_end, BB_end, CC_end, u_end, v_end, w_end);
4787
14
        } else if (move == G_1) {
4788
            enqueue_STRAIGHT_FEED(settings, block->line_number,
4789
                                  px - opx, py - opy, pz - opz,
4790
14
                                  cx, cy, pz, AA_end, BB_end, CC_end, u_end, v_end, w_end);
4791
        } else
4792
            ERS(NCE_BUG_CODE_NOT_G0_OR_G1);
4793
        // end already filled out, above
4794
    } else {
4795
        // some XY motion
4796
234
        side = settings->cutter_comp_side;
4797
234
        radius = settings->cutter_comp_radius;      /* will always be positive */
4798
234
        theta = atan2(cy - opy, cx - opx);
4799
234
        alpha = atan2(py - opy, px - opx);
4800
4801
234
        if (side == LEFT) {
4802
99
            if (theta < alpha)
4803
47
                theta = (theta + (2 * M_PIl));
4804
99
            beta = ((theta - alpha) - M_PI_2l);
4805
99
            gamma = M_PI_2l;
4806
135
        } else if (side == RIGHT) {
4807
135
            if (alpha < theta)
4808
51
                alpha = (alpha + (2 * M_PIl));
4809
135
            beta = ((alpha - theta) - M_PI_2l);
4810
135
            gamma = -M_PI_2l;
4811
        } else
4812
            ERS(NCE_BUG_SIDE_NOT_RIGHT_OR_LEFT);
4813
234
        end_x = (px + (radius * cos(alpha + gamma)));
4814
234
        end_y = (py + (radius * sin(alpha + gamma)));
4815
234
        mid_x = (opx + (radius * cos(alpha + gamma)));
4816
234
        mid_y = (opy + (radius * sin(alpha + gamma)));
4817
4818
234
        if ((beta < -small) || (beta > (M_PIl + small))) {
4819
            concave = 1;
4820
286
        } else if (beta > (M_PIl - small) &&
4821
12
                   (!qc().empty() && qc().front().type == QARC_FEED &&
4822
4
                    ((side == RIGHT && qc().front().data.arc_feed.turn > 0) ||
4823
4
                     (side == LEFT && qc().front().data.arc_feed.turn < 0)))) {
4824
            // this is an "h" shape, tool on right, going right to left
4825
            // over the hemispherical round part, then up next to the
4826
            // vertical part (or, the mirror case).  there are two ways
4827
            // to stay to the "right", either loop down and around, or
4828
            // stay above and right.  we're forcing above and right.
4829
            concave = 1;
4830
        } else {
4831
139
            concave = 0;
4832
139
            mid_x = (opx + (radius * cos(alpha + gamma)));
4833
139
            mid_y = (opy + (radius * sin(alpha + gamma)));
4834
        }
4835
4836
234
        if (!concave && (beta > small)) {       /* ARC NEEDED */
4837
93
            CHP(move_endpoint_and_flush(settings, cx, cy));
4838
93
            if(move == G_1) {
4839
                enqueue_ARC_FEED(settings, block->line_number,
4840
                                 0.0, // doesn't matter, since we will not move this arc's endpoint
4841
                                 mid_x, mid_y, opx, opy,
4842
                                 ((side == LEFT) ? -1 : 1), cz,
4843
93
                                 AA_end, BB_end, CC_end, u_end, v_end, w_end);
4844
93
                dequeue_canons(settings);
4845
93
                set_endpoint(mid_x, mid_y);
4846
            } else if(move == G_0) {
4847
                // we can't go around the corner because there is no
4848
                // arc traverse.  but, if we do this anyway, at least
4849
                // most of our rapid will be parallel to the original
4850
                // programmed one.  if nothing else, this will look a
4851
                // little less confusing in the preview.
4852
                enqueue_STRAIGHT_TRAVERSE(settings, block->line_number,
4853
                                          0.0, 0.0, 0.0,
4854
                                          mid_x, mid_y, cz,
4855
                                          AA_end, BB_end, CC_end,
4856
                                          u_end, v_end, w_end);
4857
                dequeue_canons(settings);
4858
                set_endpoint(mid_x, mid_y);
4859
            } else ERS(NCE_BUG_CODE_NOT_G0_OR_G1);
4860
141
        } else if (concave) {
4861
190
            if (qc().front().type != QARC_FEED) {
4862
                // line->line
4863
                double retreat;
4864
                // half the angle of the inside corner
4865
70
                double halfcorner = (beta + M_PIl) / 2.0;
4866
70
                CHKS((halfcorner == 0.0), (_("Zero degree inside corner is invalid for cutter compensation")));
4867
70
                retreat = radius / tan(halfcorner);
4868
                // move back along the compensated path
4869
                // this should replace the endpoint of the previous move
4870
70
                mid_x = cx + retreat * cos(theta + gamma);
4871
70
                mid_y = cy + retreat * sin(theta + gamma);
4872
                // we actually want to move the previous line's endpoint here.  That's the same as
4873
                // discarding that line and doing this one instead.
4874
70
                CHP(move_endpoint_and_flush(settings, mid_x, mid_y));
4875
            } else {
4876
                // arc->line
4877
                // beware: the arc we saved is the compensated one.
4878
50
                arc_feed prev = qc().front().data.arc_feed;
4879
25
                double oldrad = hypot(prev.center2 - prev.end2, prev.center1 - prev.end1);
4880
                double oldrad_uncomp;
4881
4882
                // new line's direction
4883
25
                double base_dir = atan2(py - opy, px - opx);
4884
                double theta;
4885
                double phi;
4886
4887
25
                theta = (prev.turn > 0) ? base_dir + M_PI_2l : base_dir - M_PI_2l;
4888
25
                phi = atan2(prev.center2 - opy, prev.center1 - opx);
4889
25
                if TOOL_INSIDE_ARC(side, prev.turn) {
4890
8
                    oldrad_uncomp = oldrad + radius;
4891
                } else {
4892
17
                    oldrad_uncomp = oldrad - radius;
4893
                }
4894
4895
25
                double alpha = theta - phi;
4896
                // distance to old arc center perpendicular to the new line
4897
25
                double d = oldrad_uncomp * cos(alpha);
4898
                double d2;
4899
                double angle_from_center;
4900
4901
25
                if TOOL_INSIDE_ARC(side, prev.turn) {
4902
8
                    d2 = d - radius;
4903
8
                    double l = d2/oldrad;
4904
8
                    CHKS((l > 1.0 || l < -1.0), _("Arc to straight motion makes a corner the compensated tool can't fit in without gouging"));
4905
8
                    if(prev.turn > 0)
4906
2
                        angle_from_center = - acos(l) + theta + M_PIl;
4907
                    else
4908
6
                        angle_from_center = acos(l) + theta + M_PIl;
4909
                } else {
4910
17
                    d2 = d + radius;
4911
17
                    double l = d2/oldrad;
4912
17
                    CHKS((l > 1.0 || l < -1.0), _("Arc to straight motion makes a corner the compensated tool can't fit in without gouging"));
4913
17
                    if(prev.turn > 0)
4914
8
                        angle_from_center = acos(l) + theta + M_PIl;
4915
                    else
4916
9
                        angle_from_center = - acos(l) + theta + M_PIl;
4917
                }
4918
25
                mid_x = prev.center1 + oldrad * cos(angle_from_center);
4919
25
                mid_y = prev.center2 + oldrad * sin(angle_from_center);
4920
25
                CHP(move_endpoint_and_flush(settings, mid_x, mid_y));
4921
            }
4922
        } else {
4923
            // no arc needed, also not concave (colinear lines or tangent arc->line)
4924
46
            dequeue_canons(settings);
4925
46
            set_endpoint(cx, cy);
4926
        }
4927
        (move == G_0? enqueue_STRAIGHT_TRAVERSE: enqueue_STRAIGHT_FEED)
4928
            (settings, block->line_number,
4929
             px - opx, py - opy, pz - opz,
4930
             end_x, end_y, pz,
4931
             AA_end, BB_end, CC_end,
4932
233
             u_end, v_end, w_end);
4933
    }
4934
4935
250
    comp_set_current(settings, end_x, end_y, pz);
4936
250
    settings->AA_current = AA_end;
4937
250
    settings->BB_current = BB_end;
4938
250
    settings->CC_current = CC_end;
4939
250
    settings->u_current = u_end;
4940
250
    settings->v_current = v_end;
4941
250
    settings->w_current = w_end;
4942
250
    comp_set_programmed(settings, px, py, pz);
4943
    return INTERP_OK;
4944
}
4945
4946
/****************************************************************************/
4947
4948
/*! convert_tool_change
4949
4950
Returned Value: int (INTERP_OK)
4951
4952
Side effects:
4953
   This makes function calls to canonical machining functions, and sets
4954
   the machine model as described below.
4955
4956
Called by: convert_m
4957
4958
This function carries out an M6 command, which changes the tool.
4959
If M61 is called, the toolnumber gets changed (without causing an actual toolchange).
4960
4961
When the CHANGE_TOOL call completes, the specified tool should be
4962
loaded.  What this means varies by machine.  According to configuration,
4963
the interpreter may also issue commands to do one or more of the
4964
following things before calling CHANGE_TOOL:
4965
4966
1. stop the spindle
4967
2. move the quill up (Z to machine zero, like G0 G53 Z0)
4968
3. move the axes to reference point #2 (like G30)
4969
4970
Further, the interpreter makes no assumptions about the axis positions
4971
after the tool change completes.  This state is queried and the internal
4972
model is resynched before the program continues.  This means CHANGE_TOOL
4973
itself can also issue motion (and it currently may, according to
4974
configuration).
4975
4976
This implements the "Next tool in T word" approach to tool selection.
4977
The tool is selected when the T word is read (and the carousel may
4978
move at that time) but is changed when M6 is read.
4979
4980
Note that if a different tool is put into the spindle, the current_z
4981
location setting will be incorrect. It is assumed the program will
4982
contain an appropriate USE_TOOL_LENGTH_OFFSET (G43) command before any
4983
subsequent motion.  It is also assumed that the program will restart the
4984
spindle and make new entry moves if necessary.
4985
4986
*/
4987
4988
9
int Interp::convert_tool_change(setup_pointer settings)  //!< pointer to machine settings
4989
{
4990
4991
9
  if (settings->selected_pocket < 0) {
4992
    ERS(NCE_TXX_MISSING_FOR_M6);
4993
  }
4994
4995
9
  CHKS((settings->cutter_comp_side),
4996
       (_("Cannot change tools with cutter radius compensation on")));
4997
4998
9
  START_CHANGE(); // indicate start of change operation
4999
9
  if (!settings->tool_change_with_spindle_on) {
5000
9
      STOP_SPINDLE_TURNING();
5001
9
      settings->spindle_turning = CANON_STOPPED;
5002
  }
5003
5004
9
  if (settings->tool_change_quill_up) {
5005
      double up_z;
5006
      double discard;
5007
      find_relative(0., 0., 0., 0., 0., 0., 0., 0., 0.,
5008
                    &discard, &discard, &up_z,
5009
                    &discard, &discard, &discard,
5010
                    &discard, &discard, &discard,
5011
1
                    settings);
5012
1
      COMMENT("AXIS,hide");
5013
      STRAIGHT_TRAVERSE(-1, settings->current_x, settings->current_y, up_z,
5014
                        settings->AA_current, settings->BB_current, settings->CC_current,
5015
1
                        settings->u_current, settings->v_current, settings->w_current);
5016
1
      COMMENT("AXIS,show");
5017
1
      settings->current_z = up_z;
5018
  }
5019
5020
9
  if (settings->tool_change_at_g30) {
5021
      double end_x;
5022
      double end_y;
5023
      double end_z;
5024
      double AA_end;
5025
      double BB_end;
5026
      double CC_end;
5027
      double u_end;
5028
      double v_end;
5029
      double w_end;
5030
5031
      find_relative(USER_TO_PROGRAM_LEN(settings->parameters[5181]),
5032
                    USER_TO_PROGRAM_LEN(settings->parameters[5182]),
5033
                    USER_TO_PROGRAM_LEN(settings->parameters[5183]),
5034
                    USER_TO_PROGRAM_ANG(settings->parameters[5184]),
5035
                    USER_TO_PROGRAM_ANG(settings->parameters[5185]),
5036
                    USER_TO_PROGRAM_ANG(settings->parameters[5186]),
5037
                    USER_TO_PROGRAM_LEN(settings->parameters[5187]),
5038
                    USER_TO_PROGRAM_LEN(settings->parameters[5188]),
5039
                    USER_TO_PROGRAM_LEN(settings->parameters[5189]),
5040
                    &end_x, &end_y, &end_z,
5041
                    &AA_end, &BB_end, &CC_end,
5042
                    &u_end, &v_end, &w_end, settings);
5043
      COMMENT("AXIS,hide");
5044
5045
      // move indexers first, one at a time
5046
      // JOINTS_AXES settings->*_indexer_jnum == -1 means notused
5047
      if (AA_end != settings->AA_current && (-1 != settings->a_indexer_jnum) )
5048
          issue_straight_index(3,settings->a_indexer_jnum, AA_end, -1, settings);
5049
      if (BB_end != settings->BB_current && (-1 != settings->b_indexer_jnum) )
5050
          issue_straight_index(4,settings->b_indexer_jnum, BB_end, -1, settings);
5051
      if (CC_end != settings->CC_current && (-1 != settings->c_indexer_jnum) )
5052
          issue_straight_index(5,settings->c_indexer_jnum, CC_end, -1, settings);
5053
5054
      STRAIGHT_TRAVERSE(-1, end_x, end_y, end_z,
5055
                        AA_end, BB_end, CC_end,
5056
                        u_end, v_end, w_end);
5057
      COMMENT("AXIS,show");
5058
      settings->current_x = end_x;
5059
      settings->current_y = end_y;
5060
      settings->current_z = end_z;
5061
      settings->AA_current = AA_end;
5062
      settings->BB_current = BB_end;
5063
      settings->CC_current = CC_end;
5064
      settings->u_current = u_end;
5065
      settings->v_current = v_end;
5066
      settings->w_current = w_end;
5067
  }
5068
5069
9
  CHANGE_TOOL(settings->selected_pocket);
5070
5071
9
  settings->current_pocket = settings->selected_pocket;
5072
  // tool change can move the controlled point.  reread it:
5073
9
  settings->toolchange_flag = true;
5074
9
  set_tool_parameters();
5075
9
  return INTERP_OK;
5076
}
5077
5078
/****************************************************************************/
5079
5080
/*! convert_tool_length_offset
5081
5082
Returned Value: int
5083
   If any of the following errors occur, this returns the error code shown.
5084
   Otherwise, it returns INTERP_OK.
5085
   1. The block has no offset index (h number): NCE_OFFSET_INDEX_MISSING
5086
   2. The g_code argument is not G_43 or G_49:
5087
      NCE_BUG_CODE_NOT_G43_OR_G49
5088
5089
Side effects:
5090
   A USE_TOOL_LENGTH_OFFSET function call is made. Current_z,
5091
   tool_length_offset, and length_offset_index are reset.
5092
5093
Called by: convert_g
5094
5095
This is called to execute g43 or g49.
5096
5097
The g49 RS274/NGC command translates into a USE_TOOL_LENGTH_OFFSET(0.0)
5098
function call.
5099
5100
The g43 RS274/NGC command translates into a USE_TOOL_LENGTH_OFFSET(length)
5101
function call, where length is the value of the entry in the tool length
5102
offset table whose index is the H number in the block.
5103
5104
The H number in the block (if present) was checked for being a non-negative
5105
integer when it was read, so that check does not need to be repeated.
5106
5107
*/
5108
5109
17
int Interp::convert_tool_length_offset(int g_code,       //!< g_code being executed (must be G_43 or G_49)
5110
                                      block_pointer block,      //!< pointer to a block of RS274/NGC instructions
5111
                                      setup_pointer settings)   //!< pointer to machine settings
5112
{
5113
  int pocket_number;
5114
  EmcPose tool_offset;
5115
17
  ZERO_EMC_POSE(tool_offset);
5116
5117
17
  CHKS((settings->cutter_comp_side),
5118
       (_("Cannot change tool offset with cutter radius compensation on")));
5119
17
  if (g_code == G_49) {
5120
7
    pocket_number = 0;
5121
10
  } else if (g_code == G_43) {
5122
10
      logDebug("convert_tool_length_offset h_flag=%d h_number=%d toolchange_flag=%d current_pocket=%d\n",
5123
	      block->h_flag,block->h_number,settings->toolchange_flag,settings->current_pocket);
5124
10
      if(block->h_flag) {
5125
        CHP((find_tool_pocket(settings, block->h_number, &pocket_number)));
5126
10
    } else if (settings->toolchange_flag) {
5127
        // Tool change is in progress, so the "current tool" is in its
5128
        // original pocket still.
5129
4
        pocket_number = settings->current_pocket;
5130
    } else {
5131
        // Tool change is done so the current tool is in pocket 0 (aka the
5132
        // spindle).
5133
6
        pocket_number = 0;
5134
    }
5135
10
    logDebug("convert_tool_length_offset: using index=%d spindle_toolno=%d pocket_toolno=%d",
5136
	     pocket_number, settings->tool_table[0].toolno,settings->tool_table[settings->current_pocket].toolno);
5137
5138
10
    tool_offset.tran.x = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.x);
5139
10
    tool_offset.tran.y = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.y);
5140
10
    tool_offset.tran.z = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.z);
5141
10
    tool_offset.a = USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.a);
5142
10
    tool_offset.b = USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.b);
5143
10
    tool_offset.c = USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.c);
5144
10
    tool_offset.u = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.u);
5145
10
    tool_offset.v = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.v);
5146
10
    tool_offset.w = USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.w);
5147
  } else if (g_code == G_43_1) {
5148
    tool_offset = settings->tool_offset;
5149
    pocket_number = -1;
5150
    if(block->x_flag) tool_offset.tran.x = block->x_number;
5151
    if(block->y_flag) tool_offset.tran.y = block->y_number;
5152
    if(block->z_flag) tool_offset.tran.z = block->z_number;
5153
    if(block->a_flag) tool_offset.a = block->a_number;
5154
    if(block->b_flag) tool_offset.b = block->b_number;
5155
    if(block->c_flag) tool_offset.c = block->c_number;
5156
    if(block->u_flag) tool_offset.u = block->u_number;
5157
    if(block->v_flag) tool_offset.v = block->v_number;
5158
    if(block->w_flag) tool_offset.w = block->w_number;
5159
  } else if (g_code == G_43_2) {
5160
    CHKS((!block->h_flag), (_("G43.2: H-word missing")));
5161
    CHP((find_tool_pocket(settings, block->h_number, &pocket_number)));
5162
    tool_offset = settings->tool_offset;
5163
    tool_offset.tran.x += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.x);
5164
    tool_offset.tran.y += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.y);
5165
    tool_offset.tran.z += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.tran.z);
5166
    tool_offset.a += USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.a);
5167
    tool_offset.b += USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.b);
5168
    tool_offset.c += USER_TO_PROGRAM_ANG(settings->tool_table[pocket_number].offset.c);
5169
    tool_offset.u += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.u);
5170
    tool_offset.v += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.v);
5171
    tool_offset.w += USER_TO_PROGRAM_LEN(settings->tool_table[pocket_number].offset.w);
5172
  } else {
5173
    ERS("BUG: Code not G43, G43.1, G43.2, or G49");
5174
  }
5175
17
  USE_TOOL_LENGTH_OFFSET(tool_offset);
5176
5177
  double dx, dy;
5178
5179
17
  dx = settings->tool_offset.tran.x - tool_offset.tran.x;
5180
17
  dy = settings->tool_offset.tran.y - tool_offset.tran.y;
5181
5182
17
  rotate(&dx, &dy, -settings->rotation_xy);
5183
5184
17
  settings->current_x += dx;
5185
17
  settings->current_y += dy;
5186
17
  settings->current_z += settings->tool_offset.tran.z - tool_offset.tran.z;
5187
17
  settings->AA_current += settings->tool_offset.a - tool_offset.a;
5188
17
  settings->BB_current += settings->tool_offset.b - tool_offset.b;
5189
17
  settings->CC_current += settings->tool_offset.c - tool_offset.c;
5190
17
  settings->u_current += settings->tool_offset.u - tool_offset.u;
5191
17
  settings->v_current += settings->tool_offset.v - tool_offset.v;
5192
17
  settings->w_current += settings->tool_offset.w - tool_offset.w;
5193
5194
17
  settings->tool_offset = tool_offset;
5195
17
  return INTERP_OK;
5196
}
5197
5198
/****************************************************************************/
5199
5200
/*! convert_tool_select
5201
5202
Returned Value: int
5203
   If the tool number given in the block is not found in the tool table,
5204
   it returns INTERP_ERROR.  Otherwise (if the tool *is* found) it returns
5205
   INTERP_OK.
5206
5207
Side effects: See below
5208
5209
Called by: execute_block
5210
5211
A select tool command is given, which causes the changer chain to move
5212
so that the slot with the tool identified by the t_number given in the
5213
block is next to the tool changer, ready for a tool change.  The
5214
settings->selected_tool_slot is set to the given slot.
5215
5216
A check that the t_number is not negative has already been made in read_t.
5217
A zero t_number is allowed and means no tool should be selected.
5218
5219
*/
5220
5221
// OK to select tool in a concave corner, I think?
5222
5223
9
int Interp::convert_tool_select(block_pointer block,     //!< pointer to a block of RS274 instructions
5224
                               setup_pointer settings)  //!< pointer to machine settings
5225
{
5226
  int pocket;
5227
9
  CHP((find_tool_pocket(settings, block->t_number, &pocket)));
5228
9
  SELECT_POCKET(pocket, block->t_number);
5229
9
  settings->selected_pocket = pocket;
5230
9
  settings->selected_tool = block->t_number;
5231
9
  return INTERP_OK;
5232
207
}
5233
5234